💄 Adjusted developer hub

This commit is contained in:
2025-11-02 17:45:03 +08:00
parent f542d9fa97
commit 7497b77384
7 changed files with 297 additions and 370 deletions

View File

@@ -1309,5 +1309,6 @@
"presenceTypeGaming": "Playing", "presenceTypeGaming": "Playing",
"presenceTypeMusic": "Listening to Music", "presenceTypeMusic": "Listening to Music",
"presenceTypeWorkout": "Working out", "presenceTypeWorkout": "Working out",
"articleCompose": "Compose Article" "articleCompose": "Compose Article",
"backToHub": "Back to Hub"
} }

View File

@@ -158,11 +158,11 @@
"checkIn": "签到", "checkIn": "签到",
"checkInNone": "尚未签到", "checkInNone": "尚未签到",
"checkInNoneHint": "通过签到获取您的财富提示和每日奖励。", "checkInNoneHint": "通过签到获取您的财富提示和每日奖励。",
"checkInResultLevel0": "最差运气", "checkInResultLevel0": "大凶",
"checkInResultLevel1": "坏运气", "checkInResultLevel1": "",
"checkInResultLevel2": "一个普通的日常", "checkInResultLevel2": "中平",
"checkInResultLevel3": "好运", "checkInResultLevel3": "",
"checkInResultLevel4": "最佳运气", "checkInResultLevel4": "大吉",
"checkInActivityTitle": "{} 在 {} 签到并获得了 {}", "checkInActivityTitle": "{} 在 {} 签到并获得了 {}",
"eventCalander": "活动日历", "eventCalander": "活动日历",
"eventCalanderEmpty": "该日无活动。", "eventCalanderEmpty": "该日无活动。",

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/pods/network.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.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:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -83,129 +85,159 @@ class CustomAppsScreen extends HookConsumerWidget {
), ),
); );
} }
return RefreshIndicator( return ExtendedRefreshIndicator(
onRefresh: onRefresh:
() => ref.refresh( () => ref.refresh(
customAppsProvider(publisherName, projectId).future, customAppsProvider(publisherName, projectId).future,
), ),
child: ListView.builder( child: Column(
padding: EdgeInsets.only(top: 4), children: [
itemCount: data.length, const Gap(8),
itemBuilder: (context, index) { Card(
final app = data[index]; child: ListTile(
return Card( title: Text('customApps').tr().padding(horizontal: 8),
margin: const EdgeInsets.all(8.0), trailing: IconButton(
clipBehavior: Clip.antiAlias, onPressed: () {
child: InkWell( context.pushNamed(
onTap: () { 'developerAppNew',
context.pushNamed( pathParameters: {
'developerAppDetail', 'name': publisherName,
pathParameters: { 'projectId': projectId,
'name': publisherName, },
'projectId': projectId, );
'appId': app.id, },
}, icon: const Icon(Symbols.add),
); ),
}, ),
child: Column( ),
children: [ Expanded(
SizedBox( child: ListView.builder(
height: 150, padding: EdgeInsets.zero,
child: Stack( itemCount: data.length,
fit: StackFit.expand, 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: [ children: [
if (app.background != null) SizedBox(
CloudFileWidget( height: 150,
item: app.background!, child: Stack(
fit: BoxFit.cover, fit: StackFit.expand,
).clipRRect(topLeft: 8, topRight: 8), children: [
if (app.picture != null) if (app.background != null)
Positioned( CloudFileWidget(
left: 16, item: app.background!,
bottom: 16, fit: BoxFit.cover,
child: ProfilePictureWidget( ).clipRRect(topLeft: 8, topRight: 8),
fileId: app.picture!.id, if (app.picture != null)
radius: 40, Positioned(
fallbackIcon: Symbols.apps, 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,
),
);
}
});
}
},
),
),
],
),
), ),
); ),
}, ],
), ),
); );
}, },

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:gap/gap.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/models/bot.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:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/widgets/extended_refresh_indicator.dart'; import 'package:island/widgets/extended_refresh_indicator.dart';
import 'package:styled_widget/styled_widget.dart';
part 'bots.g.dart'; part 'bots.g.dart';
@@ -64,95 +66,122 @@ class BotsScreen extends HookConsumerWidget {
return ExtendedRefreshIndicator( return ExtendedRefreshIndicator(
onRefresh: onRefresh:
() => ref.refresh(botsProvider(publisherName, projectId).future), () => ref.refresh(botsProvider(publisherName, projectId).future),
child: ListView.builder( child: Column(
padding: const EdgeInsets.only(top: 4), children: [
itemCount: data.length, const Gap(8),
itemBuilder: (context, index) { Card(
final bot = data[index];
return Card(
margin: const EdgeInsets.all(8.0),
child: ListTile( child: ListTile(
shape: const RoundedRectangleBorder( title: Text('bots').tr().padding(horizontal: 8),
borderRadius: BorderRadius.all(Radius.circular(8.0)), trailing: IconButton(
), onPressed: () {
leading: CircleAvatar( context.pushNamed(
child: 'developerBotNew',
bot.account.profile.picture != null pathParameters: {
? ProfilePictureWidget( 'name': publisherName,
file: bot.account.profile.picture!, 'projectId': projectId,
) },
: 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),
);
}
});
}
}, },
icon: const Icon(Symbols.add),
), ),
onTap: () { ),
context.pushNamed( ),
'developerBotDetail', Expanded(
pathParameters: { child: ListView.builder(
'name': publisherName, padding: EdgeInsets.zero,
'projectId': projectId, itemCount: data.length,
'botId': bot.id, 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,
},
);
},
),
); );
}, },
), ),
); ),
}, ],
), ),
); );
}, },

View File

@@ -1,5 +1,3 @@
import 'dart:math';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.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';
@@ -99,15 +97,6 @@ class DeveloperHubScreen extends HookConsumerWidget {
), ),
body: Column( body: Column(
children: [ children: [
if (currentProject.value == null)
...([
// Welcome Section
_WelcomeSection(currentDeveloper: currentDeveloper.value),
// Navigation Tabs
_NavigationTabs(),
]),
// Main Content // Main Content
if (currentProject.value != null) if (currentProject.value != null)
Expanded( 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<String> _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 // Main Content Section
class _MainContentSection extends HookConsumerWidget { class _MainContentSection extends HookConsumerWidget {
final SnDeveloper? currentDeveloper; final SnDeveloper? currentDeveloper;

View File

@@ -1,6 +1,7 @@
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:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/dev_project.dart'; import 'package:island/models/dev_project.dart';
import 'package:island/screens/developers/apps.dart'; import 'package:island/screens/developers/apps.dart';
@@ -54,6 +55,36 @@ class ProjectDetailView extends HookConsumerWidget {
label: Text('bots'.tr()), 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 { } else {

View File

@@ -737,7 +737,7 @@ class ActivityListNotifier extends _$ActivityListNotifier
}; };
final response = await client.get( final response = await client.get(
'/sphere/activities', '/sphere/timeline',
queryParameters: queryParameters, queryParameters: queryParameters,
); );