diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2414665..ed6fb03 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,4 +7,4 @@ RUN apt-get clean RUN git clone https://github.com/flutter/flutter.git -b stable --depth 1 /flutter ENV PATH="/flutter/bin:$PATH" -RUN flutter doctor +RUN flutter doctor && dart --disable-analytics && flutter --disable-analytics diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ca34df1..93efe58 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,8 @@ "vscode": { "extensions": [ "Dart-Code.dart-code", - "Dart-Code.flutter" + "Dart-Code.flutter", + "Google.arb-editor" ] } } diff --git a/lib/components/gallery_basic_info.dart b/lib/components/gallery_basic_info.dart index 99f9147..2326fb8 100644 --- a/lib/components/gallery_basic_info.dart +++ b/lib/components/gallery_basic_info.dart @@ -10,7 +10,7 @@ class GalleryBasicInfo extends StatelessWidget { const GalleryBasicInfo(this.gMeta, this.firstPage, {super.key, this.fileId, this.gData, this.files}); final GMeta gMeta; - final ExtendedPMeta firstPage; + final ExtendedPMeta? firstPage; final int? fileId; final GalleryData? gData; final EhFiles? files; @@ -27,7 +27,9 @@ class GalleryBasicInfo extends StatelessWidget { flex: 2, child: Container( margin: const EdgeInsets.only(right: 8), - child: Thumbnail(firstPage, fileId: fileId, gid: gMeta.gid), + child: firstPage != null + ? Thumbnail(firstPage!, fileId: fileId, gid: gMeta.gid) + : Container(), )), Expanded( flex: 3, diff --git a/lib/components/gallery_info.dart b/lib/components/gallery_info.dart index 058d11c..d9dd2a3 100644 --- a/lib/components/gallery_info.dart +++ b/lib/components/gallery_info.dart @@ -53,9 +53,9 @@ class _GalleryInfo extends State with ThemeModeWidget { @override Widget build(BuildContext context) { bool useMobile = MediaQuery.of(context).size.width <= 810; - final firstPage = widget.gData.pages.first; - final int? firstFileId = widget.files != null - ? widget.files!.files[firstPage.token]!.first.id + final firstPage = widget.gData.pages.firstOrNull; + final int? firstFileId = widget.files != null && firstPage != null + ? widget.files!.files[firstPage!.token]!.firstOrNull?.id : null; return CustomScrollView( controller: controller, diff --git a/lib/components/gallery_info_desktop.dart b/lib/components/gallery_info_desktop.dart index 14d70ed..9a61be4 100644 --- a/lib/components/gallery_info_desktop.dart +++ b/lib/components/gallery_info_desktop.dart @@ -59,9 +59,12 @@ class GalleryInfoDesktop extends StatelessWidget { child: Row(children: [ Expanded( flex: 3, - child: Padding(padding: const EdgeInsets.only(right: 8.0), - child: Thumbnail(gData.pages.first, - fileId: fileId, gid: gData.meta.gid))), + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: gData.pages.isNotEmpty + ? Thumbnail(gData.pages.first, + fileId: fileId, gid: gData.meta.gid) + : Container())), Expanded( flex: 7, child: Column( diff --git a/lib/components/thumbnail_gridview.dart b/lib/components/thumbnail_gridview.dart index 54c9b53..8dc643c 100644 --- a/lib/components/thumbnail_gridview.dart +++ b/lib/components/thumbnail_gridview.dart @@ -23,7 +23,7 @@ class ThumbnailGridView extends StatelessWidget { itemBuilder: (context, index) { final page = npages[index]!; final fileId = - files != null ? files!.files[page.token]!.first.id : null; + files != null ? files!.files[page.token]!.firstOrNull?.id : null; final key = Key("thumbnail$gid-${page.index}"); return Container( padding: const EdgeInsets.all(4), diff --git a/lib/dialog/task_page.dart b/lib/dialog/task_page.dart index b45a0ed..98465cd 100644 --- a/lib/dialog/task_page.dart +++ b/lib/dialog/task_page.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; @@ -89,13 +90,42 @@ class _TaskPage extends State { final maxWidth = MediaQuery.of(context).size.width; final endIndent = maxWidth < 400 ? 5.0 : 10.0; final indent = endIndent + 70; + final allowLink = task.base.gid != 0 && + (typ != TaskType.download || + task.status == TaskStatus.running || + task.status == TaskStatus.finished); + String? title; + if (typ == TaskType.download && tasks.meta.containsKey(task.base.gid)) { + title = tasks.meta[task.base.gid]!.preferredTitle; + } + final cs = Theme.of(context).colorScheme; return Column( children: [ _KeyValue(i18n.taskId, widget.id.toString(), fontSize: 16), Divider(indent: indent, endIndent: endIndent), _KeyValue(i18n.taskType, typ.text(context), fontSize: 16), Divider(indent: indent, endIndent: endIndent), - _KeyValue(i18n.gid, gid, fontSize: 16), + Row(children: [ + SizedBox( + width: 80, + child: Center( + child: Text(i18n.gid, + textAlign: TextAlign.center, + style: TextStyle(color: cs.primary, fontSize: 16)))), + Expanded( + child: allowLink + ? SelectableText.rich(TextSpan( + text: gid, + style: TextStyle(color: cs.secondary, fontSize: 16), + mouseCursor: SystemMouseCursors.click, + recognizer: TapGestureRecognizer() + ..onTap = () { + context.push("/gallery/${task.base.gid}", + extra: GalleryPageExtra(title: title)); + })) + : SelectableText(gid, + style: TextStyle(color: cs.secondary, fontSize: 16))), + ]), task.base.token.isEmpty ? Container() : Divider(indent: indent, endIndent: endIndent), @@ -324,8 +354,12 @@ class _TaskPage extends State { final indent = endIndent + 70; return Column(key: ValueKey("task_detail_meta_${widget.id}"), children: [ _KeyValue(i18n.title2, meta.title, fontSize: 16), - Divider(indent: indent, endIndent: endIndent), - _KeyValue(i18n.titleJpn, meta.titleJpn, fontSize: 16), + meta.titleJpn.isNotEmpty + ? Divider(indent: indent, endIndent: endIndent) + : Container(), + meta.titleJpn.isNotEmpty + ? _KeyValue(i18n.titleJpn, meta.titleJpn, fontSize: 16) + : Container(), Divider(indent: indent, endIndent: endIndent), _KeyValue(i18n.category, meta.category, fontSize: 16), Divider(indent: indent, endIndent: endIndent),