File size: 3,280 Bytes
a6394d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
function Spinner({ config }) {
  const ref = React.useRef(null);

  React.useEffect(() => {
    const spinner = new Spin.Spinner({
      lines: 13,
      color: "#ffffff",
      ...config,
    });
    spinner.spin(ref.current);
    return () => spinner.stop();
  }, [ref]);

  return <span ref={ref} />;
}

function Result({ callId, selectedFile }) {
  const [result, setResult] = React.useState();
  const [intervalId, setIntervalId] = React.useState();

  React.useEffect(() => {
    if (result) {
      clearInterval(intervalId);
      return;
    }

    const _intervalID = setInterval(async () => {
      const resp = await fetch(`/result/${callId}`);
      if (resp.status === 200) {
        setResult(await resp.json());
      }
    }, 100);

    setIntervalId(_intervalID);

    return () => clearInterval(intervalId);
  }, [result]);

  return (
    <div class="flex items-center content-center justify-center space-x-4 ">
      <img src={URL.createObjectURL(selectedFile)} class="h-[300px]" />
      {!result && <Spinner config={{}} />}
      {result && (
        <p class="w-[200px] p-4 bg-zinc-200 rounded-lg whitespace-pre-wrap text-xs font-mono">
          {JSON.stringify(result, undefined, 1)}
        </p>
      )}
    </div>
  );
}

function Form({ onSubmit, onFileSelect, selectedFile }) {
  return (
    <form class="flex flex-col space-y-4 items-center">
      <div class="text-2xl font-semibold text-gray-700"> ViT-GPT2 Image Captioning </div>
      <input
        accept="image/*"
        type="file"
        name="file"
        onChange={onFileSelect}
        class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 cursor-pointer"
      />
      {selectedFile ? (
        <img src={URL.createObjectURL(selectedFile)} class="h-[300px]" />
      ) : null}
      <div>
        <button
          type="button"
          onClick={onSubmit}
          disabled={!selectedFile}
          class="bg-indigo-400 disabled:bg-zinc-500 hover:bg-indigo-600 text-white font-bold py-2 px-4 rounded text-sm"
        >
          Upload
        </button>
      </div>
    </form>
  );
}

function App() {
  const [selectedFile, setSelectedFile] = React.useState();
  const [callId, setCallId] = React.useState();

  const handleSubmission = async () => {
    const formData = new FormData();
    formData.append("image", selectedFile);

    const resp = await fetch("/parse", {
      method: "POST",
      body: formData,
    });

    if (resp.status !== 200) {
      throw new Error("An error occurred: " + resp.status);
    }
    const body = await resp.json();
    setCallId(body.call_id);
  };

  return (
    <div class="absolute inset-0 bg-gradient-to-r from-indigo-300 via-purple-300 to-pink-300">
      <div class="mx-auto max-w-md py-8">
        <main class="rounded-xl bg-white p-6">
          {!callId && (
            <Form
              onSubmit={handleSubmission}
              onFileSelect={(e) => setSelectedFile(e.target.files[0])}
              selectedFile={selectedFile}
            />
          )}
          {callId && <Result callId={callId} selectedFile={selectedFile} />}
        </main>
      </div>
    </div>
  );
}

const container = document.getElementById("react");
ReactDOM.createRoot(container).render(<App />);