Pin code

🐛 Bug fixes
This commit is contained in:
LittleSheep 2025-06-22 00:38:51 +08:00
parent f73cf10a54
commit 006841cf82
15 changed files with 401 additions and 120 deletions

View File

@ -65,6 +65,8 @@
"authFactorTOTPDescription": "A one-time code generated by a TOTP authenticator such as Google Authenticator or Authy.", "authFactorTOTPDescription": "A one-time code generated by a TOTP authenticator such as Google Authenticator or Authy.",
"authFactorInAppNotify": "In-app notification", "authFactorInAppNotify": "In-app notification",
"authFactorInAppNotifyDescription": "A one-time code sent via in-app notification.", "authFactorInAppNotifyDescription": "A one-time code sent via in-app notification.",
"authFactorPin": "Pin Code",
"authFactorPinDescription": "It consists of 6 digits. It cannot be used to log in. When performing some dangerous operations, the system will ask you to enter this PIN for confirmation.",
"realms": "Realms", "realms": "Realms",
"createRealm": "Create a Realm", "createRealm": "Create a Realm",
"createRealmHint": "Meet friends with same interests, build communities, and more.", "createRealmHint": "Meet friends with same interests, build communities, and more.",
@ -72,6 +74,8 @@
"deleteRealm": "Delete Realm", "deleteRealm": "Delete Realm",
"deleteRealmHint": "Are you sure to delete this realm? This will also deleted all the channels, publishers, and posts under this realm.", "deleteRealmHint": "Are you sure to delete this realm? This will also deleted all the channels, publishers, and posts under this realm.",
"explore": "Explore", "explore": "Explore",
"exploreFilterSubscriptions": "Subscriptions",
"exploreFilterFriends": "Friends",
"account": "Account", "account": "Account",
"name": "Name", "name": "Name",
"description": "Description", "description": "Description",
@ -460,5 +464,7 @@
"unspecified": "Unspecified", "unspecified": "Unspecified",
"added": "Added", "added": "Added",
"preview": "Preview", "preview": "Preview",
"togglePreview": "Toggle Preview" "togglePreview": "Toggle Preview",
"subscribe": "Subscribe",
"unsubscribe": "Unsubscribe"
} }

View File

@ -86,7 +86,7 @@ sealed class SnPublisherStats with _$SnPublisherStats {
sealed class SnSubscriptionStatus with _$SnSubscriptionStatus { sealed class SnSubscriptionStatus with _$SnSubscriptionStatus {
const factory SnSubscriptionStatus({ const factory SnSubscriptionStatus({
required bool isSubscribed, required bool isSubscribed,
required int publisherId, required String publisherId,
required String publisherName, required String publisherName,
}) = _SnSubscriptionStatus; }) = _SnSubscriptionStatus;

View File

@ -789,7 +789,7 @@ as int,
/// @nodoc /// @nodoc
mixin _$SnSubscriptionStatus { mixin _$SnSubscriptionStatus {
bool get isSubscribed; int get publisherId; String get publisherName; bool get isSubscribed; String get publisherId; String get publisherName;
/// Create a copy of SnSubscriptionStatus /// Create a copy of SnSubscriptionStatus
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -822,7 +822,7 @@ abstract mixin class $SnSubscriptionStatusCopyWith<$Res> {
factory $SnSubscriptionStatusCopyWith(SnSubscriptionStatus value, $Res Function(SnSubscriptionStatus) _then) = _$SnSubscriptionStatusCopyWithImpl; factory $SnSubscriptionStatusCopyWith(SnSubscriptionStatus value, $Res Function(SnSubscriptionStatus) _then) = _$SnSubscriptionStatusCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
bool isSubscribed, int publisherId, String publisherName bool isSubscribed, String publisherId, String publisherName
}); });
@ -843,7 +843,7 @@ class _$SnSubscriptionStatusCopyWithImpl<$Res>
return _then(_self.copyWith( return _then(_self.copyWith(
isSubscribed: null == isSubscribed ? _self.isSubscribed : isSubscribed // ignore: cast_nullable_to_non_nullable isSubscribed: null == isSubscribed ? _self.isSubscribed : isSubscribed // ignore: cast_nullable_to_non_nullable
as bool,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable as bool,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
as int,publisherName: null == publisherName ? _self.publisherName : publisherName // ignore: cast_nullable_to_non_nullable as String,publisherName: null == publisherName ? _self.publisherName : publisherName // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }
@ -859,7 +859,7 @@ class _SnSubscriptionStatus implements SnSubscriptionStatus {
factory _SnSubscriptionStatus.fromJson(Map<String, dynamic> json) => _$SnSubscriptionStatusFromJson(json); factory _SnSubscriptionStatus.fromJson(Map<String, dynamic> json) => _$SnSubscriptionStatusFromJson(json);
@override final bool isSubscribed; @override final bool isSubscribed;
@override final int publisherId; @override final String publisherId;
@override final String publisherName; @override final String publisherName;
/// Create a copy of SnSubscriptionStatus /// Create a copy of SnSubscriptionStatus
@ -895,7 +895,7 @@ abstract mixin class _$SnSubscriptionStatusCopyWith<$Res> implements $SnSubscrip
factory _$SnSubscriptionStatusCopyWith(_SnSubscriptionStatus value, $Res Function(_SnSubscriptionStatus) _then) = __$SnSubscriptionStatusCopyWithImpl; factory _$SnSubscriptionStatusCopyWith(_SnSubscriptionStatus value, $Res Function(_SnSubscriptionStatus) _then) = __$SnSubscriptionStatusCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
bool isSubscribed, int publisherId, String publisherName bool isSubscribed, String publisherId, String publisherName
}); });
@ -916,7 +916,7 @@ class __$SnSubscriptionStatusCopyWithImpl<$Res>
return _then(_SnSubscriptionStatus( return _then(_SnSubscriptionStatus(
isSubscribed: null == isSubscribed ? _self.isSubscribed : isSubscribed // ignore: cast_nullable_to_non_nullable isSubscribed: null == isSubscribed ? _self.isSubscribed : isSubscribed // ignore: cast_nullable_to_non_nullable
as bool,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable as bool,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
as int,publisherName: null == publisherName ? _self.publisherName : publisherName // ignore: cast_nullable_to_non_nullable as String,publisherName: null == publisherName ? _self.publisherName : publisherName // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }

View File

@ -172,7 +172,7 @@ _SnSubscriptionStatus _$SnSubscriptionStatusFromJson(
Map<String, dynamic> json, Map<String, dynamic> json,
) => _SnSubscriptionStatus( ) => _SnSubscriptionStatus(
isSubscribed: json['is_subscribed'] as bool, isSubscribed: json['is_subscribed'] as bool,
publisherId: (json['publisher_id'] as num).toInt(), publisherId: json['publisher_id'] as String,
publisherName: json['publisher_name'] as String, publisherName: json['publisher_name'] as String,
); );

View File

@ -66,10 +66,10 @@ class AccountScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
extendBody: false, // Prevent conflicts with tabs navigation
noBackground: isWide, noBackground: isWide,
appBar: AppBar(title: const Text('account').tr()), appBar: AppBar(backgroundColor: Colors.transparent, toolbarHeight: 0),
body: SingleChildScrollView( body: SingleChildScrollView(
padding: getTabbedPadding(context),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Card( Card(
@ -144,7 +144,7 @@ class AccountScreen extends HookConsumerWidget {
level: user.value!.profile.level, level: user.value!.profile.level,
experience: user.value!.profile.experience, experience: user.value!.profile.experience,
progress: user.value!.profile.levelingProgress, progress: user.value!.profile.levelingProgress,
).padding(horizontal: 8), ).padding(horizontal: 12),
Row( Row(
children: [ children: [
Expanded( Expanded(

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -138,13 +139,13 @@ class AuthFactorSheet extends HookConsumerWidget {
children: [ children: [
if (factor.enabledAt == null) if (factor.enabledAt == null)
Badge( Badge(
label: Text('authFactorDisabled'.tr()), label: Text('authFactorDisabled').tr(),
textColor: Theme.of(context).colorScheme.onSecondary, textColor: Theme.of(context).colorScheme.onSecondary,
backgroundColor: Theme.of(context).colorScheme.secondary, backgroundColor: Theme.of(context).colorScheme.secondary,
) )
else else
Badge( Badge(
label: Text('authFactorEnabled'.tr()), label: Text('authFactorEnabled').tr(),
textColor: Theme.of(context).colorScheme.onPrimary, textColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.primary,
), ),
@ -217,6 +218,8 @@ class AuthFactorNewSheet extends HookConsumerWidget {
} }
} }
final width = math.min(400, MediaQuery.of(context).size.width);
return SheetScaffold( return SheetScaffold(
titleText: 'authFactorNew'.tr(), titleText: 'authFactorNew'.tr(),
child: Column( child: Column(
@ -248,7 +251,7 @@ class AuthFactorNewSheet extends HookConsumerWidget {
} }
}, },
), ),
if (factorType.value == 0) if ([0].contains(factorType.value))
TextField( TextField(
controller: secretController, controller: secretController,
decoration: InputDecoration( decoration: InputDecoration(
@ -259,6 +262,20 @@ class AuthFactorNewSheet extends HookConsumerWidget {
), ),
onTapOutside: onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(), (_) => FocusManager.instance.primaryFocus?.unfocus(),
)
else if ([4].contains(factorType.value))
OtpTextField(
showCursor: false,
numberOfFields: 6,
obscureText: false,
showFieldAsBox: true,
focusedBorderColor: Theme.of(context).colorScheme.primary,
fieldWidth: (width / 6) - 10,
keyboardType: TextInputType.number,
onSubmit: (String verificationCode) {
secretController.text = verificationCode;
},
textStyle: Theme.of(context).textTheme.titleLarge!,
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),

View File

@ -40,6 +40,7 @@ final Map<int, (String, String, IconData)> kFactorTypes = {
Symbols.notifications_active, Symbols.notifications_active,
), ),
3: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer), 3: ('authFactorTOTP', 'authFactorTOTPDescription', Symbols.timer),
4: ('authFactorPin', 'authFactorPinDescription', Symbols.nest_secure_alarm),
}; };
@RoutePage() @RoutePage()

View File

@ -368,10 +368,10 @@ class ChatListScreen extends HookConsumerWidget {
ref.invalidate(chatroomsJoinedProvider); ref.invalidate(chatroomsJoinedProvider);
}), }),
child: ListView.builder( child: ListView.builder(
padding: padding: getTabbedPadding(
callState.isConnected context,
? EdgeInsets.only(bottom: 96) bottom: callState.isConnected ? 96 : null,
: EdgeInsets.zero, ),
itemCount: itemCount:
items items
.where( .where(

View File

@ -1,6 +1,7 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.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:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart'; import 'package:island/models/activity.dart';
@ -46,10 +47,8 @@ class ExploreShellScreen extends ConsumerWidget {
} }
} }
@RoutePage() @RoutePage()
class ExploreScreen extends ConsumerWidget { class ExploreScreen extends HookConsumerWidget {
final bool isAside; final bool isAside;
const ExploreScreen({super.key, this.isAside = false}); const ExploreScreen({super.key, this.isAside = false});
@ -60,12 +59,67 @@ class ExploreScreen extends ConsumerWidget {
return const EmptyPageHolder(); return const EmptyPageHolder();
} }
final activitiesNotifier = ref.watch(activityListNotifierProvider.notifier); final tabController = useTabController(initialLength: 3);
final currentFilter = useState<String?>(null);
useEffect(() {
void listener() {
switch (tabController.index) {
case 0:
currentFilter.value = null;
break;
case 1:
currentFilter.value = 'subscriptions';
break;
case 2:
currentFilter.value = 'friends';
break;
}
}
tabController.addListener(listener);
return () => tabController.removeListener(listener);
}, [tabController]);
final activitiesNotifier = ref.watch(
activityListNotifierProvider(currentFilter.value).notifier,
);
return TourTriggerWidget( return TourTriggerWidget(
child: AppScaffold( child: AppScaffold(
extendBody: false, // Prevent conflicts with tabs navigation extendBody: false, // Prevent conflicts with tabs navigation
appBar: AppBar(title: const Text('explore').tr()), appBar: AppBar(
toolbarHeight: 0,
bottom: TabBar(
controller: tabController,
tabs: [
Tab(
child: Text(
'explore'.tr(),
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'exploreFilterSubscriptions'.tr(),
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
Tab(
child: Text(
'exploreFilterFriends'.tr(),
style: TextStyle(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
],
),
),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
heroTag: Key("explore-page-fab"), heroTag: Key("explore-page-fab"),
onPressed: () { onPressed: () {
@ -78,12 +132,29 @@ class ExploreScreen extends ConsumerWidget {
child: const Icon(Symbols.edit), child: const Icon(Symbols.edit),
), ),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: RefreshIndicator( body: TabBarView(
controller: tabController,
children: [
_buildActivityList(ref, null),
_buildActivityList(ref, 'subscriptions'),
_buildActivityList(ref, 'friends'),
],
),
),
);
}
Widget _buildActivityList(WidgetRef ref, String? filter) {
final activitiesNotifier = ref.watch(
activityListNotifierProvider(filter).notifier,
);
return RefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh), onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
child: PagingHelperView( child: PagingHelperView(
provider: activityListNotifierProvider, provider: activityListNotifierProvider(filter),
futureRefreshable: activityListNotifierProvider.future, futureRefreshable: activityListNotifierProvider(filter).future,
notifierRefreshable: activityListNotifierProvider.notifier, notifierRefreshable: activityListNotifierProvider(filter).notifier,
contentBuilder: contentBuilder:
(data, widgetCount, endItemView) => Center( (data, widgetCount, endItemView) => Center(
child: _ActivityListView( child: _ActivityListView(
@ -91,8 +162,7 @@ class ExploreScreen extends ConsumerWidget {
widgetCount: widgetCount, widgetCount: widgetCount,
endItemView: endItemView, endItemView: endItemView,
activitiesNotifier: activitiesNotifier, activitiesNotifier: activitiesNotifier,
), contentOnly: filter != null,
),
), ),
), ),
), ),
@ -104,6 +174,7 @@ class _ActivityListView extends HookConsumerWidget {
final CursorPagingData<SnActivity> data; final CursorPagingData<SnActivity> data;
final int widgetCount; final int widgetCount;
final Widget endItemView; final Widget endItemView;
final bool contentOnly;
final ActivityListNotifier activitiesNotifier; final ActivityListNotifier activitiesNotifier;
const _ActivityListView({ const _ActivityListView({
@ -111,6 +182,7 @@ class _ActivityListView extends HookConsumerWidget {
required this.widgetCount, required this.widgetCount,
required this.endItemView, required this.endItemView,
required this.activitiesNotifier, required this.activitiesNotifier,
this.contentOnly = false,
}); });
@override @override
@ -119,7 +191,8 @@ class _ActivityListView extends HookConsumerWidget {
return CustomScrollView( return CustomScrollView(
slivers: [ slivers: [
if (user.hasValue) SliverToBoxAdapter(child: CheckInWidget()), if (user.hasValue && !contentOnly)
SliverToBoxAdapter(child: CheckInWidget()),
SliverList.builder( SliverList.builder(
itemCount: widgetCount, itemCount: widgetCount,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@ -178,7 +251,7 @@ class _ActivityListView extends HookConsumerWidget {
return Column(children: [itemWidget, const Divider(height: 1)]); return Column(children: [itemWidget, const Divider(height: 1)]);
}, },
), ),
SliverGap(MediaQuery.of(context).padding.bottom + 16), SliverGap(getTabbedPadding(context).bottom),
], ],
); );
} }
@ -188,16 +261,23 @@ class _ActivityListView extends HookConsumerWidget {
class ActivityListNotifier extends _$ActivityListNotifier class ActivityListNotifier extends _$ActivityListNotifier
with CursorPagingNotifierMixin<SnActivity> { with CursorPagingNotifierMixin<SnActivity> {
@override @override
Future<CursorPagingData<SnActivity>> build() => fetch(cursor: null); Future<CursorPagingData<SnActivity>> build(String? filter) =>
fetch(cursor: null);
@override @override
Future<CursorPagingData<SnActivity>> fetch({required String? cursor}) async { Future<CursorPagingData<SnActivity>> fetch({required String? cursor}) async {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
final take = 20; final take = 20;
final queryParameters = {
if (cursor != null) 'cursor': cursor,
'take': take,
if (filter != null) 'filter': filter,
};
final response = await client.get( final response = await client.get(
'/activities', '/activities',
queryParameters: {if (cursor != null) 'cursor': cursor, 'take': take}, queryParameters: queryParameters,
); );
final List<SnActivity> items = final List<SnActivity> items =

View File

@ -7,25 +7,174 @@ part of 'explore.dart';
// ************************************************************************** // **************************************************************************
String _$activityListNotifierHash() => String _$activityListNotifierHash() =>
r'c9683035f7a66a2f331689e274642b60064fbb2e'; r'14ec2f211c86e1e64a9a34b142d0e8f78ff6361a';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
abstract class _$ActivityListNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnActivity>> {
late final String? filter;
FutureOr<CursorPagingData<SnActivity>> build(String? filter);
}
/// See also [ActivityListNotifier]. /// See also [ActivityListNotifier].
@ProviderFor(ActivityListNotifier) @ProviderFor(ActivityListNotifier)
final activityListNotifierProvider = AutoDisposeAsyncNotifierProvider< const activityListNotifierProvider = ActivityListNotifierFamily();
/// See also [ActivityListNotifier].
class ActivityListNotifierFamily
extends Family<AsyncValue<CursorPagingData<SnActivity>>> {
/// See also [ActivityListNotifier].
const ActivityListNotifierFamily();
/// See also [ActivityListNotifier].
ActivityListNotifierProvider call(String? filter) {
return ActivityListNotifierProvider(filter);
}
@override
ActivityListNotifierProvider getProviderOverride(
covariant ActivityListNotifierProvider provider,
) {
return call(provider.filter);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'activityListNotifierProvider';
}
/// See also [ActivityListNotifier].
class ActivityListNotifierProvider
extends
AutoDisposeAsyncNotifierProviderImpl<
ActivityListNotifier, ActivityListNotifier,
CursorPagingData<SnActivity> CursorPagingData<SnActivity>
>.internal( > {
ActivityListNotifier.new, /// See also [ActivityListNotifier].
ActivityListNotifierProvider(String? filter)
: this._internal(
() => ActivityListNotifier()..filter = filter,
from: activityListNotifierProvider,
name: r'activityListNotifierProvider', name: r'activityListNotifierProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$activityListNotifierHash, : _$activityListNotifierHash,
dependencies: ActivityListNotifierFamily._dependencies,
allTransitiveDependencies:
ActivityListNotifierFamily._allTransitiveDependencies,
filter: filter,
);
ActivityListNotifierProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.filter,
}) : super.internal();
final String? filter;
@override
FutureOr<CursorPagingData<SnActivity>> runNotifierBuild(
covariant ActivityListNotifier notifier,
) {
return notifier.build(filter);
}
@override
Override overrideWith(ActivityListNotifier Function() create) {
return ProviderOverride(
origin: this,
override: ActivityListNotifierProvider._internal(
() => create()..filter = filter,
from: from,
name: null,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); debugGetCreateSourceHash: null,
filter: filter,
),
);
}
@override
AutoDisposeAsyncNotifierProviderElement<
ActivityListNotifier,
CursorPagingData<SnActivity>
>
createElement() {
return _ActivityListNotifierProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is ActivityListNotifierProvider && other.filter == filter;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, filter.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin ActivityListNotifierRef
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnActivity>> {
/// The parameter `filter` of this provider.
String? get filter;
}
class _ActivityListNotifierProviderElement
extends
AutoDisposeAsyncNotifierProviderElement<
ActivityListNotifier,
CursorPagingData<SnActivity>
>
with ActivityListNotifierRef {
_ActivityListNotifierProviderElement(super.provider);
@override
String? get filter => (origin as ActivityListNotifierProvider).filter;
}
typedef _$ActivityListNotifier =
AutoDisposeAsyncNotifier<CursorPagingData<SnActivity>>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@ -161,48 +161,34 @@ class PublisherProfileScreen extends HookConsumerWidget {
), ),
], ],
), ),
actions: [
subStatus.when(
data:
(status) => IconButton(
onPressed:
subscribing.value
? null
: (status.isSubscribed
? unsubscribe
: subscribe),
icon: Icon(
status.isSubscribed
? Icons.remove_circle
: Icons.add_circle,
shadows: [appbarShadow],
),
),
error: (_, _) => const SizedBox(),
loading:
() => const SizedBox(
width: 48,
height: 48,
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
),
),
const Gap(8),
],
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 20, spacing: 20,
children: [ children: [
ProfilePictureWidget(file: data.picture, radius: 32), GestureDetector(
child: Badge(
isLabelVisible: data.type == 0,
padding: EdgeInsets.all(4),
label: Icon(
Symbols.launch,
size: 16,
color: Theme.of(context).colorScheme.onPrimary,
),
backgroundColor:
Theme.of(context).colorScheme.primary,
offset: Offset(0, 48),
child: ProfilePictureWidget(
file: data.picture,
radius: 32,
),
),
onTap: () {
Navigator.pop(context, true);
context.router.pushPath('/account/${data.name}');
},
),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@ -242,19 +228,49 @@ class PublisherProfileScreen extends HookConsumerWidget {
uname: data.account!.name, uname: data.account!.name,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
), ),
OutlinedButton.icon( subStatus
onPressed: () { .when(
Navigator.pop(context); data:
context.router.pushPath( (status) => FilledButton.icon(
'/account/${data.name}', onPressed:
); subscribing.value
}, ? null
icon: const Icon(Symbols.launch), : (status.isSubscribed
label: Text('accountProfileView').tr(), ? unsubscribe
style: ButtonStyle( : subscribe),
visualDensity: VisualDensity(vertical: -2), icon: Icon(
status.isSubscribed
? Symbols.remove_circle
: Symbols.add_circle,
), ),
).padding(top: 8), label:
Text(
status.isSubscribed
? 'unsubscribe'
: 'subscribe',
).tr(),
style: ButtonStyle(
visualDensity: VisualDensity(
vertical: -2,
),
),
),
error: (_, _) => const SizedBox(),
loading:
() => const SizedBox(
height: 36,
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
),
)
.padding(top: 8),
], ],
), ),
), ),

View File

@ -13,6 +13,7 @@ import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/route.gr.dart'; import 'package:island/route.gr.dart';
import 'package:island/services/file.dart'; import 'package:island/services/file.dart';
import 'package:island/services/responsive.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';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
@ -93,9 +94,7 @@ class RealmListScreen extends HookConsumerWidget {
children: [ children: [
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
padding: EdgeInsets.only( padding: getTabbedPadding(context),
bottom: MediaQuery.of(context).padding.bottom,
),
itemCount: value.length, itemCount: value.length,
itemBuilder: (context, item) { itemBuilder: (context, item) {
return ListTile( return ListTile(

View File

@ -2,7 +2,6 @@ import 'dart:ui';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.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:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/route.gr.dart'; import 'package:island/route.gr.dart';
import 'package:island/screens/notification.dart'; import 'package:island/screens/notification.dart';
@ -55,12 +54,6 @@ class TabsScreen extends HookConsumerWidget {
builder: (context, child, _) { builder: (context, child, _) {
final tabsRouter = AutoTabsRouter.of(context); final tabsRouter = AutoTabsRouter.of(context);
// Check if current route is a tab route
final currentRoute = context.router.topRoute;
final isTabRoute = routes.any(
(route) => route.routeName == currentRoute.name,
);
return Stack( return Stack(
children: [ children: [
Positioned.fill(child: child), Positioned.fill(child: child),

View File

@ -15,3 +15,24 @@ bool isWiderScreen(BuildContext context) {
bool isWidestScreen(BuildContext context) { bool isWidestScreen(BuildContext context) {
return MediaQuery.of(context).size.width > kWidescreenWidth; return MediaQuery.of(context).size.width > kWidescreenWidth;
} }
EdgeInsets getTabbedPadding(
BuildContext context, {
double? horizontal,
double? vertical,
double? left,
double? right,
double? top,
double? bottom,
}) {
final bottomPadding = bottom ?? MediaQuery.of(context).padding.bottom + 16;
return EdgeInsets.only(
left: left ?? horizontal ?? 0,
right: right ?? horizontal ?? 0,
top: top ?? vertical ?? 0,
bottom:
bottom != null
? bottomPadding
: MediaQuery.of(context).padding.bottom + 16,
);
}

View File

@ -23,7 +23,6 @@ class LevelingProgressCard extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Text('levelingProgress').tr().fontSize(16).bold(),
Row( Row(
spacing: 8, spacing: 8,
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,