From 28c4a17c43a09697bddeb37db1e0ecfb1ab21581 Mon Sep 17 00:00:00 2001 From: Flummi Date: Wed, 4 Jun 2025 12:35:09 +0200 Subject: [PATCH] v1.0.24+24 - tags lul --- lib/screens/DetailView.dart | 86 +++++++++++++++---- lib/screens/MediaGrid.dart | 166 +++++++++++++++++++++++++----------- lib/services/Api.dart | 2 + pubspec.yaml | 2 +- 4 files changed, 190 insertions(+), 66 deletions(-) diff --git a/lib/screens/DetailView.dart b/lib/screens/DetailView.dart index b55b8b7..b6437b9 100644 --- a/lib/screens/DetailView.dart +++ b/lib/screens/DetailView.dart @@ -12,6 +12,7 @@ class DetailView extends StatefulWidget { final String type; final int mode; final bool random; + final String? tagname; const DetailView({ super.key, @@ -20,6 +21,7 @@ class DetailView extends StatefulWidget { required this.type, required this.mode, required this.random, + required this.tagname, }); @override @@ -29,6 +31,7 @@ class DetailView extends StatefulWidget { class _DetailViewState extends State { late PageController _pageController; late List mediaItems; + String? _tagname; int currentItemId = 0; bool isLoading = false; @@ -36,6 +39,7 @@ class _DetailViewState extends State { void initState() { super.initState(); mediaItems = widget.mediaItems; + _tagname = widget.tagname; final initialIndex = mediaItems.indexWhere( (item) => item.id == widget.initialItemId, ); @@ -67,6 +71,7 @@ class _DetailViewState extends State { type: widget.type, mode: widget.mode, random: widget.random, + tag: _tagname, ); if (mounted && newMedia.isNotEmpty) { setState(() => mediaItems.addAll(newMedia)); @@ -110,18 +115,56 @@ class _DetailViewState extends State { title: Text('f0ck #$currentItemId (${widget.type})'), centerTitle: true, ), - body: PageTransformer( - controller: _pageController, - pages: mediaItems.map((item) { - return Scaffold( - body: SafeArea( - child: SmartRefreshIndicator( - onRefresh: _refreshMediaItem, - child: _buildMediaItem(item), + body: Stack( + children: [ + PageTransformer( + controller: _pageController, + pages: mediaItems.map((item) { + return Scaffold( + body: SafeArea( + child: SmartRefreshIndicator( + onRefresh: _refreshMediaItem, + child: _buildMediaItem(item), + ), + ), + ); + }).toList(), + ), + if (_tagname != null) + Positioned( + bottom: 60, + left: MediaQuery.of(context).size.width * 0.2, + right: MediaQuery.of(context).size.width * 0.2, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, + ), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Tag: $_tagname', + style: const TextStyle(color: Colors.white), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + setState(() { + _tagname = '___empty___'; + Navigator.pop(context, _tagname); + }); + }, + ), + ], + ), ), ), - ); - }).toList(), + ], ), ); } @@ -150,14 +193,23 @@ class _DetailViewState extends State { alignment: WrapAlignment.center, spacing: 5.0, children: item.tags.map((tag) { - return Chip( - label: Text(tag.tag), - backgroundColor: switch (tag.id) { - 1 => Colors.green, - 2 => Colors.red, - _ => const Color(0xFF090909), + return GestureDetector( + onTap: () { + if (tag.tag == 'sfw' || tag.tag == 'nsfw') return; + setState(() { + _tagname = tag.tag; + Navigator.pop(context, _tagname); + }); }, - labelStyle: const TextStyle(color: Colors.white), + child: Chip( + label: Text(tag.tag), + backgroundColor: switch (tag.id) { + 1 => Colors.green, + 2 => Colors.red, + _ => const Color(0xFF090909), + }, + labelStyle: const TextStyle(color: Colors.white), + ), ); }).toList(), ), diff --git a/lib/screens/MediaGrid.dart b/lib/screens/MediaGrid.dart index 457d519..29a3659 100644 --- a/lib/screens/MediaGrid.dart +++ b/lib/screens/MediaGrid.dart @@ -14,7 +14,7 @@ class MediaGrid extends StatefulWidget { class _MediaGridState extends State { final ScrollController _scrollController = ScrollController(); - final String _version = '1.0.23+23'; + final String _version = '1.0.24+24'; List mediaItems = []; bool isLoading = false; Timer? _debounceTimer; @@ -24,6 +24,7 @@ class _MediaGridState extends State { int _selectedMode = 0; bool _random = false; final List _modes = ["sfw", "nsfw", "untagged", "all"]; + String? _selectedTag; @override void initState() { @@ -63,6 +64,7 @@ class _MediaGridState extends State { type: _selectedType, mode: _selectedMode, random: _random, + tag: _selectedTag, ); if (mounted) { setState(() => mediaItems.addAll(newMedia)); @@ -86,6 +88,7 @@ class _MediaGridState extends State { type: _selectedType, mode: _selectedMode, random: _random, + tag: _selectedTag, ); if (mounted) { setState(() { @@ -110,7 +113,7 @@ class _MediaGridState extends State { _navigationCompleter = Completer(); try { if (mounted) { - await Navigator.push( + final String? newTag = await Navigator.push( context, MaterialPageRoute( builder: (context) => DetailView( @@ -119,9 +122,22 @@ class _MediaGridState extends State { type: _selectedType, mode: _selectedMode, random: _random, + tagname: _selectedTag, ), ), ); + + if (newTag != null) { + setState(() { + if (newTag == '___empty___') { + _selectedTag = null; + } + else { + _selectedTag = newTag; + } + _refreshMedia(); + }); + } } } catch (e) { if (mounted) { @@ -153,9 +169,9 @@ class _MediaGridState extends State { _refreshMedia(); }); }, - ) - ] - ) + ), + ], + ), ), bottomNavigationBar: BottomAppBar( color: const Color.fromARGB(255, 43, 43, 43), @@ -228,51 +244,105 @@ class _MediaGridState extends State { ), ), ), - body: RefreshIndicator( - onRefresh: _refreshMedia, - child: GridView.builder( - controller: _scrollController, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: _calculateCrossAxisCount(context), - crossAxisSpacing: 5.0, - mainAxisSpacing: 5.0, - ), - itemCount: mediaItems.length + (isLoading ? 1 : 0), - itemBuilder: (context, index) { - if (index >= mediaItems.length) { - return const Center(child: CircularProgressIndicator()); - } - final item = mediaItems[index]; - - return InkWell( - onTap: () => _navigateToDetail(item), - child: Stack( - fit: StackFit.expand, - children: [ - CachedNetworkImage( - imageUrl: item.thumbnailUrl, - fit: BoxFit.cover, - placeholder: (context, url) => SizedBox.shrink(), - errorWidget: (context, url, error) => Icon(Icons.error), - ), - Align( - alignment: FractionalOffset.bottomRight, - child: Icon( - Icons.square, - color: switch (item.mode) { - 1 => Colors.green, - 2 => Colors.red, - _ => Colors.yellow - }, - size: 15.0 - ), - ), - ], + body: Stack( + children: [ + RefreshIndicator( + onRefresh: _refreshMedia, + child: GridView.builder( + controller: _scrollController, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _calculateCrossAxisCount(context), + crossAxisSpacing: 5.0, + mainAxisSpacing: 5.0, ), - ); - }, - ), + itemCount: mediaItems.length + (isLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index >= mediaItems.length) { + return const Center(child: CircularProgressIndicator()); + } + final item = mediaItems[index]; + + return InkWell( + onTap: () => _navigateToDetail(item), + child: Stack( + fit: StackFit.expand, + children: [ + CachedNetworkImage( + imageUrl: item.thumbnailUrl, + fit: BoxFit.cover, + placeholder: (context, url) => SizedBox.shrink(), + errorWidget: (context, url, error) => Icon(Icons.error), + ), + Align( + alignment: FractionalOffset.bottomRight, + child: Icon( + Icons.square, + color: switch (item.mode) { + 1 => Colors.green, + 2 => Colors.red, + _ => Colors.yellow, + }, + size: 15.0, + ), + ), + ], + ), + ); + }, + ), + ), + if (_selectedTag != null) + Positioned( + bottom: 20, + left: MediaQuery.of(context).size.width * 0.2, + right: MediaQuery.of(context).size.width * 0.2, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, + ), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Tag: $_selectedTag', + style: const TextStyle(color: Colors.white), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + setState(() { + _selectedTag = null; + _refreshMedia(); + }); + _scrollController.animateTo( + 0.0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeOut, + ); + }, + ), + ], + ), + ), + ), + ], ), + /*floatingActionButton: FloatingActionButton( + backgroundColor: Colors.black.withValues(alpha: 0.8), + child: const Icon(Icons.arrow_upward, color: Colors.white), + onPressed: () { + _scrollController.animateTo( + 0.0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeOut, + ); + }, + ),*/ ); } diff --git a/lib/services/Api.dart b/lib/services/Api.dart index c7a1a56..9c88cfa 100644 --- a/lib/services/Api.dart +++ b/lib/services/Api.dart @@ -8,12 +8,14 @@ Future> fetchMedia({ String? type, int? mode, bool? random, + String? tag, }) async { final Uri url = Uri.parse('https://api.f0ck.me/items/get').replace( queryParameters: { 'type': type ?? 'image', 'mode': (mode ?? 0).toString(), 'random': (random! ? 1 : 0).toString(), + if (tag != null) 'tag': tag, if (older != null) 'older': older, }, ); diff --git a/pubspec.yaml b/pubspec.yaml index bbb3a99..3e53f59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.23+23 +version: 1.0.24+24 environment: sdk: ^3.9.0-100.2.beta