From 7497b77384cbd11e2a65af9b8a4248f5f23e4dc5 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 2 Nov 2025 17:45:03 +0800 Subject: [PATCH] :lipstick: Adjusted developer hub --- assets/i18n/en-US.json | 3 +- assets/i18n/zh-CN.json | 10 +- lib/screens/developers/apps.dart | 258 ++++++++++-------- lib/screens/developers/bots.dart | 195 +++++++------ lib/screens/developers/hub.dart | 167 ------------ .../developers/project_detail_view.dart | 32 +++ lib/screens/explore.dart | 2 +- 7 files changed, 297 insertions(+), 370 deletions(-) diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index b6128601..d1622e15 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1309,5 +1309,6 @@ "presenceTypeGaming": "Playing", "presenceTypeMusic": "Listening to Music", "presenceTypeWorkout": "Working out", - "articleCompose": "Compose Article" + "articleCompose": "Compose Article", + "backToHub": "Back to Hub" } diff --git a/assets/i18n/zh-CN.json b/assets/i18n/zh-CN.json index ed2866b4..9637c7e9 100644 --- a/assets/i18n/zh-CN.json +++ b/assets/i18n/zh-CN.json @@ -158,11 +158,11 @@ "checkIn": "签到", "checkInNone": "尚未签到", "checkInNoneHint": "通过签到获取您的财富提示和每日奖励。", - "checkInResultLevel0": "最差运气", - "checkInResultLevel1": "坏运气", - "checkInResultLevel2": "一个普通的日常", - "checkInResultLevel3": "好运", - "checkInResultLevel4": "最佳运气", + "checkInResultLevel0": "大凶", + "checkInResultLevel1": "凶", + "checkInResultLevel2": "中平", + "checkInResultLevel3": "吉", + "checkInResultLevel4": "大吉", "checkInActivityTitle": "{} 在 {} 签到并获得了 {}", "eventCalander": "活动日历", "eventCalanderEmpty": "该日无活动。", diff --git a/lib/screens/developers/apps.dart b/lib/screens/developers/apps.dart index eb7cd6f0..e43a9dab 100644 --- a/lib/screens/developers/apps.dart +++ b/lib/screens/developers/apps.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -7,6 +8,7 @@ import 'package:island/models/custom_app.dart'; import 'package:island/pods/network.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/content/cloud_files.dart'; +import 'package:island/widgets/extended_refresh_indicator.dart'; import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -83,129 +85,159 @@ class CustomAppsScreen extends HookConsumerWidget { ), ); } - return RefreshIndicator( + return ExtendedRefreshIndicator( onRefresh: () => ref.refresh( customAppsProvider(publisherName, projectId).future, ), - child: ListView.builder( - padding: EdgeInsets.only(top: 4), - itemCount: data.length, - itemBuilder: (context, index) { - final app = data[index]; - return Card( - margin: const EdgeInsets.all(8.0), - clipBehavior: Clip.antiAlias, - child: InkWell( - onTap: () { - context.pushNamed( - 'developerAppDetail', - pathParameters: { - 'name': publisherName, - 'projectId': projectId, - 'appId': app.id, - }, - ); - }, - child: Column( - children: [ - SizedBox( - height: 150, - child: Stack( - fit: StackFit.expand, + child: Column( + children: [ + const Gap(8), + Card( + child: ListTile( + title: Text('customApps').tr().padding(horizontal: 8), + trailing: IconButton( + onPressed: () { + context.pushNamed( + 'developerAppNew', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + }, + ); + }, + icon: const Icon(Symbols.add), + ), + ), + ), + Expanded( + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: data.length, + itemBuilder: (context, index) { + final app = data[index]; + return Card( + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () { + context.pushNamed( + 'developerAppDetail', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + 'appId': app.id, + }, + ); + }, + child: Column( children: [ - if (app.background != null) - CloudFileWidget( - item: app.background!, - fit: BoxFit.cover, - ).clipRRect(topLeft: 8, topRight: 8), - if (app.picture != null) - Positioned( - left: 16, - bottom: 16, - child: ProfilePictureWidget( - fileId: app.picture!.id, - radius: 40, - fallbackIcon: Symbols.apps, - ), + SizedBox( + height: 150, + child: Stack( + fit: StackFit.expand, + children: [ + if (app.background != null) + CloudFileWidget( + item: app.background!, + fit: BoxFit.cover, + ).clipRRect(topLeft: 8, topRight: 8), + if (app.picture != null) + Positioned( + left: 16, + bottom: 16, + child: ProfilePictureWidget( + fileId: app.picture!.id, + radius: 40, + fallbackIcon: Symbols.apps, + ), + ), + ], ), + ), + ListTile( + title: Text(app.name), + subtitle: Text( + app.slug, + style: GoogleFonts.robotoMono(fontSize: 12), + ), + contentPadding: EdgeInsets.only( + left: 20, + right: 12, + ), + trailing: PopupMenuButton( + itemBuilder: + (context) => [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Symbols.edit), + const SizedBox(width: 12), + Text('edit').tr(), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + const Icon( + Symbols.delete, + color: Colors.red, + ), + const SizedBox(width: 12), + Text( + 'delete', + style: TextStyle( + color: Colors.red, + ), + ).tr(), + ], + ), + ), + ], + onSelected: (value) { + if (value == 'edit') { + context.pushNamed( + 'developerAppEdit', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + 'id': app.id, + }, + ); + } else if (value == 'delete') { + showConfirmAlert( + 'deleteCustomAppHint'.tr(), + 'deleteCustomApp'.tr(), + ).then((confirm) { + if (confirm) { + final client = ref.read( + apiClientProvider, + ); + client.delete( + '/develop/developers/$publisherName/projects/$projectId/apps/${app.id}', + ); + ref.invalidate( + customAppsProvider( + publisherName, + projectId, + ), + ); + } + }); + } + }, + ), + ), ], ), ), - ListTile( - title: Text(app.name), - subtitle: Text( - app.slug, - style: GoogleFonts.robotoMono(fontSize: 12), - ), - contentPadding: EdgeInsets.only(left: 20, right: 12), - trailing: PopupMenuButton( - itemBuilder: - (context) => [ - PopupMenuItem( - value: 'edit', - child: Row( - children: [ - const Icon(Symbols.edit), - const SizedBox(width: 12), - Text('edit').tr(), - ], - ), - ), - PopupMenuItem( - value: 'delete', - child: Row( - children: [ - const Icon( - Symbols.delete, - color: Colors.red, - ), - const SizedBox(width: 12), - Text( - 'delete', - style: TextStyle(color: Colors.red), - ).tr(), - ], - ), - ), - ], - onSelected: (value) { - if (value == 'edit') { - context.pushNamed( - 'developerAppEdit', - pathParameters: { - 'name': publisherName, - 'projectId': projectId, - 'id': app.id, - }, - ); - } else if (value == 'delete') { - showConfirmAlert( - 'deleteCustomAppHint'.tr(), - 'deleteCustomApp'.tr(), - ).then((confirm) { - if (confirm) { - final client = ref.read(apiClientProvider); - client.delete( - '/develop/developers/$publisherName/projects/$projectId/apps/${app.id}', - ); - ref.invalidate( - customAppsProvider( - publisherName, - projectId, - ), - ); - } - }); - } - }, - ), - ), - ], - ), + ); + }, ), - ); - }, + ), + ], ), ); }, diff --git a/lib/screens/developers/bots.dart b/lib/screens/developers/bots.dart index dbc3ff54..b45277b8 100644 --- a/lib/screens/developers/bots.dart +++ b/lib/screens/developers/bots.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/bot.dart'; @@ -10,6 +11,7 @@ import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:island/widgets/extended_refresh_indicator.dart'; +import 'package:styled_widget/styled_widget.dart'; part 'bots.g.dart'; @@ -64,95 +66,122 @@ class BotsScreen extends HookConsumerWidget { return ExtendedRefreshIndicator( onRefresh: () => ref.refresh(botsProvider(publisherName, projectId).future), - child: ListView.builder( - padding: const EdgeInsets.only(top: 4), - itemCount: data.length, - itemBuilder: (context, index) { - final bot = data[index]; - return Card( - margin: const EdgeInsets.all(8.0), + child: Column( + children: [ + const Gap(8), + Card( child: ListTile( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8.0)), - ), - leading: CircleAvatar( - child: - bot.account.profile.picture != null - ? ProfilePictureWidget( - file: bot.account.profile.picture!, - ) - : const Icon(Symbols.smart_toy), - ), - title: Text(bot.account.nick), - subtitle: Text(bot.account.name), - trailing: PopupMenuButton( - itemBuilder: - (context) => [ - PopupMenuItem( - value: 'edit', - child: Row( - children: [ - const Icon(Symbols.edit), - const SizedBox(width: 12), - Text('edit').tr(), - ], - ), - ), - PopupMenuItem( - value: 'delete', - child: Row( - children: [ - const Icon(Symbols.delete, color: Colors.red), - const SizedBox(width: 12), - Text( - 'delete', - style: TextStyle(color: Colors.red), - ).tr(), - ], - ), - ), - ], - onSelected: (value) { - if (value == 'edit') { - context.pushNamed( - 'developerBotEdit', - pathParameters: { - 'name': publisherName, - 'projectId': projectId, - 'id': bot.id, - }, - ); - } else if (value == 'delete') { - showConfirmAlert( - 'deleteBotHint'.tr(), - 'deleteBot'.tr(), - ).then((confirm) { - if (confirm) { - final client = ref.read(apiClientProvider); - client.delete( - '/develop/developers/$publisherName/projects/$projectId/bots/${bot.id}', - ); - ref.invalidate( - botsProvider(publisherName, projectId), - ); - } - }); - } + title: Text('bots').tr().padding(horizontal: 8), + trailing: IconButton( + onPressed: () { + context.pushNamed( + 'developerBotNew', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + }, + ); }, + icon: const Icon(Symbols.add), ), - onTap: () { - context.pushNamed( - 'developerBotDetail', - pathParameters: { - 'name': publisherName, - 'projectId': projectId, - 'botId': bot.id, - }, + ), + ), + Expanded( + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: data.length, + itemBuilder: (context, index) { + final bot = data[index]; + return Card( + margin: const EdgeInsets.all(8.0), + child: ListTile( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + leading: CircleAvatar( + child: + bot.account.profile.picture != null + ? ProfilePictureWidget( + file: bot.account.profile.picture!, + ) + : const Icon(Symbols.smart_toy), + ), + title: Text(bot.account.nick), + subtitle: Text(bot.account.name), + trailing: PopupMenuButton( + itemBuilder: + (context) => [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Symbols.edit), + const SizedBox(width: 12), + Text('edit').tr(), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + const Icon( + Symbols.delete, + color: Colors.red, + ), + const SizedBox(width: 12), + Text( + 'delete', + style: TextStyle(color: Colors.red), + ).tr(), + ], + ), + ), + ], + onSelected: (value) { + if (value == 'edit') { + context.pushNamed( + 'developerBotEdit', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + 'id': bot.id, + }, + ); + } else if (value == 'delete') { + showConfirmAlert( + 'deleteBotHint'.tr(), + 'deleteBot'.tr(), + ).then((confirm) { + if (confirm) { + final client = ref.read(apiClientProvider); + client.delete( + '/develop/developers/$publisherName/projects/$projectId/bots/${bot.id}', + ); + ref.invalidate( + botsProvider(publisherName, projectId), + ); + } + }); + } + }, + ), + onTap: () { + context.pushNamed( + 'developerBotDetail', + pathParameters: { + 'name': publisherName, + 'projectId': projectId, + 'botId': bot.id, + }, + ); + }, + ), ); }, ), - ); - }, + ), + ], ), ); }, diff --git a/lib/screens/developers/hub.dart b/lib/screens/developers/hub.dart index 1add2b07..70d860ea 100644 --- a/lib/screens/developers/hub.dart +++ b/lib/screens/developers/hub.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -99,15 +97,6 @@ class DeveloperHubScreen extends HookConsumerWidget { ), body: Column( children: [ - if (currentProject.value == null) - ...([ - // Welcome Section - _WelcomeSection(currentDeveloper: currentDeveloper.value), - - // Navigation Tabs - _NavigationTabs(), - ]), - // Main Content if (currentProject.value != null) Expanded( @@ -195,162 +184,6 @@ class _ConsoleAppBar extends StatelessWidget implements PreferredSizeWidget { } } -// Welcome Section -class _WelcomeSection extends StatelessWidget { - final SnDeveloper? currentDeveloper; - - const _WelcomeSection({required this.currentDeveloper}); - - @override - Widget build(BuildContext context) { - final isDark = Theme.of(context).brightness == Brightness.dark; - return Stack( - children: [ - Positioned.fill( - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: - isDark - ? [ - Theme.of(context).colorScheme.surfaceContainerHighest, - Theme.of(context).colorScheme.surfaceContainerLow, - ] - : [const Color(0xFFE8F0FE), const Color(0xFFF1F3F4)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - ), - ), - ), - Positioned( - right: 16, - top: 0, - bottom: 0, - child: _RandomStickerImage( - width: 180, - height: 180, - ).opacity(isWideScreen(context) ? 1 : 0.5), - ), - Container( - height: 180, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Good morning!', - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.w400, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - const Gap(4), - Text( - currentDeveloper != null - ? "You're working as ${currentDeveloper!.publisher!.nick}" - : "Choose a developer and continue.", - style: TextStyle( - fontSize: 16, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ), - ], - ); - } -} - -// Random Sticker Image Widget -class _RandomStickerImage extends StatelessWidget { - final double? width; - final double? height; - - const _RandomStickerImage({this.width, this.height}); - - static const List _stickers = [ - 'assets/images/stickers/clap.png', - 'assets/images/stickers/confuse.png', - 'assets/images/stickers/pray.png', - 'assets/images/stickers/thumb_up.png', - ]; - - String _getRandomSticker() { - final random = Random(); - return _stickers[random.nextInt(_stickers.length)]; - } - - @override - Widget build(BuildContext context) { - return Image.asset( - _getRandomSticker(), - width: width ?? 80, - height: height ?? 80, - fit: BoxFit.contain, - ); - } -} - -// Navigation Tabs -class _NavigationTabs extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface), - child: Row( - children: [ - const Gap(24), - _NavTabItem(title: 'Dashboard', isActive: true), - ], - ), - ); - } -} - -class _NavTabItem extends StatelessWidget { - final String title; - final bool isActive; - - const _NavTabItem({required this.title, this.isActive = false}); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: - isActive - ? Theme.of(context).colorScheme.primary - : Colors.transparent, - width: 2, - ), - ), - ), - child: Row( - children: [ - Text( - title, - style: TextStyle( - color: - isActive - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurface, - fontWeight: isActive ? FontWeight.w500 : FontWeight.w400, - ), - ), - ], - ), - ); - } -} - // Main Content Section class _MainContentSection extends HookConsumerWidget { final SnDeveloper? currentDeveloper; diff --git a/lib/screens/developers/project_detail_view.dart b/lib/screens/developers/project_detail_view.dart index 253de0da..55608d66 100644 --- a/lib/screens/developers/project_detail_view.dart +++ b/lib/screens/developers/project_detail_view.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/dev_project.dart'; import 'package:island/screens/developers/apps.dart'; @@ -54,6 +55,36 @@ class ProjectDetailView extends HookConsumerWidget { label: Text('bots'.tr()), ), ], + leading: Container( + width: 256, + padding: EdgeInsets.only( + left: 16, + right: 16, + bottom: 8, + top: 2, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.12), + ), + ), + ), + child: Row( + spacing: 8, + children: [ + IconButton( + onPressed: onBackToHub, + icon: const Icon(Icons.arrow_back), + iconSize: 16, + visualDensity: VisualDensity.compact, + ), + Expanded(child: Text("backToHub").tr()), + ], + ), + ), ), ), ), @@ -69,6 +100,7 @@ class ProjectDetailView extends HookConsumerWidget { ], ), ), + const Gap(4), ], ); } else { diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 24a45bc7..18c2e446 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -737,7 +737,7 @@ class ActivityListNotifier extends _$ActivityListNotifier }; final response = await client.get( - '/sphere/activities', + '/sphere/timeline', queryParameters: queryParameters, );