Compare commits

..

No commits in common. "1b88677e91ab2116e81945dc979f563e88c1da0b" and "9aa85d1d2aa979759d3a96295c02572748f40c27" have entirely different histories.

9 changed files with 44 additions and 122 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -3,7 +3,7 @@ import type { CommentAction } from "./schema";
type ActionAttachmentType = "empty" | "image" | "voice"; type ActionAttachmentType = "empty" | "image" | "voice";
const imageExtensions = ["jpeg", "jpg", "png"]; const imageExtensions = ["jpeg", "jpg", "png"];
const voiceExtensions = ["mp3", "ogg"]; const voiceExtensions = ["mp3"];
export function getActionAttachmentType( export function getActionAttachmentType(
action: CommentAction, action: CommentAction,

View File

@ -10,10 +10,13 @@ import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent"; import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle"; import DialogTitle from "@mui/material/DialogTitle";
import { TextField } from "@mui/material"; import { TextField } from "@mui/material";
import { IconButton, Button } from "@mui/material"; import { Button } from "@mui/material";
import AddIcon from "@mui/icons-material/Add";
export default function AddChainButton() { interface AddChainButtonProps {
children?: React.ReactNode;
}
export default function AddChainButton({ children }: AddChainButtonProps) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isError, setIsError] = useState(false); const [isError, setIsError] = useState(false);
@ -47,14 +50,13 @@ export default function AddChainButton() {
return ( return (
<> <>
<IconButton <Button
size="medium"
color="info"
sx={{ borderRadius: "4px" }}
onClick={() => setIsOpen(true)} onClick={() => setIsOpen(true)}
variant="outlined"
sx={{ width: "100%", fontWeight: "semibold" }}
> >
<AddIcon /> {children}
</IconButton> </Button>
<Dialog <Dialog
open={isOpen} open={isOpen}

View File

@ -50,7 +50,7 @@ export default function ChangeWaitForButton({
cleanWaitFor = waitFor; cleanWaitFor = waitFor;
} }
action.waitFor = cleanWaitFor * 60; action.waitFor = cleanWaitFor > 10 ? 10 * 60 : cleanWaitFor * 60;
updateAction(chainId, actionIndex, action); updateAction(chainId, actionIndex, action);
const chain = getChain(chainId)!; const chain = getChain(chainId)!;

View File

@ -34,7 +34,7 @@ export default function LinkItem({
> >
<span <span
className={cn( className={cn(
"mr-2 max-h-2 min-h-2 min-w-2 max-w-2 rounded-full bg-neutral", "mr-2 h-2 w-2 rounded-full bg-neutral",
isSelected && "bg-white", isSelected && "bg-white",
)} )}
/> />

View File

@ -11,15 +11,14 @@ export default function Root({ children, className }: RootProps) {
return ( return (
<motion.ul layout className={cn("", className)}> <motion.ul layout className={cn("", className)}>
<AnimatePresence initial={false}> <AnimatePresence initial={false}>
{Children.map(children, (child) => ( {Children.map(children, (child, index) => (
<motion.li <motion.li
className="block"
// @ts-expect-error lazy... sry
key={child.key}
layout layout
initial={{ opacity: 0 }} className="block"
animate={{ opacity: 1 }} key={index}
exit={{ opacity: 0 }} initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
> >
{child} {child}
</motion.li> </motion.li>

View File

@ -147,14 +147,6 @@ export default function ActionCard({
className={`w-full select-none ${!action.text && "rounded-b-md"} rounded-t-md`} className={`w-full select-none ${!action.text && "rounded-b-md"} rounded-t-md`}
/> />
)} )}
{attachmentType === "voice" && (
<div className="flex justify-center p-3">
<audio controls>
<source src={action.fileUrls[0]} type="audio/mp3" />
<source src={action.fileUrls[0]} type="audio/ogg" />
</audio>
</div>
)}
{action.text && <div className="p-4">{action.text}</div>} {action.text && <div className="p-4">{action.text}</div>}
</div> </div>
); );

View File

@ -11,8 +11,7 @@ import CircularProgress from "@mui/material/CircularProgress";
import TextFieldsIcon from "@mui/icons-material/TextFields"; import TextFieldsIcon from "@mui/icons-material/TextFields";
import ImageIcon from "@mui/icons-material/Image"; import ImageIcon from "@mui/icons-material/Image";
import RecordVoiceOverIcon from "@mui/icons-material/RecordVoiceOver"; // import RecordVoiceOverIcon from "@mui/icons-material/RecordVoiceOver";
import MicNoneIcon from "@mui/icons-material/MicNone";
interface TabPanelProps { interface TabPanelProps {
children?: React.ReactNode; children?: React.ReactNode;
@ -60,8 +59,6 @@ export default function ActionEditor({
canExit = false, canExit = false,
}: ActionEditorProps) { }: ActionEditorProps) {
const imageRef = useRef<HTMLInputElement>(null); const imageRef = useRef<HTMLInputElement>(null);
const voiceRef = useRef<HTMLInputElement>(null);
const attachmentType = initialAction const attachmentType = initialAction
? getActionAttachmentType(initialAction) ? getActionAttachmentType(initialAction)
: "empty"; : "empty";
@ -78,12 +75,6 @@ export default function ActionEditor({
: null, : null,
); );
const [voiceUrl, setVoiceUrl] = useState<string | null>(
initialAction && attachmentType === "voice"
? initialAction.fileUrls[0]!
: null,
);
const [action, setAction] = useState<CommentAction>( const [action, setAction] = useState<CommentAction>(
initialAction ?? { initialAction ?? {
actionType: "comment", actionType: "comment",
@ -115,8 +106,6 @@ export default function ActionEditor({
if (origin === "image") { if (origin === "image") {
setImageUrl(fileUrl); setImageUrl(fileUrl);
} else if (origin === "voice") {
setVoiceUrl(fileUrl);
} }
setLoading(false); setLoading(false);
@ -148,16 +137,8 @@ export default function ActionEditor({
fileUrls: [imageUrl], fileUrls: [imageUrl],
}); });
} else if (value === 2) { } else if (value === 2) {
if (!voiceUrl) { alert("Unsupported");
alert("Загрузите голосовое :3"); return;
return;
}
onSave(actionIndex, {
actionType: action.actionType,
text: null,
fileUrls: [voiceUrl],
});
} }
} }
@ -173,11 +154,11 @@ export default function ActionEditor({
> >
<Tab label="Текст" icon={<TextFieldsIcon />} {...a11yProps(0)} /> <Tab label="Текст" icon={<TextFieldsIcon />} {...a11yProps(0)} />
<Tab label="Изображение" icon={<ImageIcon />} {...a11yProps(1)} /> <Tab label="Изображение" icon={<ImageIcon />} {...a11yProps(1)} />
<Tab {/* <Tab
label="Голосовое" label="Голосовое"
icon={<RecordVoiceOverIcon />} icon={<RecordVoiceOverIcon />}
{...a11yProps(2)} {...a11yProps(2)}
/> /> */}
</Tabs> </Tabs>
</Box> </Box>
@ -231,7 +212,6 @@ export default function ActionEditor({
<input <input
ref={imageRef} ref={imageRef}
accept="image/*"
type="file" type="file"
className="mb-4 hidden select-none" className="mb-4 hidden select-none"
onChange={async (e) => await handleFileChange(e, "image")} onChange={async (e) => await handleFileChange(e, "image")}
@ -253,38 +233,9 @@ export default function ActionEditor({
/> />
</TabPanel> </TabPanel>
<TabPanel value={value} index={2}> {/* <TabPanel value={value} index={2}>
<div className="mt-5 text-center"> In progress
<input </TabPanel> */}
ref={voiceRef}
type="file"
accept=".mp3, .ogg"
className="mb-4 hidden select-none"
onChange={async (e) => await handleFileChange(e, "voice")}
/>
<Button
startIcon={<MicNoneIcon />}
variant="contained"
color="info"
disabled={loading}
onClick={() => {
if (!voiceRef.current) return;
voiceRef.current.click();
}}
>
{voiceUrl ? "Изменить голосовое" : "Загрузить голосовое"}
</Button>
{voiceUrl && (
<div className="mt-10 flex justify-center">
<audio controls>
<source src={voiceUrl} type="audio/mp3" />
<source src={voiceUrl} type="audio/ogg" />
</audio>
</div>
)}
</div>
</TabPanel>
</Box> </Box>
</div> </div>

View File

@ -1,56 +1,34 @@
import { useMemo } from "react";
import { useState } from "react";
import { useParams } from "@tanstack/react-router"; import { useParams } from "@tanstack/react-router";
import { useChainState } from "@/entities/chain/model"; import { useChainState } from "@/entities/chain/model";
import List from "@/shared/ui/List"; import List from "@/shared/ui/List";
import { TextField } from "@mui/material";
import { AddChainButton } from "@/features/add-chain"; import { AddChainButton } from "@/features/add-chain";
export default function Sidebar() { export default function Sidebar() {
const params = useParams({ strict: false }); const params = useParams({ strict: false });
const chains = useChainState((state) => state.chains || []); const chains = useChainState((state) => state.chains || []);
const [query, setQuery] = useState("");
const filteredChains = useMemo(
() =>
chains.filter((value) =>
value.name?.toLowerCase().includes(query.toLocaleLowerCase()),
),
[query, chains],
);
// @ts-expect-error because i don't know why // @ts-expect-error because i don't know why
const chainId: string | undefined = params.chainId; const chainId: string | undefined = params.chainId;
return ( return (
<div className="no-scrollbar relative flex h-screen w-[450px] flex-col bg-bg-color px-4 pb-2"> <div className="no-scrollbar h-screen w-[300px] overflow-y-scroll bg-bg-color px-4 pb-8">
<div className="mt-3 flex w-full items-start gap-x-3 pb-3"> <List.Root className="mt-8 space-y-2">
<TextField {chains.map((chain, index) => (
fullWidth <List.LinkItem
placeholder="Искать по названию" key={index}
color="primary" to={`/$namespace/${chain._id}`}
sx={{ backgroundColor: "#23253B", input: { color: "white" } }} isSelected={chainId == chain._id}
variant="outlined" >
size="small" {chain.name}
value={query} </List.LinkItem>
onChange={(e) => setQuery(e.target.value)} ))}
/> </List.Root>
<AddChainButton />
</div>
<div className="no-scrollbar grow overflow-y-scroll"> <div className="mt-4">
<List.Root className="space-y-2"> <AddChainButton>
{filteredChains.map((chain) => ( <span className="mr-2 text-2xl font-light">+</span>Добавить
<List.LinkItem </AddChainButton>
key={chain._id}
to={`/$namespace/${chain._id}`}
isSelected={chainId == chain._id}
>
{chain.name}
</List.LinkItem>
))}
</List.Root>
</div> </div>
</div> </div>
); );