Skip to content

Commit

Permalink
feat: draft a reorder for chains
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat616 committed Sep 22, 2024
1 parent b87dc18 commit 94f5a75
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 62 deletions.
20 changes: 19 additions & 1 deletion backend/tauri/src/config/profile/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::utils::{dirs, help};
use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize};
use serde_yaml::Mapping;
use std::{fs, io::Write};
use std::{borrow::Borrow, fs, io::Write};
use tracing_attributes::instrument;

/// Define the `profiles.yaml` schema
Expand Down Expand Up @@ -182,6 +182,24 @@ impl IProfiles {
self.save_file()
}

/// reorder items with the full order list
pub fn reorder_by_list<T: Borrow<String>>(&mut self, order: &[T]) -> Result<()> {
let mut items = self.items.take().unwrap_or_default();
let mut new_items = vec![];

for uid in order {
if let Some(index) = items
.iter()
.position(|e| e.uid.as_ref() == Some(uid.borrow()))
{
new_items.push(items.remove(index));
}
}

self.items = Some(new_items);
self.save_file()
}

/// update the item value
#[instrument]
pub fn patch_item(&mut self, uid: String, item: ProfileItem) -> Result<()> {
Expand Down
3 changes: 2 additions & 1 deletion backend/tauri/src/core/hotkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ impl Hotkey {
_ => bail!("invalid function \"{func}\""),
};

manager.on_shortcut(hotkey, move |_app_handle, _hotkey, ev| {
manager.on_shortcut(hotkey, move |_app_handle, hotkey, ev| {
if let ShortcutState::Pressed = ev.state {
tracing::info!("hotkey pressed: {}", hotkey);
f();
}
})?;
Expand Down
6 changes: 6 additions & 0 deletions backend/tauri/src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
wrap_err!(Config::profiles().data().reorder(active_id, over_id))
}

#[tauri::command]
pub fn reorder_profiles_by_list(list: Vec<String>) -> CmdResult {
wrap_err!(Config::profiles().data().reorder_by_list(&list))
}


#[tauri::command]
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
wrap_err!(feat::update_profile(index, option).await)
Expand Down
1 change: 1 addition & 0 deletions backend/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ pub fn run() -> std::io::Result<()> {
ipc::create_profile,
ipc::import_profile,
ipc::reorder_profile,
ipc::reorder_profiles_by_list,
ipc::update_profile,
ipc::delete_profile,
ipc::read_profile_file,
Expand Down
7 changes: 7 additions & 0 deletions frontend/interface/src/ipc/useClash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ export const useClash = () => {
refreshInterval: 1000,
});

const reorderProfilesByList = async (list: string[]) => {
await tauri.reorderProfilesByList(list);

await getProfiles.mutate();
};

return {
getClashInfo,
getConfigs,
Expand All @@ -110,5 +116,6 @@ export const useClash = () => {
getProfileFile,
getRuntimeLogs,
setProfileFile: tauri.saveProfileFile,
reorderProfilesByList,
};
};
4 changes: 4 additions & 0 deletions frontend/interface/src/service/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,7 @@ export const setStorageItem = async (key: string, value: string) => {
export const removeStorageItem = async (key: string) => {
return await invoke<void>("remove_storage_item", { key });
};

export const reorderProfilesByList = async (list: string[]) => {
return await invoke<void>("reorder_profiles_by_list", { list });
};
129 changes: 86 additions & 43 deletions frontend/nyanpasu/src/components/profiles/modules/chain-item.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import { memo, useState, useTransition } from "react";
import { useLongPress } from "ahooks";
import { Reorder, useDragControls } from "framer-motion";
import {
memo,
PointerEvent,
useCallback,
useRef,
useState,
useTransition,
} from "react";
import { useTranslation } from "react-i18next";
import { Menu as MenuIcon } from "@mui/icons-material";
import { LoadingButton } from "@mui/lab";
import { alpha, ListItemButton, Menu, MenuItem, useTheme } from "@mui/material";
import { Profile, useClash } from "@nyanpasu/interface";
import { cleanDeepClickEvent } from "@nyanpasu/ui";

const longPressDelay = 200;

export const ChainItem = memo(function ChainItem({
item,
selected,
Expand Down Expand Up @@ -43,53 +54,85 @@ export const ChainItem = memo(function ChainItem({
func();
};

// const controls = useDragControls();

const onLongPress = (e: PointerEvent) => {
cleanDeepClickEvent(e);
// controls.start(e);
};

const longPressTimerRef = useRef<number | null>(null);

return (
<>
<ListItemButton
className="!mb-2 !mt-2 !flex !justify-between gap-2"
sx={[
{
borderRadius: 4,
},
selected
? {
backgroundColor: alpha(palette.primary.main, 0.3),
}
: {
backgroundColor: alpha(palette.secondary.main, 0.1),
},
selected
? {
"&:hover": {
backgroundColor: alpha(palette.primary.main, 0.5),
<Reorder.Item
css={{
zIndex: 100,
}}
value={item.uid}
// dragListener={false}
// dragControls={controls}
onPointerDown={(e: PointerEvent) => {
longPressTimerRef.current = window.setTimeout(() => {
longPressTimerRef.current = null;
onLongPress(e as unknown as PointerEvent);
}, longPressDelay);
}}
onPointerUp={(e: PointerEvent) => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current!);
} else {
cleanDeepClickEvent(e);
longPressTimerRef.current = null;
}
}}
>
<ListItemButton
className="!mb-2 !mt-2 !flex !justify-between gap-2"
sx={[
{
borderRadius: 4,
},
selected
? {
backgroundColor: alpha(palette.primary.main, 0.3),
}
: {
backgroundColor: alpha(palette.secondary.main, 0.1),
},
}
: {
"&:hover": {
backgroundColor: null,
selected
? {
"&:hover": {
backgroundColor: alpha(palette.primary.main, 0.5),
},
}
: {
"&:hover": {
backgroundColor: null,
},
},
},
]}
onClick={handleClick}
disabled={isPending}
>
<div className="truncate py-1">
<span>{item.name}</span>
</div>

<LoadingButton
size="small"
color="primary"
className="!size-8 !min-w-0"
onClick={(e) => {
cleanDeepClickEvent(e);
setAnchorEl(e.currentTarget);
}}
loading={isPending}
]}
onClick={handleClick}
disabled={isPending}
>
<MenuIcon />
</LoadingButton>
</ListItemButton>
<div className="truncate py-1">
<span>{item.name}</span>
</div>

<LoadingButton
size="small"
color="primary"
className="!size-8 !min-w-0"
loading={isPending}
onClick={(e) => {
cleanDeepClickEvent(e);
setAnchorEl(e!.currentTarget as HTMLButtonElement);
}}
>
<MenuIcon />
</LoadingButton>
</ListItemButton>
</Reorder.Item>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
Expand Down
52 changes: 35 additions & 17 deletions frontend/nyanpasu/src/components/profiles/modules/side-chain.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useLockFn } from "ahooks";
import { Reorder } from "framer-motion";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
Expand All @@ -24,9 +25,10 @@ export const SideChain = ({ onChainEdit }: SideChainProps) => {

const currentProfileUid = useAtomValue(atomChainsSelected);

const { getProfiles, setProfilesConfig, setProfiles } = useClash();
const { getProfiles, setProfilesConfig, setProfiles, reorderProfilesByList } =
useClash();

const { scripts } = filterProfiles(getProfiles.data?.items);
const { scripts, profiles } = filterProfiles(getProfiles.data?.items);

const currentProfile = useMemo(() => {
return getProfiles.data?.items?.find(
Expand Down Expand Up @@ -60,23 +62,39 @@ export const SideChain = ({ onChainEdit }: SideChainProps) => {
}
});

const reorderValues = useMemo(
() => scripts?.map((item) => item.uid) || [],
[scripts],
);

return (
<div className="h-full overflow-auto !pl-2 !pr-2">
{scripts?.map((item, index) => {
const selected = isGlobalChainCurrent
? getProfiles.data?.chain?.includes(item.uid)
: currentProfile?.chains?.includes(item.uid);

return (
<ChainItem
key={index}
item={item}
selected={selected}
onClick={async () => await handleChainClick(item.uid)}
onChainEdit={() => onChainEdit(item)}
/>
);
})}
<Reorder.Group
axis="y"
values={reorderValues}
onReorder={(values) => {
const profileUids = profiles?.map((item) => item.uid) || [];
reorderProfilesByList([...profileUids, ...values]);
}}
layoutScroll
style={{ overflowY: "scroll" }}
>
{scripts?.map((item, index) => {
const selected = isGlobalChainCurrent
? getProfiles.data?.chain?.includes(item.uid)
: currentProfile?.chains?.includes(item.uid);

return (
<ChainItem
key={index}
item={item}
selected={selected}
onClick={async () => await handleChainClick(item.uid)}
onChainEdit={() => onChainEdit(item)}
/>
);
})}
</Reorder.Group>

<ListItemButton
className="!mb-2 !mt-2 flex justify-center gap-2"
Expand Down

0 comments on commit 94f5a75

Please sign in to comment.