snow-editor

small markdown and org-mode editor
Log | Files | Refs | README

SharedViewPage.jsx (2818B)


      1 import { useEffect, useState } from 'react';
      2 import { useParams } from 'react-router-dom';
      3 import EditorLayout from '../components/EditorLayout.jsx';
      4 import StatusBadge from '../components/StatusBadge.jsx';
      5 import { ApiError, fetchViewDocument, friendlyErrorMessage } from '../lib/api.js';
      6 import { downloadDocument } from '../lib/download.js';
      7 import { STR } from '../lib/strings.js';
      8 import LinkErrorPage from './LinkErrorPage.jsx';
      9 
     10 export default function SharedViewPage() {
     11   const { token } = useParams();
     12   const [doc, setDoc] = useState(null);
     13   const [error, setError] = useState(null);
     14   const [loading, setLoading] = useState(true);
     15 
     16   useEffect(() => {
     17     let cancelled = false;
     18 
     19     (async () => {
     20       setLoading(true);
     21       setError(null);
     22       try {
     23         const data = await fetchViewDocument(token);
     24         if (!cancelled) setDoc(data);
     25       } catch (err) {
     26         if (!cancelled) setError(err);
     27       } finally {
     28         if (!cancelled) setLoading(false);
     29       }
     30     })();
     31 
     32     return () => {
     33       cancelled = true;
     34     };
     35   }, [token]);
     36 
     37   if (loading) {
     38     return (
     39       <div className="app">
     40         <p className="page-loading">{STR.LOADING_DOCUMENT}</p>
     41       </div>
     42     );
     43   }
     44 
     45   if (error instanceof ApiError) {
     46     if (error.status === 410) {
     47       return (
     48         <LinkErrorPage
     49           title={STR.LINK_EXPIRED_TITLE}
     50           message={STR.LINK_EXPIRED_VIEW}
     51         />
     52       );
     53     }
     54     if (error.status === 404) {
     55       return (
     56         <LinkErrorPage
     57           title={STR.DOCUMENT_NOT_FOUND_TITLE}
     58           message={friendlyErrorMessage(error)}
     59         />
     60       );
     61     }
     62   }
     63 
     64   if (error || !doc) {
     65     return (
     66       <LinkErrorPage
     67         title={STR.LOAD_ERROR_TITLE}
     68         message={friendlyErrorMessage(error)}
     69       />
     70     );
     71   }
     72 
     73   const saveLabel = doc.mode === 'org' ? STR.DOWNLOAD_ORG : STR.DOWNLOAD_MD;
     74 
     75   return (
     76     <div className="app">
     77       <header className="app-header">
     78         <div className="app-header-text">
     79           <div className="app-header-top">
     80             <h1 className="app-title">{doc.title}</h1>
     81             <StatusBadge variant="shared">{STR.BADGE_SHARED}</StatusBadge>
     82             <StatusBadge variant="readonly">{STR.BADGE_READONLY}</StatusBadge>
     83           </div>
     84           <p className="app-subtitle">{STR.SHARED_VIEW}</p>
     85         </div>
     86         <div className="toolbar">
     87           <button
     88             type="button"
     89             className="btn"
     90             onClick={() => downloadDocument(doc.content, doc.mode, doc.title)}
     91           >
     92             {saveLabel}
     93           </button>
     94         </div>
     95       </header>
     96 
     97       <EditorLayout mode={doc.mode} content={doc.content} previewOnly showEditor={false} />
     98 
     99       <footer className="app-footer">
    100         <p className="app-meta">{STR.FOOTER_SHARED}</p>
    101       </footer>
    102     </div>
    103   );
    104 }