This commit is contained in:
lollipopkit
2023-07-08 14:18:15 +08:00
parent 0bc176b603
commit 291c737a40
10 changed files with 116 additions and 42 deletions

View File

@@ -31,16 +31,54 @@ extension OrderX<T> on Order<T> {
return indexOf(id); return indexOf(id);
} }
void moveById( void moveByItem(
T oid, List<T> items,
T nid, { int o,
int n, {
StoreProperty<List<T>>? property, StoreProperty<List<T>>? property,
_OnMove<T>? onMove, _OnMove<T>? onMove,
}) { }) {
final index = indexOf(oid); if (o == n) return;
if (o < n) {
n -= 1;
}
final index = indexOf(items[o]);
if (index == -1) return; if (index == -1) return;
final newIndex = indexOf(nid); var newIndex = indexOf(items[n]);
if (newIndex == -1) return; if (newIndex == -1) return;
if (o < n) {
newIndex += 1;
}
move(index, newIndex, property: property, onMove: onMove); move(index, newIndex, property: property, onMove: onMove);
} }
/// order: ['d', 'b', 'e']\
/// items: ['a', 'b', 'c', 'd']\
/// result: ['b', 'd', 'a', 'c']\
/// return: ['e']
List<String> reorder({
required List<String> order,
required bool Function(T, String) finder,
}) {
final newOrder = <T>[];
final missed = <T>[];
final surplus = <String>[];
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;
}
} }

View File

@@ -39,15 +39,15 @@ class ServerProvider extends BusyProvider {
Future<void> loadLocalData() async { Future<void> loadLocalData() async {
setBusyState(true); setBusyState(true);
final infos = _serverStore.fetch(); final spis = _serverStore.fetch();
for (final info in infos) { for (final spi in spis) {
_servers[info.id] = genServer(info); _servers[spi.id] = genServer(spi);
} }
final serverOrder_ = _settingStore.serverOrder.fetch(); final serverOrder_ = _settingStore.serverOrder.fetch();
if (serverOrder_ != null) { if (serverOrder_ != null) {
_serverOrder.addAll(serverOrder_.toSet()); _serverOrder.addAll(serverOrder_.toSet());
if (_serverOrder.length != infos.length) { if (_serverOrder.length != spis.length) {
final missed = infos final missed = spis
.where( .where(
(e) => !_serverOrder.contains(e.id), (e) => !_serverOrder.contains(e.id),
) )

View File

@@ -6,6 +6,7 @@ import 'package:toolbox/data/store/snippet.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
import '../../core/extension/order.dart'; import '../../core/extension/order.dart';
import '../store/setting.dart';
class SnippetProvider extends BusyProvider { class SnippetProvider extends BusyProvider {
late Order<Snippet> _snippets; late Order<Snippet> _snippets;
@@ -15,9 +16,19 @@ class SnippetProvider extends BusyProvider {
List<String> get tags => _tags; List<String> get tags => _tags;
final _store = locator<SnippetStore>(); final _store = locator<SnippetStore>();
final _setting = locator<SettingStore>();
void loadData() { void loadData() {
_snippets = _store.fetch(); _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(); _updateTags();
} }
@@ -36,15 +47,15 @@ class SnippetProvider extends BusyProvider {
void add(Snippet snippet) { void add(Snippet snippet) {
_snippets.add(snippet); _snippets.add(snippet);
_store.put(snippet); _store.put(snippet);
notifyListeners();
_updateTags(); _updateTags();
notifyListeners();
} }
void del(Snippet snippet) { void del(Snippet snippet) {
_snippets.remove(snippet); _snippets.remove(snippet);
_store.delete(snippet); _store.delete(snippet);
notifyListeners();
_updateTags(); _updateTags();
notifyListeners();
} }
void update(Snippet old, Snippet newOne) { void update(Snippet old, Snippet newOne) {
@@ -52,8 +63,8 @@ class SnippetProvider extends BusyProvider {
_store.put(newOne); _store.put(newOne);
_snippets.remove(old); _snippets.remove(old);
_snippets.add(newOne); _snippets.add(newOne);
notifyListeners();
_updateTags(); _updateTags();
notifyListeners();
} }
void renameTag(String old, String newOne) { void renameTag(String old, String newOne) {
@@ -61,13 +72,11 @@ class SnippetProvider extends BusyProvider {
if (s.tags?.contains(old) ?? false) { if (s.tags?.contains(old) ?? false) {
s.tags?.remove(old); s.tags?.remove(old);
s.tags?.add(newOne); s.tags?.add(newOne);
_store.put(s);
} }
} }
for (final s in _snippets) {
_store.put(s);
}
notifyListeners();
_updateTags(); _updateTags();
notifyListeners();
} }
String get export => json.encode(snippets); String get export => json.encode(snippets);

View File

@@ -47,6 +47,8 @@ class _HomePageState extends State<HomePage>
final _selectIndex = ValueNotifier(0); final _selectIndex = ValueNotifier(0);
late S _s; late S _s;
bool _switchingPage = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -119,12 +121,18 @@ class _HomePageState extends State<HomePage>
], ],
), ),
body: PageView.builder( body: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
controller: _pageController, controller: _pageController,
itemBuilder: (_, index) => AppTab.values[index].page, 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<HomePage>
selectedIndex: _selectIndex.value, selectedIndex: _selectIndex.value,
animationDuration: const Duration(milliseconds: 250), animationDuration: const Duration(milliseconds: 250),
onDestinationSelected: (int index) { onDestinationSelected: (int index) {
if (_selectIndex.value == index) return;
_selectIndex.value = index; _selectIndex.value = index;
_switchingPage = true;
_pageController.animateToPage( _pageController.animateToPage(
index, index,
duration: const Duration(milliseconds: 677), duration: const Duration(milliseconds: 677),
curve: Curves.fastLinearToSlowEaseIn, curve: Curves.fastLinearToSlowEaseIn,
); );
Future.delayed(const Duration(milliseconds: 677), () {
_switchingPage = false;
});
}, },
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
destinations: [ destinations: [

View File

@@ -10,6 +10,7 @@ import 'package:toolbox/core/utils/misc.dart';
import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/model/server/snippet.dart';
import 'package:toolbox/data/provider/snippet.dart'; import 'package:toolbox/data/provider/snippet.dart';
import 'package:toolbox/view/page/process.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/picker.dart';
import 'package:toolbox/view/widget/tag/switcher.dart'; import 'package:toolbox/view/widget/tag/switcher.dart';
@@ -117,15 +118,18 @@ class _ServerPageState extends State<ServerPage>
), ),
padding: const EdgeInsets.fromLTRB(7, 10, 7, 7), padding: const EdgeInsets.fromLTRB(7, 10, 7, 7),
onReorder: (oldIndex, newIndex) => setState(() { onReorder: (oldIndex, newIndex) => setState(() {
pro.serverOrder.moveById( pro.serverOrder.moveByItem(
filtered[oldIndex], filtered,
filtered[newIndex], oldIndex,
newIndex,
property: _settingStore.serverOrder, property: _settingStore.serverOrder,
); );
}), }),
itemBuilder: (_, index) => _buildEachServerCard( itemBuilder: (_, index) => FadeIn(
pro.servers[filtered[index]], key: ValueKey('$_tag${filtered[index]}'),
), child: _buildEachServerCard(
pro.servers[filtered[index]],
)),
itemCount: filtered.length, itemCount: filtered.length,
); );
}, },

View File

@@ -120,7 +120,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
}), }),
s: _s, s: _s,
tagSuggestions: [..._provider.tags], tagSuggestions: [..._provider.tags],
onRenameTag: _provider.renameTag, onRenameTag: (old, n) => setState(() {
_provider.renameTag(old, n);
}),
) )
], ],
); );

View File

@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/order.dart'; import 'package:toolbox/core/extension/order.dart';
import 'package:toolbox/data/model/server/server.dart'; import 'package:toolbox/data/model/server/server.dart';
import 'package:toolbox/data/provider/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 'package:toolbox/view/widget/tag/switcher.dart';
import '../../../core/utils/misc.dart'; import '../../../core/utils/misc.dart';
@@ -71,9 +72,10 @@ class _SnippetListPageState extends State<SnippetListPage> {
padding: const EdgeInsets.all(13), padding: const EdgeInsets.all(13),
itemCount: filtered.length, itemCount: filtered.length,
onReorder: (oldIdx, newIdx) => setState(() { onReorder: (oldIdx, newIdx) => setState(() {
provider.snippets.moveById( provider.snippets.moveByItem(
filtered[oldIdx], filtered,
filtered[newIdx], oldIdx,
newIdx,
onMove: (p0) { onMove: (p0) {
_settingStore.snippetOrder.put(p0.map((e) => e.name).toList()); _settingStore.snippetOrder.put(p0.map((e) => e.name).toList());
}, },
@@ -87,7 +89,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
width: _media.size.width, width: _media.size.width,
), ),
itemBuilder: (context, idx) { itemBuilder: (context, idx) {
final snippet = filtered[idx]; final snippet = filtered.elementAt(idx);
return RoundRectCard( return RoundRectCard(
ListTile( ListTile(
contentPadding: const EdgeInsets.only(left: 23, right: 17), contentPadding: const EdgeInsets.only(left: 23, right: 17),
@@ -96,6 +98,12 @@ class _SnippetListPageState extends State<SnippetListPage> {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
), ),
subtitle: Text(
snippet.script,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: grey,
),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [

View File

@@ -58,7 +58,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
showSnackBar(context, Text(_s.disabled)); showSnackBar(context, Text(_s.disabled));
return; return;
} }
keys.moveById(keys[o], keys[n], property: _setting.sshVirtKeys); keys.moveByItem(keys, o, n, property: _setting.sshVirtKeys);
setState(() {}); setState(() {});
}, },
); );

View File

@@ -8,7 +8,7 @@ class FadeIn extends StatefulWidget {
const FadeIn({ const FadeIn({
Key? key, Key? key,
required this.child, required this.child,
this.duration = const Duration(milliseconds: 377), this.duration = const Duration(milliseconds: 477),
}) : super(key: key); }) : super(key: key);
@override @override

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nil/nil.dart'; import 'package:nil/nil.dart';
import 'package:toolbox/view/widget/fade_in.dart';
import 'package:toolbox/view/widget/tag/view.dart'; import 'package:toolbox/view/widget/tag/view.dart';
class TagSwitcher extends StatelessWidget { class TagSwitcher extends StatelessWidget {
@@ -20,10 +21,6 @@ class TagSwitcher extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _buildTagsSwitcher(tags);
}
Widget _buildTagsSwitcher(List<String> tags) {
if (tags.isEmpty) return nil; if (tags.isEmpty) return nil;
final items = <String?>[null, ...tags]; final items = <String?>[null, ...tags];
return Container( return Container(
@@ -35,11 +32,14 @@ class TagSwitcher extends StatelessWidget {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = items[index]; final item = items[index];
return TagView( return FadeIn(
tag: item, key: ValueKey(initTag),
initTag: initTag, child: TagView(
all: all, tag: item,
onTap: onTagChanged, initTag: initTag,
all: all,
onTap: onTagChanged,
),
); );
}, },
itemCount: items.length, itemCount: items.length,