💄 Merge the creator hub and developer hub to the tabs
This commit is contained in:
362
lib/route.dart
362
lib/route.dart
@@ -146,185 +146,6 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
return EventCalanderScreen(name: name);
|
return EventCalanderScreen(name: name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
name: 'creatorHub',
|
|
||||||
path: '/creators',
|
|
||||||
builder: (context, state) => const CreatorHubScreen(),
|
|
||||||
routes: [
|
|
||||||
// Web Feed Routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorFeeds',
|
|
||||||
path: ':name/feeds',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return WebFeedListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPosts',
|
|
||||||
path: ':name/posts',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return CreatorPostListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Poll list route
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPolls',
|
|
||||||
path: ':name/polls',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return CreatorPollListScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// Poll routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPollNew',
|
|
||||||
path: ':name/polls/new',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
// initialPollId left null for create; initialPublisher prefilled
|
|
||||||
return PollEditorScreen(initialPublisher: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorPollEdit',
|
|
||||||
path: ':name/polls/:id/edit',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
final id = state.pathParameters['id']!;
|
|
||||||
return PollEditorScreen(
|
|
||||||
initialPollId: id,
|
|
||||||
initialPublisher: name,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorStickers',
|
|
||||||
path: ':name/stickers',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return StickersScreen(pubName: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorNew',
|
|
||||||
path: 'new',
|
|
||||||
builder: (context, state) => const NewPublisherScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'creatorEdit',
|
|
||||||
path: ':name/edit',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
return EditPublisherScreen(name: name);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerHub',
|
|
||||||
path: '/developers',
|
|
||||||
builder:
|
|
||||||
(context, state) => DeveloperHubScreen(
|
|
||||||
initialPublisherName: state.uri.queryParameters['publisher'],
|
|
||||||
initialProjectId: state.uri.queryParameters['project'],
|
|
||||||
),
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectNew',
|
|
||||||
path: ':name/projects/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewProjectScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectEdit',
|
|
||||||
path: ':name/projects/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditProjectScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerProjectDetail',
|
|
||||||
path: ':name/projects/:projectId',
|
|
||||||
builder: (context, state) {
|
|
||||||
final name = state.pathParameters['name']!;
|
|
||||||
final projectId = state.pathParameters['projectId']!;
|
|
||||||
// Redirect to hub with project selected
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
context.go(
|
|
||||||
'/developers?publisher=$name&project=$projectId',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return const SizedBox.shrink(); // Temporary placeholder
|
|
||||||
},
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppNew',
|
|
||||||
path: 'apps/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewCustomAppScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppEdit',
|
|
||||||
path: 'apps/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditAppScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerAppDetail',
|
|
||||||
path: 'apps/:appId',
|
|
||||||
builder:
|
|
||||||
(context, state) => AppDetailScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
appId: state.pathParameters['appId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotNew',
|
|
||||||
path: 'bots/new',
|
|
||||||
builder:
|
|
||||||
(context, state) => NewBotScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotDetail',
|
|
||||||
path: 'bots/:botId',
|
|
||||||
builder:
|
|
||||||
(context, state) => BotDetailScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
botId: state.pathParameters['botId']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
name: 'developerBotEdit',
|
|
||||||
path: 'bots/:id/edit',
|
|
||||||
builder:
|
|
||||||
(context, state) => EditBotScreen(
|
|
||||||
publisherName: state.pathParameters['name']!,
|
|
||||||
projectId: state.pathParameters['projectId']!,
|
|
||||||
id: state.pathParameters['id']!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Web articles
|
// Web articles
|
||||||
GoRoute(
|
GoRoute(
|
||||||
@@ -639,6 +460,189 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
return AccountProfileScreen(name: name);
|
return AccountProfileScreen(name: name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
// Creator hub tab
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorHub',
|
||||||
|
path: '/creators',
|
||||||
|
builder: (context, state) => const CreatorHubScreen(),
|
||||||
|
routes: [
|
||||||
|
// Web Feed Routes
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorFeeds',
|
||||||
|
path: ':name/feeds',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return WebFeedListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPosts',
|
||||||
|
path: ':name/posts',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return CreatorPostListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Poll list route
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPolls',
|
||||||
|
path: ':name/polls',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return CreatorPollListScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Poll routes
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPollNew',
|
||||||
|
path: ':name/polls/new',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
// initialPollId left null for create; initialPublisher prefilled
|
||||||
|
return PollEditorScreen(initialPublisher: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorPollEdit',
|
||||||
|
path: ':name/polls/:id/edit',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
final id = state.pathParameters['id']!;
|
||||||
|
return PollEditorScreen(
|
||||||
|
initialPollId: id,
|
||||||
|
initialPublisher: name,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorStickers',
|
||||||
|
path: ':name/stickers',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return StickersScreen(pubName: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorNew',
|
||||||
|
path: 'new',
|
||||||
|
builder: (context, state) => const NewPublisherScreen(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'creatorEdit',
|
||||||
|
path: ':name/edit',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
return EditPublisherScreen(name: name);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// Developer hub tab
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerHub',
|
||||||
|
path: '/developers',
|
||||||
|
builder:
|
||||||
|
(context, state) => DeveloperHubScreen(
|
||||||
|
initialPublisherName:
|
||||||
|
state.uri.queryParameters['publisher'],
|
||||||
|
initialProjectId: state.uri.queryParameters['project'],
|
||||||
|
),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectNew',
|
||||||
|
path: ':name/projects/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewProjectScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectEdit',
|
||||||
|
path: ':name/projects/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditProjectScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerProjectDetail',
|
||||||
|
path: ':name/projects/:projectId',
|
||||||
|
builder: (context, state) {
|
||||||
|
final name = state.pathParameters['name']!;
|
||||||
|
final projectId = state.pathParameters['projectId']!;
|
||||||
|
// Redirect to hub with project selected
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
context.go(
|
||||||
|
'/developers?publisher=$name&project=$projectId',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return const SizedBox.shrink(); // Temporary placeholder
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppNew',
|
||||||
|
path: 'apps/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewCustomAppScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppEdit',
|
||||||
|
path: 'apps/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditAppScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerAppDetail',
|
||||||
|
path: 'apps/:appId',
|
||||||
|
builder:
|
||||||
|
(context, state) => AppDetailScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
appId: state.pathParameters['appId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotNew',
|
||||||
|
path: 'bots/new',
|
||||||
|
builder:
|
||||||
|
(context, state) => NewBotScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotDetail',
|
||||||
|
path: 'bots/:botId',
|
||||||
|
builder:
|
||||||
|
(context, state) => BotDetailScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
botId: state.pathParameters['botId']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'developerBotEdit',
|
||||||
|
path: 'bots/:id/edit',
|
||||||
|
builder:
|
||||||
|
(context, state) => EditBotScreen(
|
||||||
|
publisherName: state.pathParameters['name']!,
|
||||||
|
projectId: state.pathParameters['projectId']!,
|
||||||
|
id: state.pathParameters['id']!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@@ -149,7 +149,8 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('leveling');
|
context.pushNamed('leveling');
|
||||||
},
|
},
|
||||||
).padding(horizontal: 12),
|
).padding(horizontal: 12),
|
||||||
const SizedBox.shrink(),
|
if (!isWideScreen(context)) const SizedBox.shrink(),
|
||||||
|
if (!isWideScreen(context))
|
||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
@@ -210,71 +211,62 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
).padding(horizontal: 12),
|
).padding(horizontal: 12),
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
Row(
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Card(
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(Symbols.settings, size: 28).padding(bottom: 8),
|
Icon(Symbols.settings, size: 20),
|
||||||
Text('appSettings').tr().fontSize(16).bold(),
|
Text('appSettings').tr().fontSize(13).bold(),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('settings');
|
context.pushNamed('settings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Card(
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Symbols.person_edit, size: 20),
|
||||||
Symbols.person_edit,
|
Text('updateYourProfile').tr().fontSize(13).bold(),
|
||||||
size: 28,
|
|
||||||
).padding(bottom: 8),
|
|
||||||
Text('updateYourProfile').tr().fontSize(16).bold(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('profileUpdate');
|
context.pushNamed('profileUpdate');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Card(
|
||||||
child: Card(
|
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Symbols.manage_accounts, size: 20),
|
||||||
Symbols.manage_accounts,
|
Text('accountSettings').tr().fontSize(13).bold(),
|
||||||
size: 28,
|
|
||||||
).padding(bottom: 8),
|
|
||||||
Text('accountSettings').tr().fontSize(16).bold(),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushNamed('accountSettings');
|
context.pushNamed('accountSettings');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).height(120),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 12),
|
).padding(horizontal: 12),
|
||||||
|
).height(48),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: const Icon(Symbols.notifications),
|
leading: const Icon(Symbols.notifications),
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -11,6 +12,16 @@ import 'package:material_symbols_icons/symbols.dart';
|
|||||||
|
|
||||||
final currentRouteProvider = StateProvider<String?>((ref) => null);
|
final currentRouteProvider = StateProvider<String?>((ref) => null);
|
||||||
|
|
||||||
|
const kWideScreenRouteStart = 4;
|
||||||
|
const kTabRoutes = [
|
||||||
|
'/',
|
||||||
|
'/chat',
|
||||||
|
'/realms',
|
||||||
|
'/account',
|
||||||
|
'/creators',
|
||||||
|
'/developers',
|
||||||
|
];
|
||||||
|
|
||||||
class TabsScreen extends HookConsumerWidget {
|
class TabsScreen extends HookConsumerWidget {
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
const TabsScreen({super.key, this.child});
|
const TabsScreen({super.key, this.child});
|
||||||
@@ -32,6 +43,8 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
notificationUnreadCountNotifierProvider,
|
notificationUnreadCountNotifierProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final wideScreen = isWideScreen(context);
|
||||||
|
|
||||||
final destinations = [
|
final destinations = [
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
label: 'explore'.tr(),
|
label: 'explore'.tr(),
|
||||||
@@ -50,19 +63,30 @@ class TabsScreen extends HookConsumerWidget {
|
|||||||
child: const Icon(Symbols.account_circle),
|
child: const Icon(Symbols.account_circle),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (wideScreen)
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'creatorHub'.tr(),
|
||||||
|
icon: const Icon(Symbols.draw),
|
||||||
|
),
|
||||||
|
if (wideScreen)
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'developerHub'.tr(),
|
||||||
|
icon: const Icon(Symbols.code),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
final routes = ['/', '/chat', '/realms', '/account'];
|
|
||||||
|
|
||||||
int getCurrentIndex() {
|
int getCurrentIndex() {
|
||||||
if (currentLocation.startsWith('/chat')) return 1;
|
if (currentLocation == '/') return 0;
|
||||||
if (currentLocation.startsWith('/realms')) return 2;
|
final idx = kTabRoutes.indexWhere(
|
||||||
if (currentLocation.startsWith('/account')) return 3;
|
(p) => currentLocation.startsWith(p),
|
||||||
return 0; // Default to explore
|
1,
|
||||||
|
);
|
||||||
|
final value = math.max(idx, 0);
|
||||||
|
return math.min(value, destinations.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDestinationSelected(int index) {
|
void onDestinationSelected(int index) {
|
||||||
context.go(routes[index]);
|
context.go(kTabRoutes[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentIndex = getCurrentIndex();
|
final currentIndex = getCurrentIndex();
|
||||||
|
@@ -5,9 +5,10 @@ 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/heatmap.dart';
|
import 'package:island/models/heatmap.dart';
|
||||||
|
import '../services/responsive.dart';
|
||||||
|
|
||||||
/// A reusable heatmap widget for displaying activity data in GitHub-style layout.
|
/// A reusable heatmap widget for displaying activity data in GitHub-style layout.
|
||||||
/// Shows exactly 365 days of data ending at the current date.
|
/// Shows exactly 365 days (wide screen) or 90 days (non-wide screen) of data ending at the current date.
|
||||||
class ActivityHeatmapWidget extends HookConsumerWidget {
|
class ActivityHeatmapWidget extends HookConsumerWidget {
|
||||||
final SnHeatmap heatmap;
|
final SnHeatmap heatmap;
|
||||||
|
|
||||||
@@ -17,11 +18,13 @@ class ActivityHeatmapWidget extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final selectedItem = useState<HeatmapItem?>(null);
|
final selectedItem = useState<HeatmapItem?>(null);
|
||||||
|
|
||||||
// Generate exactly 365 days ending at current date
|
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
// Start from exactly 365 days ago
|
final isWide = isWideScreen(context);
|
||||||
final startDate = now.subtract(const Duration(days: 365));
|
final days = isWide ? 365 : 90;
|
||||||
|
|
||||||
|
// Start from exactly the selected days ago
|
||||||
|
final startDate = now.subtract(Duration(days: days));
|
||||||
// End at current date
|
// End at current date
|
||||||
final endDate = now;
|
final endDate = now;
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ class ActivityHeatmapWidget extends HookConsumerWidget {
|
|||||||
// Find sunday of the week containing end date
|
// Find sunday of the week containing end date
|
||||||
final endSunday = endDate.add(Duration(days: 7 - endDate.weekday));
|
final endSunday = endDate.add(Duration(days: 7 - endDate.weekday));
|
||||||
|
|
||||||
// Generate weeks to cover exactly 365 days
|
// Generate weeks to cover the selected date range
|
||||||
final weeks = <DateTime>[];
|
final weeks = <DateTime>[];
|
||||||
var current = startMonday;
|
var current = startMonday;
|
||||||
while (current.isBefore(endSunday) || current.isAtSameMomentAs(endSunday)) {
|
while (current.isBefore(endSunday) || current.isAtSameMomentAs(endSunday)) {
|
||||||
@@ -45,7 +48,7 @@ class ActivityHeatmapWidget extends HookConsumerWidget {
|
|||||||
for (final week in weeks) {
|
for (final week in weeks) {
|
||||||
for (var i = 0; i < 7; i++) {
|
for (var i = 0; i < 7; i++) {
|
||||||
final date = week.add(Duration(days: i));
|
final date = week.add(Duration(days: i));
|
||||||
// Only include dates within our 365-day range
|
// Only include dates within our selected range
|
||||||
if (date.isAfter(startDate.subtract(const Duration(days: 1))) &&
|
if (date.isAfter(startDate.subtract(const Duration(days: 1))) &&
|
||||||
date.isBefore(endDate.add(const Duration(days: 1)))) {
|
date.isBefore(endDate.add(const Duration(days: 1)))) {
|
||||||
final item = heatmap.items.firstWhere(
|
final item = heatmap.items.firstWhere(
|
||||||
|
@@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.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';
|
||||||
@@ -68,26 +69,28 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
final pageButtonActions = [
|
final router = ref.watch(routerProvider);
|
||||||
|
|
||||||
|
final pageActionsButton = [
|
||||||
|
if (router.canPop())
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Symbols.keyboard_arrow_left),
|
icon: Icon(Symbols.close),
|
||||||
onPressed:
|
onPressed: router.canPop() ? () => router.pop() : null,
|
||||||
ref.watch(routerProvider).canPop()
|
|
||||||
? () => ref.read(routerProvider).pop()
|
|
||||||
: null,
|
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
constraints: BoxConstraints(),
|
constraints: BoxConstraints(),
|
||||||
color: Theme.of(context).iconTheme.color,
|
color: Theme.of(context).iconTheme.color,
|
||||||
),
|
)
|
||||||
|
else
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Symbols.home),
|
icon: Icon(Symbols.home),
|
||||||
onPressed: () => ref.read(routerProvider).go('/'),
|
onPressed: () => router.go('/'),
|
||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
constraints: BoxConstraints(),
|
constraints: BoxConstraints(),
|
||||||
color: Theme.of(context).iconTheme.color,
|
color: Theme.of(context).iconTheme.color,
|
||||||
),
|
),
|
||||||
|
const Gap(8),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
@@ -111,13 +114,18 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
? Stack(
|
? Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
if (isWideScreen(context))
|
||||||
Row(
|
Row(
|
||||||
children: [
|
key: Key(
|
||||||
if (Platform.isMacOS)
|
'app-page-action-${router.state.pageKey.value}',
|
||||||
const SizedBox(width: 80),
|
|
||||||
...pageButtonActions,
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
...pageActionsButton,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
SizedBox(height: 32),
|
||||||
Text(
|
Text(
|
||||||
'Solar Network',
|
'Solar Network',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@@ -374,7 +382,7 @@ class PageBackButton extends StatelessWidget {
|
|||||||
final isDesktop =
|
final isDesktop =
|
||||||
!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows);
|
!kIsWeb && (Platform.isMacOS || Platform.isLinux || Platform.isWindows);
|
||||||
|
|
||||||
if (isDesktop) return const SizedBox.shrink();
|
if (isDesktop && isWideScreen(context)) return const SizedBox.shrink();
|
||||||
|
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -387,9 +395,11 @@ class PageBackButton extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
color: color,
|
color: color,
|
||||||
(!kIsWeb && (Platform.isMacOS || Platform.isIOS))
|
context.canPop()
|
||||||
|
? (!kIsWeb && (Platform.isMacOS || Platform.isIOS))
|
||||||
? Symbols.arrow_back_ios_new
|
? Symbols.arrow_back_ios_new
|
||||||
: Symbols.arrow_back,
|
: Symbols.arrow_back
|
||||||
|
: Symbols.home,
|
||||||
shadows: shadows,
|
shadows: shadows,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/screens/tabs.dart';
|
||||||
|
import 'package:island/services/responsive.dart';
|
||||||
|
|
||||||
class ConditionalBottomNav extends HookConsumerWidget {
|
class ConditionalBottomNav extends HookConsumerWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@@ -17,10 +19,11 @@ class ConditionalBottomNav extends HookConsumerWidget {
|
|||||||
return null;
|
return null;
|
||||||
}, [currentLocation]);
|
}, [currentLocation]);
|
||||||
|
|
||||||
// Use the same route logic as TabsScreen for consistency
|
final routes = kTabRoutes.sublist(
|
||||||
const mainTabRoutes = ['/', '/chat', '/realms', '/account'];
|
0,
|
||||||
|
isWideScreen(context) ? null : kWideScreenRouteStart,
|
||||||
final shouldShowBottomNav = mainTabRoutes.contains(currentLocation);
|
);
|
||||||
|
final shouldShowBottomNav = routes.contains(currentLocation);
|
||||||
|
|
||||||
return shouldShowBottomNav ? child : const SizedBox.shrink();
|
return shouldShowBottomNav ? child : const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user