all repos — snow-editor @ 65993a713586363acceab129af67161a5fbe71cc

small and cozy markdown, and orgmode editor

src/hooks/useServerAutosave.js (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
import { useCallback, useEffect, useRef, useState } from 'react';
import { ApiError, updateDocument } from '../lib/api.js';

const AUTOSAVE_DEBOUNCE_MS = 1000;

export function useServerAutosave({
  editToken,
  clientId,
  lockToken,
  enabled,
  title,
  mode,
  content,
}) {
  const [saveStatus, setSaveStatus] = useState('idle');
  const timerRef = useRef(null);
  const lastSavedRef = useRef('');

  const saveNow = useCallback(async () => {
    if (!enabled || !editToken || !clientId || !lockToken) {
      setSaveStatus('no_permission');
      return false;
    }

    const snapshot = JSON.stringify({ title, mode, content });
    if (snapshot === lastSavedRef.current) {
      setSaveStatus('saved');
      return true;
    }

    setSaveStatus('saving');
    try {
      const result = await updateDocument(editToken, {
        clientId,
        lockToken,
        title,
        mode,
        content,
      });
      lastSavedRef.current = snapshot;
      setSaveStatus('saved');
      return result;
    } catch (error) {
      if (error instanceof ApiError && (error.status === 403 || error.status === 423)) {
        setSaveStatus('no_permission');
      } else {
        setSaveStatus('error');
      }
      return false;
    }
  }, [enabled, editToken, clientId, lockToken, title, mode, content]);

  useEffect(() => {
    if (!enabled) {
      setSaveStatus('no_permission');
      return;
    }

    window.clearTimeout(timerRef.current);
    timerRef.current = window.setTimeout(() => {
      saveNow();
    }, AUTOSAVE_DEBOUNCE_MS);

    return () => window.clearTimeout(timerRef.current);
  }, [enabled, title, mode, content, saveNow]);

  return { saveStatus, saveNow, setSaveStatus };
}