From 847fc6e8642b3f1d3909568e165d062c0e780a11 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 29 Jun 2025 23:34:56 +0800 Subject: [PATCH] :lipstick: Optimized custom app display --- assets/i18n/en-US.json | 2 + lib/screens/developers/apps.dart | 133 ++++++++++++++++++++++++++----- 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index a3cf418..efb4f60 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -643,6 +643,8 @@ "noCustomApps": "No custom apps yet.", "createCustomApp": "Create Custom App", "editCustomApp": "Edit Custom App", + "deleteCustomApp": "Delete Custom App", + "deleteCustomAppHint": "Are you sure you want to delete this custom app? This action cannot be undone.", "publicRealm": "Public Realm", "publicRealmDescription": "Anyone can preview the content of this realm.", "communityRealm": "Community Realm", diff --git a/lib/screens/developers/apps.dart b/lib/screens/developers/apps.dart index ae141b3..c8dc4ec 100644 --- a/lib/screens/developers/apps.dart +++ b/lib/screens/developers/apps.dart @@ -1,13 +1,17 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/custom_app.dart'; import 'package:island/pods/network.dart'; +import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; +import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:styled_widget/styled_widget.dart'; part 'apps.g.dart'; @@ -27,30 +31,123 @@ class CustomAppsScreen extends HookConsumerWidget { final apps = ref.watch(customAppsProvider(publisherName)); return AppScaffold( - appBar: AppBar(title: Text('customApps').tr()), - floatingActionButton: FloatingActionButton( - child: const Icon(Symbols.add), - onPressed: () { - context.push('/developers/$publisherName/apps/new'); - }, + appBar: AppBar( + title: Text('customApps').tr(), + actions: [ + IconButton( + icon: const Icon(Symbols.add), + onPressed: () { + context.push('/developers/$publisherName/apps/new'); + }, + ), + ], ), body: apps.when( data: (data) { if (data.isEmpty) { return Center(child: Text('noCustomApps').tr()); } - return ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) { - final app = data[index]; - return ListTile( - title: Text(app.name), - subtitle: Text(app.slug), - onTap: () { - context.push('/developers/$publisherName/apps/${app.id}'); - }, - ); - }, + return RefreshIndicator( + onRefresh: + () => ref.refresh(customAppsProvider(publisherName).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), + child: Column( + children: [ + 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.push( + '/developers/$publisherName/apps/${app.id}', + ); + } else if (value == 'delete') { + showConfirmAlert( + 'deleteCustomAppHint'.tr(), + 'deleteCustomApp'.tr(), + ).then((confirm) { + if (confirm) { + final client = ref.read(apiClientProvider); + client.delete( + '/developers/$publisherName/apps/${app.id}', + ); + ref.invalidate( + customAppsProvider(publisherName), + ); + } + }); + } + }, + ), + ), + ], + ), + ); + }, + ), ); }, loading: () => const Center(child: CircularProgressIndicator()),