mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
#54 fix order
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final s in _snippets) {
|
|
||||||
_store.put(s);
|
_store.put(s);
|
||||||
}
|
}
|
||||||
notifyListeners();
|
}
|
||||||
_updateTags();
|
_updateTags();
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
String get export => json.encode(snippets);
|
String get export => json.encode(snippets);
|
||||||
|
|||||||
@@ -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: [
|
||||||
|
|||||||
@@ -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(
|
||||||
|
key: ValueKey('$_tag${filtered[index]}'),
|
||||||
|
child: _buildEachServerCard(
|
||||||
pro.servers[filtered[index]],
|
pro.servers[filtered[index]],
|
||||||
),
|
)),
|
||||||
itemCount: filtered.length,
|
itemCount: filtered.length,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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: [
|
||||||
|
|||||||
@@ -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(() {});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
key: ValueKey(initTag),
|
||||||
|
child: TagView(
|
||||||
tag: item,
|
tag: item,
|
||||||
initTag: initTag,
|
initTag: initTag,
|
||||||
all: all,
|
all: all,
|
||||||
onTap: onTagChanged,
|
onTap: onTagChanged,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: items.length,
|
itemCount: items.length,
|
||||||
|
|||||||
Reference in New Issue
Block a user