Skip to content

Commit

Permalink
add game provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartaithan committed Nov 13, 2023
1 parent 83c407d commit a61e121
Show file tree
Hide file tree
Showing 10 changed files with 1,219 additions and 57 deletions.
72 changes: 72 additions & 0 deletions components/AddImageDialog/AddImageDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"use client";

import { type ModalProps } from "@/models/ModalModel";
import { centeredDialog } from "@/styles/theme";
import {
Button,
Text,
TextInput,
Dialog,
Group,
type DialogProps,
} from "@mantine/core";
import { type TransformedValues, matches, useForm } from "@mantine/form";
import { useRichTextEditorContext } from "@mantine/tiptap";
import { type FC, memo } from "react";

const AddImageDialog: FC<ModalProps> = (props) => {
const { opened, setOpened } = props;
const { editor } = useRichTextEditorContext();

const form = useForm({
initialValues: { image_url: "" },
validate: {
image_url: matches(
/^(http(s?):)([/|.|\w|\s|-])*\.(?:png|gif|webp|jpeg|jpg)$/,
"Enter a valid image url",
),
},
});

const handleClose = (): void => {
setOpened(false);
};

const submitImage = (values: TransformedValues<typeof form>): void => {
if (editor == null) return;
editor.chain().focus().setImage({ src: values.image_url }).run();
form.reset();
setOpened(false);
};

const handleSubmit = (): void => {
form.onSubmit(submitImage)();
};

return (
<Dialog
opened={opened}
withCloseButton
position={centeredDialog as DialogProps["position"]}
onClose={handleClose}
size="lg"
radius="md"
zIndex={9999}>
<Text size="sm" mb="xs" fw={500}>
Add image link
</Text>
<Group align="flex-end">
<TextInput
{...form.getInputProps("image_url")}
placeholder="https://example.com/image.png"
style={{ flex: 1 }}
/>
<Button onClick={handleSubmit} style={{ alignSelf: "flex-start" }}>
Add
</Button>
</Group>
</Dialog>
);
};

export default memo(AddImageDialog);
76 changes: 76 additions & 0 deletions components/AddYoutubeLinkDialog/AddYoutubeLinkDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import { type ModalProps } from "@/models/ModalModel";
import { centeredDialog } from "@/styles/theme";
import {
Button,
Text,
TextInput,
Dialog,
Group,
type DialogProps,
} from "@mantine/core";
import { type TransformedValues, matches, useForm } from "@mantine/form";
import { useRichTextEditorContext } from "@mantine/tiptap";
import { type FC, memo } from "react";

const isYoutubeLink =
/(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-_]+)/gim;

const AddYoutubeLinkDialog: FC<ModalProps> = (props) => {
const { opened, setOpened } = props;
const { editor } = useRichTextEditorContext();

const form = useForm({
initialValues: { url: "" },
validate: {
url: matches(isYoutubeLink, "Enter a valid youtube url"),
},
});

const handleClose = (): void => {
setOpened(false);
};

const submitLink = (values: TransformedValues<typeof form>): void => {
if (editor == null) return;
editor.commands.setYoutubeVideo({
src: values.url,
width: 640,
height: 480,
});
form.reset();
setOpened(false);
};

const handleSubmit = (): void => {
form.onSubmit(submitLink)();
};

return (
<Dialog
opened={opened}
withCloseButton
position={centeredDialog as DialogProps["position"]}
onClose={handleClose}
size="lg"
radius="md"
zIndex={9999}>
<Text size="sm" mb="xs" fw={500}>
Add youtube link
</Text>
<Group align="flex-end">
<TextInput
{...form.getInputProps("url")}
placeholder="https://example.com/image.png"
style={{ flex: 1 }}
/>
<Button onClick={handleSubmit} style={{ alignSelf: "flex-start" }}>
Add
</Button>
</Group>
</Dialog>
);
};

export default memo(AddYoutubeLinkDialog);
113 changes: 113 additions & 0 deletions components/Controls/Controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use client";

import { type FC } from "react";
import { RichTextEditor, useRichTextEditorContext } from "@mantine/tiptap";
import {
IconArrowBarBoth,
IconBrandYoutube,
IconCheckbox,
IconIndentDecrease,
IconIndentIncrease,
IconPhotoPlus,
} from "@tabler/icons-react";

interface ControlProps {
onClick: () => void;
}

export const ToggleTaskListControl: FC = () => {
const { editor } = useRichTextEditorContext();

const handleToggle = (): void => {
editor?.commands.toggleTaskList();
};

return (
<RichTextEditor.Control
onClick={handleToggle}
active={editor?.isActive("taskList")}
aria-label="Toggle task list"
title="Toggle task list">
<IconCheckbox stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};

export const SplitListItemControl: FC = () => {
const { editor } = useRichTextEditorContext();

const handleSplit = (): void => {
editor?.chain().focus().splitListItem("taskItem").run();
};

return (
<RichTextEditor.Control
onClick={handleSplit}
interactive={editor?.can().splitListItem("taskItem")}
disabled={!(editor?.can().splitListItem("taskItem") ?? false)}
aria-label="Split list item"
title="Split list item">
<IconArrowBarBoth stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};

export const SinkListItemControl: FC = () => {
const { editor } = useRichTextEditorContext();

const handleSink = (): void => {
editor?.chain().focus().sinkListItem("taskItem").run();
};

return (
<RichTextEditor.Control
onClick={handleSink}
interactive={editor?.can().sinkListItem("taskItem")}
disabled={!(editor?.can().sinkListItem("taskItem") ?? false)}
aria-label="Sink list item"
title="Sink list item">
<IconIndentIncrease stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};

export const LiftListItemControl: FC = () => {
const { editor } = useRichTextEditorContext();

const handleLift = (): void => {
editor?.chain().focus().liftListItem("taskItem").run();
};

return (
<RichTextEditor.Control
onClick={handleLift}
interactive={editor?.can().liftListItem("taskItem")}
disabled={!(editor?.can().liftListItem("taskItem") ?? false)}
aria-label="Lift list item"
title="Lift list item">
<IconIndentDecrease stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};

export const ImageControl: FC<ControlProps> = ({ onClick }) => {
return (
<RichTextEditor.Control
onClick={onClick}
aria-label="Add image"
title="Add image">
<IconPhotoPlus stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};

export const YoutubeControl: FC<ControlProps> = ({ onClick }) => {
return (
<RichTextEditor.Control
onClick={onClick}
aria-label="Add youtube video"
title="Add youtube video">
<IconBrandYoutube stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
};
22 changes: 22 additions & 0 deletions hooks/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useCallback, useRef } from "react";

const useDebounce = (
fn: (...args: any[]) => void,
delay: number,
): ((...args: any[]) => void) => {
const timer = useRef<NodeJS.Timeout | null>(null);

const debounceCallback = useCallback(
(...args: any[]) => {
if (timer.current != null) clearTimeout(timer.current);
timer.current = setTimeout(() => {
fn(...args);
}, delay);
},
[fn, delay],
);

return debounceCallback;
};

export default useDebounce;
Loading

1 comment on commit a61e121

@vercel
Copy link

@vercel vercel bot commented on a61e121 Nov 13, 2023

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:

trophy-hunt – ./

trophy-hunt-hartaithan.vercel.app
trophy-hunt-git-main-hartaithan.vercel.app
trophy-hunt.vercel.app

Please sign in to comment.