Skip to content

Commit

Permalink
모달과 관련된 수정사항들 (#9)
Browse files Browse the repository at this point in the history
* Fix z-index of alert box

* Move message to title

* Add Color type at tailwindcss

* Fix modal logic

Promise를 씌우는 대신 Promise.resolve 를 동작시키도록 modal의 비즈니스 로직을 변경했습니다.

* Rename `push` to `alert`

* Remove unused function `close`

* Split `Modal` component

* Change `id` from `action` of actions element
  • Loading branch information
nabi-chan authored Apr 20, 2022
1 parent 31cb779 commit 03d798e
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 120 deletions.
33 changes: 33 additions & 0 deletions components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Color } from "types/tailwindcss";

interface IModalProps {
callback(action: string): void;
title: string;
message: string;
actions: {
id: string;
color: Color;
content: string;
}[];
}

export function Modal({ title, message, actions, callback }: IModalProps) {
return (
<>
<input type="checkbox" className="modal-toggle" defaultChecked={true} />
<div className="modal modal-bottom sm:modal-middle">
<div className="modal-box">
<h3 className="font-bold text-lg">{title}</h3>
<p className="py-4 whitespace-pre-line keep-all">{message}</p>
<div className="modal-action">
{actions.map(({ id, color, content }) => (
<label key={id} onClick={() => callback(id)} className={`btn btn-${color}`}>
{content}
</label>
))}
</div>
</div>
</div>
</>
);
}
13 changes: 5 additions & 8 deletions contexts/AlertContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,20 @@ interface IAlertsWithId extends IAlert {

interface IAlertContext {
alerts: IAlertsWithId[];
push(alert: IAlert): void;
close(id: string): void;
alert(alert: IAlert): void;
}

const doNothing = () => null;

export const AlertContext = createContext<IAlertContext>({
alerts: [],
push: doNothing,
close: doNothing,
alert: doNothing,
});

const AlertContextProvider: FC = ({ children }) => {
const [alerts, setAlerts] = useState<IAlertsWithId[]>([]);

const push = (alert: IAlert) =>
const alert = (alert: IAlert) =>
setAlerts((prev) => [
...prev,
{ id: `${String(Date.now()) + alert.title + String(Math.random() * 100)}`, ...alert },
Expand All @@ -40,11 +38,10 @@ const AlertContextProvider: FC = ({ children }) => {
<AlertContext.Provider
value={{
alerts,
push,
close,
alert,
}}
>
<aside className={"fixed z-60 top-8 inset-x-4 container mx-auto flex flex-col gap-4"}>
<aside className={"fixed z-50 top-8 inset-x-4 container mx-auto flex flex-col gap-4"}>
<AnimatePresence>
{alerts.map(({ id, type, title, message }) => (
<Alert key={id} type={type} title={title} message={message} close={() => close(id)} />
Expand Down
6 changes: 3 additions & 3 deletions contexts/LoginContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const LoginContext = createContext<ILoginContext>({
});

const LoginContextProvider: FC = ({ children }) => {
const { push } = useAlertContext();
const { alert } = useAlertContext();
const [isLogin, setLogin] = useState<boolean>(false);
const [idError, setIdError] = useState<string>("");
const [passwordError, setPasswordError] = useState<string>("");
Expand Down Expand Up @@ -72,7 +72,7 @@ const LoginContextProvider: FC = ({ children }) => {
} catch (e) {
console.error(e);
if (e instanceof Error) {
push({
alert({
type: "error",
title: "로그인에 실패했습니다.",
message: "아이디 / 비밀번호가 일치하지 않습니다.",
Expand All @@ -82,7 +82,7 @@ const LoginContextProvider: FC = ({ children }) => {
}

const login = () => {
push({
alert({
type: "success",
title: "성공적으로 로그인되었습니다.",
});
Expand Down
122 changes: 51 additions & 71 deletions contexts/ModalContext.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
import { createContext, FC, Reducer, useReducer } from "react";
import { Color } from "types/tailwindcss";
import produce from "immer";
import { Modal } from "components/Modal";

interface IModalActions {
callback: () => void;
content: string;
color:
| "primary"
| "secondary"
| "accent"
| "info"
| "success"
| "warning"
| "error"
| "ghost"
| "link"
| "outline"
| "active"
| "disabled";
}

interface IModal {
export interface IModal {
id: string;
callback(action: string): void;
title: string;
message: string;
actions: IModalActions[];
actions: IActions[];
}

type IModalInput = Omit<IModal, "id" | "callback">;

interface IActions {
id: string;
color: Color;
content: string;
}

interface IModalContext {
modals: IModal[];
push(data: IModal): void;
close(id: string): void;
modal(input: IModalInput): Promise<string>;
}

const doNothing = async () => "";
export const ModalContext = createContext<IModalContext>({
modals: [],
modal: async () => "",
});

type Actions =
type Action =
| {
type: "push";
data: IModal;
type: "createModal";
input: IModal;
}
| {
type: "close";
type: "resolveModal";
id: string;
};

const reducer: Reducer<IModal[], Actions> = (draft, action) => {
const reducer: Reducer<IModal[], Action> = (draft, action) => {
switch (action.type) {
case "push":
draft.push(action.data);
case "createModal":
draft.push(action.input);
break;
case "close":
case "resolveModal":
const index = draft.findIndex((modal) => modal.id === action.id);
if (index !== -1) draft.splice(index, 1);
break;
Expand All @@ -58,61 +53,46 @@ const reducer: Reducer<IModal[], Actions> = (draft, action) => {
return draft;
};

export const ModalContext = createContext<IModalContext>({
modals: [],
push: doNothing,
close: doNothing,
});

const curriedReducer = produce(reducer);

const ModalContextProvider: FC = ({ children }) => {
const [modals, dispatch] = useReducer(curriedReducer, []);

const push = (modal: IModal) =>
dispatch({
type: "push",
data: modal,
});
const modal = (input: IModalInput) =>
new Promise<string>((resolve) => {
const id = String(Math.random() * 10 ** 10);
const callback = (action: string) => {
resolve(action);
dispatch({
type: "resolveModal",
id,
});
};

const close = (id: string) =>
dispatch({
type: "close",
id,
// create modal
dispatch({
type: "createModal",
input: {
...input,
id,
callback,
},
});
});

const closeWithAction = (id: string, callback: Function) => {
close(id);
callback?.();
};

return (
<ModalContext.Provider
value={{
modals,
push,
close,
modal,
}}
>
{children}
{modals.map((modal) => (
<div key={modal.id}>
<input type="checkbox" className="modal-toggle" defaultChecked={true} />
<div className="modal modal-bottom sm:modal-middle">
<div className="modal-box">
<h3 className="font-bold text-lg">{modal.title}</h3>
<p className="py-4">{modal.message}</p>
<div className="modal-action">
{modal.actions.map(({ color, content, callback }, i) => (
<label key={i} onClick={() => closeWithAction(modal.id, callback)} className={`btn btn-${color}`}>
{content}
</label>
))}
</div>
</div>
</div>
</div>
))}
<div id={"modal-container"}>
{modals.map((modal) => (
<Modal key={modal.id} {...modal} />
))}
</div>
</ModalContext.Provider>
);
};
Expand Down
6 changes: 3 additions & 3 deletions pages/refresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ValidationError } from "utils/error";
import { useAlertContext } from "hooks/useAlertContext";

const Home: NextPage = () => {
const { push } = useAlertContext();
const { alert } = useAlertContext();
const [uma_id, setUmaId] = useState<string>("");
const [umaError, setUmaError] = useState<string>("");

Expand All @@ -27,7 +27,7 @@ const Home: NextPage = () => {
if (e instanceof ValidationError) {
setUmaError(e.message);
} else if (e instanceof Error) {
push({
alert({
type: "error",
title: "Uma의 정보를 수정할 수 없었습니다.",
message: e.message,
Expand All @@ -45,7 +45,7 @@ const Home: NextPage = () => {
if (e instanceof ValidationError) {
setUmaError(e.message);
} else if (e instanceof Error) {
push({
alert({
type: "error",
title: "Card의 정보를 수정할 수 없었습니다.",
message: e.message,
Expand Down
60 changes: 25 additions & 35 deletions pages/upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const reducer: Reducer<IFilesMeta[], Actions> = (draft, action) => {
const curriedReducer = produce(reducer);

const Home: NextPage = () => {
const { push } = useAlertContext();
const { push: modal } = useModalContext();
const { alert } = useAlertContext();
const { modal } = useModalContext();

const [files, dispatch] = useReducer(curriedReducer, []);

Expand Down Expand Up @@ -111,36 +111,26 @@ const Home: NextPage = () => {
},
});

if (exists)
await new Promise((resolve) =>
modal({
id: "file_is_duplicated",
title: "파일이 중복되었어요!",
message: "그렇다는데요?",
actions: [
{
content: "덮어쓰기",
color: "primary",
callback() {
resolve("");
},
},
{
content: "취소",
color: "ghost",
callback() {
push({
type: "error",
title: `${file.name} 업로드가 실패했습니다.`,
message: "사용자가 업로드를 취소했습니다.",
});
changeProgress(index, 100);
changeStatus(index, UploadStatus.ERROR);
},
},
],
}),
);
if (exists) {
const action = await modal({
title: "파일이 중복되었어요!",
message: `${file.name} 이랑 같은 이름을 가진 파일이 서버에 올라가있어요.\n이 파일을 덮어씌우시겠어요?`,
actions: [
{
id: "overwrite",
content: "덮어쓰기",
color: "primary",
},
{
id: "cancel",
content: "취소",
color: "ghost",
},
],
});

if (action === "cancel") throw new Error("사용자가 업로드를 취소했어요.");
}

const {
data: { endpoint },
Expand All @@ -157,16 +147,16 @@ const Home: NextPage = () => {
},
});
// notification
push({
alert({
type: "success",
message: `${file.name} 업로드가 성공했습니다.`,
title: `${file.name} 업로드가 성공했습니다.`,
});
changeStatus(index, UploadStatus.SUCCESS);
} catch (e) {
console.error(e);
if (e instanceof Error) {
// notification
push({
alert({
type: "error",
title: `${file.name} 업로드가 실패했습니다.`,
message: e.message,
Expand Down
13 changes: 13 additions & 0 deletions types/tailwindcss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type Color =
| "primary"
| "secondary"
| "accent"
| "info"
| "success"
| "warning"
| "error"
| "ghost"
| "link"
| "outline"
| "active"
| "disabled";

1 comment on commit 03d798e

@vercel
Copy link

@vercel vercel bot commented on 03d798e Apr 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

umasupport-carrot – ./

umasupport-carrot-nabichan.vercel.app
umasupport-carrot-git-main-nabichan.vercel.app
umasupport-carrot.vercel.app

Please sign in to comment.