Compare commits

...

2 Commits

Author SHA1 Message Date
8352ce8b5b New explore screen on wide device 2025-07-21 20:03:06 +08:00
c06abf6e42 Remove the two column in the explore 2025-07-21 19:35:52 +08:00
6 changed files with 125 additions and 79 deletions

View File

@@ -310,42 +310,36 @@ final routerProvider = Provider<GoRouter>((ref) {
}, },
routes: [ routes: [
// Explore tab // Explore tab
ShellRoute( GoRoute(
builder: name: 'explore',
(context, state, child) => ExploreShellScreen(child: child), path: '/',
routes: [ builder: (context, state) => const ExploreScreen(),
GoRoute( ),
name: 'explore', GoRoute(
path: '/', name: 'postSearch',
builder: (context, state) => const ExploreScreen(), path: '/posts/search',
), builder: (context, state) => const PostSearchScreen(),
GoRoute( ),
name: 'postSearch', GoRoute(
path: '/posts/search', name: 'postDetail',
builder: (context, state) => const PostSearchScreen(), path: '/posts/:id',
), builder: (context, state) {
GoRoute( final id = state.pathParameters['id']!;
name: 'postDetail', return PostDetailScreen(id: id);
path: '/posts/:id', },
builder: (context, state) { ),
final id = state.pathParameters['id']!; GoRoute(
return PostDetailScreen(id: id); name: 'publisherProfile',
}, path: '/publishers/:name',
), builder: (context, state) {
GoRoute( final name = state.pathParameters['name']!;
name: 'publisherProfile', return PublisherProfileScreen(name: name);
path: '/publishers/:name', },
builder: (context, state) { ),
final name = state.pathParameters['name']!; GoRoute(
return PublisherProfileScreen(name: name); name: 'discoveryRealms',
}, path: '/discovery/realms',
), builder: (context, state) => const DiscoveryRealmsScreen(),
GoRoute(
name: 'discoveryRealms',
path: '/discovery/realms',
builder: (context, state) => const DiscoveryRealmsScreen(),
),
],
), ),
// Chat tab // Chat tab

View File

@@ -17,6 +17,7 @@ class DiscoveryRealmsScreen extends HookConsumerWidget {
final currentQuery = useState<String?>(null); final currentQuery = useState<String?>(null);
return AppScaffold( return AppScaffold(
noBackground: false,
appBar: AppBar(title: Text('discoverRealms'.tr())), appBar: AppBar(title: Text('discoverRealms'.tr())),
body: Stack( body: Stack(
children: [ children: [

View File

@@ -9,8 +9,11 @@ import 'package:island/models/activity.dart';
import 'package:island/models/publisher.dart'; import 'package:island/models/publisher.dart';
import 'package:island/models/realm.dart'; import 'package:island/models/realm.dart';
import 'package:island/models/webfeed.dart'; import 'package:island/models/webfeed.dart';
import 'package:island/pods/event_calendar.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/services/responsive.dart'; import 'package:island/services/responsive.dart';
import 'package:island/widgets/account/event_calendar.dart';
import 'package:island/widgets/account/fortune_graph.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/widgets/check_in.dart'; import 'package:island/widgets/check_in.dart';
@@ -27,42 +30,11 @@ import 'package:styled_widget/styled_widget.dart';
part 'explore.g.dart'; part 'explore.g.dart';
class ExploreShellScreen extends HookConsumerWidget {
final Widget child;
const ExploreShellScreen({super.key, required this.child});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isWide = MediaQuery.of(context).size.width > 640;
if (isWide) {
return AppBackground(
isRoot: true,
child: Row(
children: [
Flexible(flex: 2, child: ExploreScreen(isAside: true)),
VerticalDivider(width: 1),
Flexible(flex: 3, child: child),
],
),
);
}
return AppBackground(isRoot: true, child: child);
}
}
class ExploreScreen extends HookConsumerWidget { class ExploreScreen extends HookConsumerWidget {
final bool isAside; const ExploreScreen({super.key});
const ExploreScreen({super.key, this.isAside = false});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isWide = isWideScreen(context);
if (isWide && !isAside) {
return const EmptyPageHolder();
}
final tabController = useTabController(initialLength: 3); final tabController = useTabController(initialLength: 3);
final currentFilter = useState<String?>(null); final currentFilter = useState<String?>(null);
@@ -89,8 +61,31 @@ class ExploreScreen extends HookConsumerWidget {
activityListNotifierProvider(currentFilter.value).notifier, activityListNotifierProvider(currentFilter.value).notifier,
); );
final now = DateTime.now();
final query = useState(
EventCalendarQuery(uname: 'me', year: now.year, month: now.month),
);
final events = ref.watch(eventCalendarProvider(query.value));
final selectedDay = useState(now);
void onMonthChanged(int year, int month) {
query.value = EventCalendarQuery(
uname: query.value.uname,
year: year,
month: month,
);
}
// Function to handle day selection for synchronizing between widgets
void onDaySelected(DateTime day) {
selectedDay.value = day;
}
return AppScaffold( return AppScaffold(
extendBody: false, // Prevent conflicts with tabs navigation noBackground: false,
appBar: AppBar( appBar: AppBar(
toolbarHeight: 0, toolbarHeight: 0,
bottom: PreferredSize( bottom: PreferredSize(
@@ -184,23 +179,76 @@ class ExploreScreen extends HookConsumerWidget {
child: const Icon(Symbols.edit), child: const Icon(Symbols.edit),
), ),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: TabBarView( body: Builder(
controller: tabController, builder: (context) {
physics: const NeverScrollableScrollPhysics(), final isWider = isWiderScreen(context);
children: [
_buildActivityList(ref, null), final bodyView = TabBarView(
_buildActivityList(ref, 'subscriptions'), controller: tabController,
_buildActivityList(ref, 'friends'), physics: const NeverScrollableScrollPhysics(),
], children: [
_buildActivityList(context, ref, null),
_buildActivityList(context, ref, 'subscriptions'),
_buildActivityList(context, ref, 'friends'),
],
);
if (isWider) {
return Row(
children: [
Flexible(flex: 3, child: bodyView),
const VerticalDivider(width: 1),
Flexible(
flex: 2,
child: SingleChildScrollView(
child: Column(
children: [
CheckInWidget(),
Card(
margin: EdgeInsets.only(left: 16, right: 16, top: 8),
child: Column(
children: [
// Use the reusable EventCalendarWidget
EventCalendarWidget(
events: events,
initialDate: now,
showEventDetails: true,
onMonthChanged: onMonthChanged,
onDaySelected: onDaySelected,
),
],
),
),
FortuneGraphWidget(
events: events,
constrainWidth: true,
onPointSelected: onDaySelected,
),
],
),
),
),
],
);
}
return bodyView;
},
), ),
); );
} }
Widget _buildActivityList(WidgetRef ref, String? filter) { Widget _buildActivityList(
BuildContext context,
WidgetRef ref,
String? filter,
) {
final activitiesNotifier = ref.watch( final activitiesNotifier = ref.watch(
activityListNotifierProvider(filter).notifier, activityListNotifierProvider(filter).notifier,
); );
final isWider = isWiderScreen(context);
return RefreshIndicator( return RefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh), onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
child: PagingHelperView( child: PagingHelperView(
@@ -214,7 +262,7 @@ class ExploreScreen extends HookConsumerWidget {
widgetCount: widgetCount, widgetCount: widgetCount,
endItemView: endItemView, endItemView: endItemView,
activitiesNotifier: activitiesNotifier, activitiesNotifier: activitiesNotifier,
contentOnly: filter != null, contentOnly: isWider || filter != null,
), ),
), ),
), ),

View File

@@ -57,6 +57,7 @@ class PostDetailScreen extends HookConsumerWidget {
final isWide = isWideScreen(context); final isWide = isWideScreen(context);
return AppScaffold( return AppScaffold(
noBackground: false,
appBar: AppBar(title: const Text('Post')), appBar: AppBar(title: const Text('Post')),
body: postState.when( body: postState.when(
data: (post) { data: (post) {

View File

@@ -109,6 +109,7 @@ class _PostSearchScreenState extends ConsumerState<PostSearchScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: false,
appBar: AppBar( appBar: AppBar(
title: TextField( title: TextField(
controller: _searchController, controller: _searchController,

View File

@@ -124,6 +124,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
return publisher.when( return publisher.when(
data: data:
(data) => AppScaffold( (data) => AppScaffold(
noBackground: false,
body: CustomScrollView( body: CustomScrollView(
slivers: [ slivers: [
SliverAppBar( SliverAppBar(