diff --git a/lib/core/extension/order.dart b/lib/core/extension/order.dart index 5a7294c8..c94615dd 100644 --- a/lib/core/extension/order.dart +++ b/lib/core/extension/order.dart @@ -31,16 +31,54 @@ extension OrderX on Order { return indexOf(id); } - void moveById( - T oid, - T nid, { + void moveByItem( + List items, + int o, + int n, { StoreProperty>? property, _OnMove? onMove, }) { - final index = indexOf(oid); + if (o == n) return; + if (o < n) { + n -= 1; + } + final index = indexOf(items[o]); if (index == -1) return; - final newIndex = indexOf(nid); + var newIndex = indexOf(items[n]); if (newIndex == -1) return; + if (o < n) { + newIndex += 1; + } move(index, newIndex, property: property, onMove: onMove); } + + /// order: ['d', 'b', 'e']\ + /// items: ['a', 'b', 'c', 'd']\ + /// result: ['b', 'd', 'a', 'c']\ + /// return: ['e'] + List reorder({ + required List order, + required bool Function(T, String) finder, + }) { + final newOrder = []; + final missed = []; + final surplus = []; + for (final id in order.toSet()) { + try { + final item = firstWhere((e) => finder(e, id)); + newOrder.add(item); + } catch (e) { + surplus.add(id); + } + } + for (final item in this) { + if (!newOrder.contains(item)) { + missed.add(item); + } + } + clear(); + addAll(newOrder); + addAll(missed); + return surplus; + } } diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 0fcbb38e..f1d0580c 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -39,15 +39,15 @@ class ServerProvider extends BusyProvider { Future loadLocalData() async { setBusyState(true); - final infos = _serverStore.fetch(); - for (final info in infos) { - _servers[info.id] = genServer(info); + final spis = _serverStore.fetch(); + for (final spi in spis) { + _servers[spi.id] = genServer(spi); } final serverOrder_ = _settingStore.serverOrder.fetch(); if (serverOrder_ != null) { _serverOrder.addAll(serverOrder_.toSet()); - if (_serverOrder.length != infos.length) { - final missed = infos + if (_serverOrder.length != spis.length) { + final missed = spis .where( (e) => !_serverOrder.contains(e.id), ) diff --git a/lib/data/provider/snippet.dart b/lib/data/provider/snippet.dart index 8c9f7e5f..3f2ed7b2 100644 --- a/lib/data/provider/snippet.dart +++ b/lib/data/provider/snippet.dart @@ -6,6 +6,7 @@ import 'package:toolbox/data/store/snippet.dart'; import 'package:toolbox/locator.dart'; import '../../core/extension/order.dart'; +import '../store/setting.dart'; class SnippetProvider extends BusyProvider { late Order _snippets; @@ -15,9 +16,19 @@ class SnippetProvider extends BusyProvider { List get tags => _tags; final _store = locator(); + final _setting = locator(); void loadData() { _snippets = _store.fetch(); + final order = _setting.snippetOrder.fetch(); + if (order != null) { + final surplus = _snippets.reorder( + order: order, + finder: (order, name) => order.name == name, + ); + order.removeWhere((e) => surplus.any((ele) => ele == e)); + _setting.snippetOrder.put(order); + } _updateTags(); } @@ -36,15 +47,15 @@ class SnippetProvider extends BusyProvider { void add(Snippet snippet) { _snippets.add(snippet); _store.put(snippet); - notifyListeners(); _updateTags(); + notifyListeners(); } void del(Snippet snippet) { _snippets.remove(snippet); _store.delete(snippet); - notifyListeners(); _updateTags(); + notifyListeners(); } void update(Snippet old, Snippet newOne) { @@ -52,8 +63,8 @@ class SnippetProvider extends BusyProvider { _store.put(newOne); _snippets.remove(old); _snippets.add(newOne); - notifyListeners(); _updateTags(); + notifyListeners(); } void renameTag(String old, String newOne) { @@ -61,13 +72,11 @@ class SnippetProvider extends BusyProvider { if (s.tags?.contains(old) ?? false) { s.tags?.remove(old); s.tags?.add(newOne); + _store.put(s); } } - for (final s in _snippets) { - _store.put(s); - } - notifyListeners(); _updateTags(); + notifyListeners(); } String get export => json.encode(snippets); diff --git a/lib/view/page/home.dart b/lib/view/page/home.dart index 3ea01f88..afbccd2e 100644 --- a/lib/view/page/home.dart +++ b/lib/view/page/home.dart @@ -47,6 +47,8 @@ class _HomePageState extends State final _selectIndex = ValueNotifier(0); late S _s; + bool _switchingPage = false; + @override void initState() { super.initState(); @@ -119,12 +121,18 @@ class _HomePageState extends State ], ), body: PageView.builder( - physics: const NeverScrollableScrollPhysics(), controller: _pageController, itemBuilder: (_, index) => AppTab.values[index].page, + onPageChanged: (value) { + if (!_switchingPage) { + _selectIndex.value = value; + } + }, + ), + bottomNavigationBar: ValueBuilder( + listenable: _selectIndex, + build: _buildBottomBar, ), - bottomNavigationBar: - ValueBuilder(listenable: _selectIndex, build: _buildBottomBar), ); } @@ -133,12 +141,17 @@ class _HomePageState extends State selectedIndex: _selectIndex.value, animationDuration: const Duration(milliseconds: 250), onDestinationSelected: (int index) { + if (_selectIndex.value == index) return; _selectIndex.value = index; + _switchingPage = true; _pageController.animateToPage( index, duration: const Duration(milliseconds: 677), curve: Curves.fastLinearToSlowEaseIn, ); + Future.delayed(const Duration(milliseconds: 677), () { + _switchingPage = false; + }); }, labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, destinations: [ diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index cb8cca5f..f8fbde9f 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -10,6 +10,7 @@ import 'package:toolbox/core/utils/misc.dart'; import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/provider/snippet.dart'; import 'package:toolbox/view/page/process.dart'; +import 'package:toolbox/view/widget/fade_in.dart'; import 'package:toolbox/view/widget/tag/picker.dart'; import 'package:toolbox/view/widget/tag/switcher.dart'; @@ -117,15 +118,18 @@ class _ServerPageState extends State ), padding: const EdgeInsets.fromLTRB(7, 10, 7, 7), onReorder: (oldIndex, newIndex) => setState(() { - pro.serverOrder.moveById( - filtered[oldIndex], - filtered[newIndex], + pro.serverOrder.moveByItem( + filtered, + oldIndex, + newIndex, property: _settingStore.serverOrder, ); }), - itemBuilder: (_, index) => _buildEachServerCard( - pro.servers[filtered[index]], - ), + itemBuilder: (_, index) => FadeIn( + key: ValueKey('$_tag${filtered[index]}'), + child: _buildEachServerCard( + pro.servers[filtered[index]], + )), itemCount: filtered.length, ); }, diff --git a/lib/view/page/snippet/edit.dart b/lib/view/page/snippet/edit.dart index f7b3099a..945375c4 100644 --- a/lib/view/page/snippet/edit.dart +++ b/lib/view/page/snippet/edit.dart @@ -120,7 +120,9 @@ class _SnippetEditPageState extends State }), s: _s, tagSuggestions: [..._provider.tags], - onRenameTag: _provider.renameTag, + onRenameTag: (old, n) => setState(() { + _provider.renameTag(old, n); + }), ) ], ); diff --git a/lib/view/page/snippet/list.dart b/lib/view/page/snippet/list.dart index b42b1a4c..ede4d2ca 100644 --- a/lib/view/page/snippet/list.dart +++ b/lib/view/page/snippet/list.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import 'package:toolbox/core/extension/order.dart'; import 'package:toolbox/data/model/server/server.dart'; import 'package:toolbox/data/provider/server.dart'; +import 'package:toolbox/data/res/ui.dart'; import 'package:toolbox/view/widget/tag/switcher.dart'; import '../../../core/utils/misc.dart'; @@ -71,9 +72,10 @@ class _SnippetListPageState extends State { padding: const EdgeInsets.all(13), itemCount: filtered.length, onReorder: (oldIdx, newIdx) => setState(() { - provider.snippets.moveById( - filtered[oldIdx], - filtered[newIdx], + provider.snippets.moveByItem( + filtered, + oldIdx, + newIdx, onMove: (p0) { _settingStore.snippetOrder.put(p0.map((e) => e.name).toList()); }, @@ -87,7 +89,7 @@ class _SnippetListPageState extends State { width: _media.size.width, ), itemBuilder: (context, idx) { - final snippet = filtered[idx]; + final snippet = filtered.elementAt(idx); return RoundRectCard( ListTile( contentPadding: const EdgeInsets.only(left: 23, right: 17), @@ -96,6 +98,12 @@ class _SnippetListPageState extends State { overflow: TextOverflow.ellipsis, maxLines: 1, ), + subtitle: Text( + snippet.script, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: grey, + ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/view/page/ssh/virt_key_setting.dart b/lib/view/page/ssh/virt_key_setting.dart index f6b91bdd..98bf2242 100644 --- a/lib/view/page/ssh/virt_key_setting.dart +++ b/lib/view/page/ssh/virt_key_setting.dart @@ -58,7 +58,7 @@ class _SSHVirtKeySettingPageState extends State { showSnackBar(context, Text(_s.disabled)); return; } - keys.moveById(keys[o], keys[n], property: _setting.sshVirtKeys); + keys.moveByItem(keys, o, n, property: _setting.sshVirtKeys); setState(() {}); }, ); diff --git a/lib/view/widget/fade_in.dart b/lib/view/widget/fade_in.dart index 88e43bb3..832f19c1 100644 --- a/lib/view/widget/fade_in.dart +++ b/lib/view/widget/fade_in.dart @@ -8,7 +8,7 @@ class FadeIn extends StatefulWidget { const FadeIn({ Key? key, required this.child, - this.duration = const Duration(milliseconds: 377), + this.duration = const Duration(milliseconds: 477), }) : super(key: key); @override diff --git a/lib/view/widget/tag/switcher.dart b/lib/view/widget/tag/switcher.dart index 9378ed2a..ded962ff 100644 --- a/lib/view/widget/tag/switcher.dart +++ b/lib/view/widget/tag/switcher.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:nil/nil.dart'; +import 'package:toolbox/view/widget/fade_in.dart'; import 'package:toolbox/view/widget/tag/view.dart'; class TagSwitcher extends StatelessWidget { @@ -20,10 +21,6 @@ class TagSwitcher extends StatelessWidget { @override Widget build(BuildContext context) { - return _buildTagsSwitcher(tags); - } - - Widget _buildTagsSwitcher(List tags) { if (tags.isEmpty) return nil; final items = [null, ...tags]; return Container( @@ -35,11 +32,14 @@ class TagSwitcher extends StatelessWidget { scrollDirection: Axis.horizontal, itemBuilder: (context, index) { final item = items[index]; - return TagView( - tag: item, - initTag: initTag, - all: all, - onTap: onTagChanged, + return FadeIn( + key: ValueKey(initTag), + child: TagView( + tag: item, + initTag: initTag, + all: all, + onTap: onTagChanged, + ), ); }, itemCount: items.length,