Compare commits

...

3 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
37cc0a5291 📝 Update README.md to refernece Suki 2025-07-20 18:40:20 +08:00
7 changed files with 128 additions and 81 deletions

View File

@@ -8,6 +8,9 @@
Hello there! Welcome to the main repository of the DysonNetwork (also known as the Solar Network). The code here is mainly about the front-end app (also known as Solian). But you can still post issues here to get help and request new features! Hello there! Welcome to the main repository of the DysonNetwork (also known as the Solar Network). The code here is mainly about the front-end app (also known as Solian). But you can still post issues here to get help and request new features!
如果你看得懂这行字,你可以前往我们的文档来了解更多:
[Suki - Solar Network](https://kb.solsynth.dev/zh/solar-network)
## Server ## Server
The backend of the Solar Network project is located at [Solsynth/DysonNetwork](https://github.com/Solsynth/DysonNetwork) The backend of the Solar Network project is located at [Solsynth/DysonNetwork](https://github.com/Solsynth/DysonNetwork)
@@ -25,8 +28,6 @@ The content below will lead you to the world of Solar Network.
### For Normal Users ### For Normal Users
**The v3 Release is not ready, yet.**
1. Go to the Github Releases page, and download the latest release / pre-release according to your platform. 1. Go to the Github Releases page, and download the latest release / pre-release according to your platform.
- **What's the difference between stable and pre-release?** The pre-release is untested by the other users and includes the new cutting-edge features, usually the pre-release is the feature drop. At the same time, due to we're not doing the API versioning, some breaking changes may break the stable release, so use the pre-release one instead. - **What's the difference between stable and pre-release?** The pre-release is untested by the other users and includes the new cutting-edge features, usually the pre-release is the feature drop. At the same time, due to we're not doing the API versioning, some breaking changes may break the stable release, so use the pre-release one instead.
2. Create an account on the Solar Network 2. Create an account on the Solar Network

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(