Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"test": "jest"
},
"dependencies": {
"@milkdown/kit": "^7.16.0",
"@milkdown/react": "^7.16.0",
"@milkdown/utils": "^7.16.0",
"@prisma/client": "5.22.0",
"html-to-slack": "0.3.5",
"jest": "^29.7.0",
Expand Down
1,961 changes: 1,959 additions & 2 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/ContentPane/ContentPane.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
padding: 1rem;
border-radius: 0.5rem;
border: 2px solid var(--content-border-color);
overflow: hidden;
overflow: clip;
}
92 changes: 28 additions & 64 deletions src/components/DivisionPageForm/DivisionPageForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,14 @@ import TextArea from '@/components/TextArea/TextArea';
import { useRef, useState } from 'react';
import DropdownList from '../DropdownList/DropdownList';
import styles from '../NewsPostForm/NewsPostForm.module.scss';
import Popup from 'reactjs-popup';
import DivisionPageService, {
DivisionPage
} from '@/services/divisionPageService';
import { create, edit } from '@/actions/divisionPages';
import { toast } from 'react-toastify';
import i18nService from '@/services/i18nService';
import MarkdownView from '../MarkdownView/MarkdownView';
import FileService, { MediaType } from '@/services/fileService';
import ContentPane from '../ContentPane/ContentPane';

const PreviewContentStyle = {
backgroundColor: '#000000AA'
};
const validUploadTypes = Object.values(MediaType);

interface DivisionPostFormProps {
Expand All @@ -43,13 +37,10 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
const [page, setPage] = useState(divisionPost.parentId);
const [titleEn, setTitleEn] = useState(divisionPost.titleEn ?? '');
const [titleSv, setTitleSv] = useState(divisionPost.titleSv ?? '');
const [contentEn, setContentEn] = useState(divisionPost.contentEn ?? '');
const [contentSv, setContentSv] = useState(divisionPost.contentSv ?? '');
const contentEnRef = useRef<{ getMarkdown: () => string }>(null);
const contentSvRef = useRef<{ getMarkdown: () => string }>(null);
const [slug, setSlug] = useState(divisionPost.slug ?? '');
const [showPreview, setShowPreview] = useState(false);
const [prio, setPrio] = useState(divisionPost.priority ?? 0);
const [previewContentSv, setPreviewContentSv] = useState('');
const [previewContentEn, setPreviewContentEn] = useState('');

const fileInputRef = useRef<HTMLInputElement>(null);
const [uploadQueue, setUploadQueue] = useState<{
Expand All @@ -58,14 +49,20 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {

const dropFiles = async (f: FileList) => {
const newQueue = { ...uploadQueue };
const droppedFiles: { [key: string]: File } = {};
for (let i = 0; i < f.length; i++) {
const file = f[i];
if (!FileService.checkValidFile(file, validUploadTypes)) continue;

const sha256 = await FileService.fileSha256Browser(file);
newQueue[sha256] = file;
droppedFiles[sha256] = file;
}
setUploadQueue(newQueue);
return Object.entries(droppedFiles).map(([sha256, file]) => ({
file,
url: '/api/media/' + sha256
}));
};

const delFile = (sha256: string) => {
Expand All @@ -74,18 +71,14 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
setUploadQueue(newQueue);
};

const copyFile = (sha256: string) => {
navigator.clipboard.writeText('[Text](/api/media/' + sha256 + ')');
const copyFile = (sha256: string, file: File) => {
const embed = FileService.isMimeEmbeddable(file.type);
navigator.clipboard.writeText(
(embed ? '!' : '') + '[Text](/api/media/' + sha256 + ')'
);
toast(l.editor.linkCopied, { type: 'success' });
};

function preview() {
setPreviewContentSv(FileService.replaceLocalFiles(contentSv, uploadQueue));
setPreviewContentEn(FileService.replaceLocalFiles(contentEn, uploadQueue));

setShowPreview(true);
}

async function apply() {
const formData = new FormData();
for (const file of Object.values(uploadQueue)) {
Expand All @@ -98,8 +91,8 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
divisionPost.editedId,
titleEn,
titleSv,
contentEn,
contentSv,
contentEnRef.current!.getMarkdown(),
contentSvRef.current!.getMarkdown(),
slug,
prio,
formData,
Expand All @@ -120,8 +113,8 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
create(
titleEn,
titleSv,
contentEn,
contentSv,
contentEnRef.current!.getMarkdown(),
contentSvRef.current!.getMarkdown(),
slug,
prio,
formData,
Expand Down Expand Up @@ -187,24 +180,22 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
<TextArea value={titleEn} onChange={(e) => setTitleEn(e.target.value)} />
<h2>{l.pages.content} (Eng)</h2>
<MarkdownEditor
onDragOver={(e) => {
e.preventDefault();
}}
onDrop={(e) => dropFiles(e.dataTransfer.files)}
value={contentEn}
onChange={(e) => setContentEn(e.target.value)}
defaultMd={divisionPost.contentEn}
ref={contentEnRef}
onUpload={dropFiles}
locale={divisionPost.locale}
localFiles={uploadQueue}
/>

<h2>{l.pages.title} (Sv)</h2>
<TextArea value={titleSv} onChange={(e) => setTitleSv(e.target.value)} />
<h2>{l.pages.content} (Sv)</h2>
<MarkdownEditor
onDragOver={(e) => {
e.preventDefault();
}}
onDrop={(e) => dropFiles(e.dataTransfer.files)}
value={contentSv}
onChange={(e) => setContentSv(e.target.value)}
defaultMd={divisionPost.contentSv}
ref={contentSvRef}
onUpload={dropFiles}
locale={divisionPost.locale}
localFiles={uploadQueue}
/>

<br />
Expand All @@ -214,11 +205,7 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
{Object.entries(uploadQueue).map(([sha256, file]) => (
<li className={styles.fileActions} key={sha256}>
<p>{file.name}</p>{' '}
<ActionButton
onClick={() => {
copyFile(sha256);
}}
>
<ActionButton type="button" onClick={() => copyFile(sha256, file)}>
{l.editor.copyLink}
</ActionButton>{' '}
<ActionButton
Expand Down Expand Up @@ -250,30 +237,7 @@ const DivisionPageForm = (divisionPost: DivisionPostFormProps) => {
? l.general.save
: l.general.create}
</ActionButton>
<ActionButton onClick={preview}>Förhandsgranska</ActionButton>
</div>

<Popup
modal
className={styles.dialog}
open={showPreview}
onClose={() => setShowPreview(false)}
overlayStyle={PreviewContentStyle}
>
<ContentPane className={styles.dialog}>
<h1>{l.pages.preview}</h1>
<Divider />
<h2>{titleEn}</h2>
<MarkdownView content={previewContentEn} allowBlob />
<Divider />
<h2>{titleSv}</h2>
<MarkdownView content={previewContentSv} allowBlob />

<ActionButton onClick={() => setShowPreview(false)}>
{l.pages.close}
</ActionButton>
</ContentPane>
</Popup>
</>
);
};
Expand Down
Loading