all repos — snow-editor @ 65993a713586363acceab129af67161a5fbe71cc

small and cozy markdown, and orgmode editor

src/pages/SharedViewPage.jsx (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import EditorLayout from '../components/EditorLayout.jsx';
import StatusBadge from '../components/StatusBadge.jsx';
import { ApiError, fetchViewDocument, friendlyErrorMessage } from '../lib/api.js';
import { downloadDocument } from '../lib/download.js';
import { STR } from '../lib/strings.js';
import LinkErrorPage from './LinkErrorPage.jsx';

export default function SharedViewPage() {
  const { token } = useParams();
  const [doc, setDoc] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;

    (async () => {
      setLoading(true);
      setError(null);
      try {
        const data = await fetchViewDocument(token);
        if (!cancelled) setDoc(data);
      } catch (err) {
        if (!cancelled) setError(err);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [token]);

  if (loading) {
    return (
      <div className="app">
        <p className="page-loading">{STR.LOADING_DOCUMENT}</p>
      </div>
    );
  }

  if (error instanceof ApiError) {
    if (error.status === 410) {
      return (
        <LinkErrorPage
          title={STR.LINK_EXPIRED_TITLE}
          message={STR.LINK_EXPIRED_VIEW}
        />
      );
    }
    if (error.status === 404) {
      return (
        <LinkErrorPage
          title={STR.DOCUMENT_NOT_FOUND_TITLE}
          message={friendlyErrorMessage(error)}
        />
      );
    }
  }

  if (error || !doc) {
    return (
      <LinkErrorPage
        title={STR.LOAD_ERROR_TITLE}
        message={friendlyErrorMessage(error)}
      />
    );
  }

  const saveLabel = doc.mode === 'org' ? STR.DOWNLOAD_ORG : STR.DOWNLOAD_MD;

  return (
    <div className="app">
      <header className="app-header">
        <div className="app-header-text">
          <div className="app-header-top">
            <h1 className="app-title">{doc.title}</h1>
            <StatusBadge variant="shared">{STR.BADGE_SHARED}</StatusBadge>
            <StatusBadge variant="readonly">{STR.BADGE_READONLY}</StatusBadge>
          </div>
          <p className="app-subtitle">{STR.SHARED_VIEW}</p>
        </div>
        <div className="toolbar">
          <button
            type="button"
            className="btn"
            onClick={() => downloadDocument(doc.content, doc.mode, doc.title)}
          >
            {saveLabel}
          </button>
        </div>
      </header>

      <EditorLayout mode={doc.mode} content={doc.content} previewOnly showEditor={false} />

      <footer className="app-footer">
        <p className="app-meta">{STR.FOOTER_SHARED}</p>
      </footer>
    </div>
  );
}