mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-21 04:39:06 +00:00
error handling and clean up
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import useTranscript from "../../useTranscript";
|
|
||||||
import TopicHeader from "./topicHeader";
|
import TopicHeader from "./topicHeader";
|
||||||
import TopicWords from "./topicWords";
|
import TopicWords from "./topicWords";
|
||||||
import TopicPlayer from "./topicPlayer";
|
import TopicPlayer from "./topicPlayer";
|
||||||
@@ -8,6 +7,7 @@ import useParticipants from "../../useParticipants";
|
|||||||
import useTopicWithWords from "../../useTopicWithWords";
|
import useTopicWithWords from "../../useTopicWithWords";
|
||||||
import ParticipantList from "./participantList";
|
import ParticipantList from "./participantList";
|
||||||
import { GetTranscriptTopic } from "../../../../api";
|
import { GetTranscriptTopic } from "../../../../api";
|
||||||
|
import { SelectedText, selectedTextIsTimeSlice } from "./types";
|
||||||
|
|
||||||
export type TranscriptCorrect = {
|
export type TranscriptCorrect = {
|
||||||
params: {
|
params: {
|
||||||
@@ -15,48 +15,15 @@ export type TranscriptCorrect = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TimeSlice = {
|
export default function TranscriptCorrect({
|
||||||
start: number;
|
params: { transcriptId },
|
||||||
end: number;
|
}: TranscriptCorrect) {
|
||||||
};
|
|
||||||
|
|
||||||
export type SelectedText = number | TimeSlice | undefined;
|
|
||||||
|
|
||||||
export function selectedTextIsSpeaker(
|
|
||||||
selectedText: SelectedText,
|
|
||||||
): selectedText is number {
|
|
||||||
return typeof selectedText == "number";
|
|
||||||
}
|
|
||||||
export function selectedTextIsTimeSlice(
|
|
||||||
selectedText: SelectedText,
|
|
||||||
): selectedText is TimeSlice {
|
|
||||||
return (
|
|
||||||
typeof (selectedText as any)?.start == "number" &&
|
|
||||||
typeof (selectedText as any)?.end == "number"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TranscriptCorrect(details: TranscriptCorrect) {
|
|
||||||
const transcriptId = details.params.transcriptId;
|
|
||||||
const stateCurrentTopic = useState<GetTranscriptTopic>();
|
const stateCurrentTopic = useState<GetTranscriptTopic>();
|
||||||
const [currentTopic, _sct] = stateCurrentTopic;
|
const [currentTopic, _sct] = stateCurrentTopic;
|
||||||
const topicWithWords = useTopicWithWords(currentTopic?.id, transcriptId);
|
|
||||||
|
|
||||||
const [topicTime, setTopicTime] = useState<TimeSlice>();
|
|
||||||
const participants = useParticipants(transcriptId);
|
|
||||||
const stateSelectedText = useState<SelectedText>();
|
const stateSelectedText = useState<SelectedText>();
|
||||||
const [selectedText, _sst] = stateSelectedText;
|
const [selectedText, _sst] = stateSelectedText;
|
||||||
|
const topicWithWords = useTopicWithWords(currentTopic?.id, transcriptId);
|
||||||
useEffect(() => {
|
const participants = useParticipants(transcriptId);
|
||||||
if (currentTopic) {
|
|
||||||
setTopicTime({
|
|
||||||
start: currentTopic.timestamp,
|
|
||||||
end: currentTopic.timestamp + currentTopic.duration,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setTopicTime(undefined);
|
|
||||||
}
|
|
||||||
}, [currentTopic]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full grid grid-cols-2 gap-4">
|
<div className="h-full grid grid-cols-2 gap-4">
|
||||||
@@ -73,13 +40,21 @@ export default function TranscriptCorrect(details: TranscriptCorrect) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-stretch">
|
<div className="flex flex-col justify-stretch">
|
||||||
|
{currentTopic ? (
|
||||||
<TopicPlayer
|
<TopicPlayer
|
||||||
transcriptId={transcriptId}
|
transcriptId={transcriptId}
|
||||||
selectedTime={
|
selectedTime={
|
||||||
selectedTextIsTimeSlice(selectedText) ? selectedText : undefined
|
selectedTextIsTimeSlice(selectedText) ? selectedText : undefined
|
||||||
}
|
}
|
||||||
topicTime={topicTime}
|
topicTime={{
|
||||||
|
start: currentTopic?.timestamp,
|
||||||
|
end: currentTopic?.timestamp + currentTopic?.duration,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div></div>
|
||||||
|
)}
|
||||||
|
{participants.response && (
|
||||||
<ParticipantList
|
<ParticipantList
|
||||||
{...{
|
{...{
|
||||||
transcriptId,
|
transcriptId,
|
||||||
@@ -88,6 +63,7 @@ export default function TranscriptCorrect(details: TranscriptCorrect) {
|
|||||||
stateSelectedText,
|
stateSelectedText,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { ChangeEvent, useEffect, useRef, useState } from "react";
|
|||||||
import { Participant } from "../../../../api";
|
import { Participant } from "../../../../api";
|
||||||
import getApi from "../../../../lib/getApi";
|
import getApi from "../../../../lib/getApi";
|
||||||
import { UseParticipants } from "../../useParticipants";
|
import { UseParticipants } from "../../useParticipants";
|
||||||
import { selectedTextIsSpeaker, selectedTextIsTimeSlice } from "./page";
|
import { selectedTextIsSpeaker, selectedTextIsTimeSlice } from "./types";
|
||||||
|
import { useError } from "../../../../(errors)/errorContext";
|
||||||
|
|
||||||
type ParticipantList = {
|
type ParticipantList = {
|
||||||
participants: UseParticipants;
|
participants: UseParticipants;
|
||||||
@@ -13,6 +14,7 @@ type ParticipantList = {
|
|||||||
stateSelectedText: any;
|
stateSelectedText: any;
|
||||||
};
|
};
|
||||||
// NTH re-order list when searching
|
// NTH re-order list when searching
|
||||||
|
// HTH case-insensitive matching
|
||||||
const ParticipantList = ({
|
const ParticipantList = ({
|
||||||
transcriptId,
|
transcriptId,
|
||||||
participants,
|
participants,
|
||||||
@@ -20,7 +22,7 @@ const ParticipantList = ({
|
|||||||
stateSelectedText,
|
stateSelectedText,
|
||||||
}: ParticipantList) => {
|
}: ParticipantList) => {
|
||||||
const api = getApi();
|
const api = getApi();
|
||||||
|
const { setError } = useError();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [participantInput, setParticipantInput] = useState("");
|
const [participantInput, setParticipantInput] = useState("");
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -40,40 +42,42 @@ const ParticipantList = ({
|
|||||||
);
|
);
|
||||||
if (participant) {
|
if (participant) {
|
||||||
setParticipantInput(participant.name);
|
setParticipantInput(participant.name);
|
||||||
|
setOneMatch(undefined);
|
||||||
setSelectedParticipant(participant);
|
setSelectedParticipant(participant);
|
||||||
inputRef.current?.focus();
|
|
||||||
setAction("Rename");
|
setAction("Rename");
|
||||||
} else if (!selectedParticipant) {
|
} else if (!selectedParticipant) {
|
||||||
setSelectedParticipant(undefined);
|
setSelectedParticipant(undefined);
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
inputRef.current?.focus();
|
setOneMatch(undefined);
|
||||||
setAction("Create to rename");
|
setAction("Create to rename");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selectedTextIsTimeSlice(selectedText)) {
|
if (selectedTextIsTimeSlice(selectedText)) {
|
||||||
setParticipantInput("");
|
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
|
setParticipantInput("");
|
||||||
|
setOneMatch(undefined);
|
||||||
setAction("Create and assign");
|
setAction("Create and assign");
|
||||||
setSelectedParticipant(undefined);
|
setSelectedParticipant(undefined);
|
||||||
}
|
}
|
||||||
if (typeof selectedText == undefined) {
|
if (typeof selectedText == undefined) {
|
||||||
|
inputRef.current?.blur();
|
||||||
setAction(null);
|
setAction(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [selectedText, participants]);
|
}, [selectedText, participants.response]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.onkeyup = (e) => {
|
document.onkeyup = (e) => {
|
||||||
if (loading || participants.loading || topicWithWords.loading) return;
|
if (loading || participants.loading || topicWithWords.loading) return;
|
||||||
if (e.key === "Enter" && e.ctrlKey) {
|
if (e.key === "Enter" && e.ctrlKey) {
|
||||||
if (oneMatch) {
|
if (oneMatch) {
|
||||||
if (action == "Create and assign") {
|
if (
|
||||||
assignTo(oneMatch)();
|
action == "Create and assign" &&
|
||||||
setOneMatch(undefined);
|
selectedTextIsTimeSlice(selectedText)
|
||||||
setParticipantInput("");
|
) {
|
||||||
|
assignTo(oneMatch)().catch(() => {});
|
||||||
} else if (
|
} else if (
|
||||||
action == "Create to rename" &&
|
action == "Create to rename" &&
|
||||||
oneMatch &&
|
|
||||||
selectedTextIsSpeaker(selectedText)
|
selectedTextIsSpeaker(selectedText)
|
||||||
) {
|
) {
|
||||||
mergeSpeaker(selectedText, oneMatch)();
|
mergeSpeaker(selectedText, oneMatch)();
|
||||||
@@ -85,11 +89,50 @@ const ParticipantList = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onSuccess = () => {
|
||||||
|
topicWithWords.refetch();
|
||||||
|
participants.refetch();
|
||||||
|
setLoading(false);
|
||||||
|
setAction(null);
|
||||||
|
setSelectedText(undefined);
|
||||||
|
setSelectedParticipant(undefined);
|
||||||
|
setParticipantInput("");
|
||||||
|
setOneMatch(undefined);
|
||||||
|
inputRef?.current?.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assignTo =
|
||||||
|
(participant) => async (e?: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
e?.preventDefault();
|
||||||
|
e?.stopPropagation();
|
||||||
|
|
||||||
|
if (loading || participants.loading || topicWithWords.loading) return;
|
||||||
|
if (!selectedTextIsTimeSlice(selectedText)) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await api?.v1TranscriptAssignSpeaker({
|
||||||
|
speakerAssignment: {
|
||||||
|
participant: participant.id,
|
||||||
|
timestampFrom: selectedText.start,
|
||||||
|
timestampTo: selectedText.end,
|
||||||
|
},
|
||||||
|
transcriptId,
|
||||||
|
});
|
||||||
|
onSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
setError(error, "There was an error assigning");
|
||||||
|
setLoading(false);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const mergeSpeaker =
|
const mergeSpeaker =
|
||||||
(speakerFrom, participantTo: Participant) => async () => {
|
(speakerFrom, participantTo: Participant) => async () => {
|
||||||
if (loading || participants.loading || topicWithWords.loading) return;
|
if (loading || participants.loading || topicWithWords.loading) return;
|
||||||
if (participantTo.speaker) {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
if (participantTo.speaker) {
|
||||||
|
try {
|
||||||
await api?.v1TranscriptMergeSpeaker({
|
await api?.v1TranscriptMergeSpeaker({
|
||||||
transcriptId,
|
transcriptId,
|
||||||
speakerMerge: {
|
speakerMerge: {
|
||||||
@@ -97,21 +140,27 @@ const ParticipantList = ({
|
|||||||
speakerTo: participantTo.speaker,
|
speakerTo: participantTo.speaker,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
onSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
setError(error, "There was an error merging");
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
try {
|
||||||
await api?.v1TranscriptUpdateParticipant({
|
await api?.v1TranscriptUpdateParticipant({
|
||||||
transcriptId,
|
transcriptId,
|
||||||
participantId: participantTo.id,
|
participantId: participantTo.id,
|
||||||
updateParticipant: { speaker: speakerFrom },
|
updateParticipant: { speaker: speakerFrom },
|
||||||
});
|
});
|
||||||
}
|
onSuccess();
|
||||||
participants.refetch();
|
} catch (error) {
|
||||||
topicWithWords.refetch();
|
setError(error, "There was an error merging (update)");
|
||||||
setAction(null);
|
|
||||||
setParticipantInput("");
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const doAction = (e?) => {
|
const doAction = async (e?) => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
if (
|
if (
|
||||||
@@ -138,6 +187,10 @@ const ParticipantList = ({
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
participants.refetch();
|
participants.refetch();
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
setError(e, "There was an error renaming");
|
||||||
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -156,6 +209,11 @@ const ParticipantList = ({
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
participants.refetch();
|
participants.refetch();
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
|
setOneMatch(undefined);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
setError(e, "There was an error creating");
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
@@ -163,18 +221,22 @@ const ParticipantList = ({
|
|||||||
selectedTextIsTimeSlice(selectedText)
|
selectedTextIsTimeSlice(selectedText)
|
||||||
) {
|
) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
api
|
try {
|
||||||
?.v1TranscriptAddParticipant({
|
const participant = await api?.v1TranscriptAddParticipant({
|
||||||
createParticipant: {
|
createParticipant: {
|
||||||
name: participantInput,
|
name: participantInput,
|
||||||
},
|
},
|
||||||
transcriptId,
|
transcriptId,
|
||||||
})
|
|
||||||
.then((participant) => {
|
|
||||||
setLoading(false);
|
|
||||||
assignTo(participant)();
|
|
||||||
setParticipantInput("");
|
|
||||||
});
|
});
|
||||||
|
setLoading(false);
|
||||||
|
assignTo(participant)().catch(() => {
|
||||||
|
// error and loading are handled by assignTo catch
|
||||||
|
participants.refetch();
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
setError(e, "There was an error creating");
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
} else if (action == "Create") {
|
} else if (action == "Create") {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
api
|
api
|
||||||
@@ -188,6 +250,10 @@ const ParticipantList = ({
|
|||||||
participants.refetch();
|
participants.refetch();
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
setError(e, "There was an error creating");
|
||||||
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -204,33 +270,10 @@ const ParticipantList = ({
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
participants.refetch();
|
participants.refetch();
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const assignTo =
|
|
||||||
(participant) => (e?: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
e?.preventDefault();
|
|
||||||
e?.stopPropagation();
|
|
||||||
if (loading || participants.loading || topicWithWords.loading) return;
|
|
||||||
if (!selectedTextIsTimeSlice(selectedText)) return;
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
api
|
|
||||||
?.v1TranscriptAssignSpeaker({
|
|
||||||
speakerAssignment: {
|
|
||||||
participant: participant.id,
|
|
||||||
timestampFrom: selectedText.start,
|
|
||||||
timestampTo: selectedText.end,
|
|
||||||
},
|
|
||||||
transcriptId,
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch((e) => {
|
||||||
topicWithWords.refetch();
|
setError(e, "There was an error deleting");
|
||||||
participants.refetch();
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setAction(null);
|
|
||||||
setSelectedText(undefined);
|
|
||||||
setSelectedParticipant(undefined);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,6 +282,7 @@ const ParticipantList = ({
|
|||||||
setSelectedText(participant.speaker);
|
setSelectedText(participant.speaker);
|
||||||
setAction("Rename");
|
setAction("Rename");
|
||||||
setParticipantInput(participant.name);
|
setParticipantInput(participant.name);
|
||||||
|
oneMatch && setOneMatch(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
@@ -246,6 +290,7 @@ const ParticipantList = ({
|
|||||||
setSelectedText(undefined);
|
setSelectedText(undefined);
|
||||||
setAction(null);
|
setAction(null);
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
|
oneMatch && setOneMatch(undefined);
|
||||||
};
|
};
|
||||||
const preventClick = (e) => {
|
const preventClick = (e) => {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
|
|||||||
@@ -2,8 +2,19 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
import useMp3 from "../../useMp3";
|
import useMp3 from "../../useMp3";
|
||||||
import { formatTime } from "../../../../lib/time";
|
import { formatTime } from "../../../../lib/time";
|
||||||
import SoundWaveCss from "./soundWaveCss";
|
import SoundWaveCss from "./soundWaveCss";
|
||||||
|
import { TimeSlice } from "./types";
|
||||||
|
|
||||||
const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
type TopicPlayer = {
|
||||||
|
transcriptId: string;
|
||||||
|
selectedTime: TimeSlice | undefined;
|
||||||
|
topicTime: TimeSlice;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TopicPlayer = ({
|
||||||
|
transcriptId,
|
||||||
|
selectedTime,
|
||||||
|
topicTime,
|
||||||
|
}: TopicPlayer) => {
|
||||||
const mp3 = useMp3(transcriptId);
|
const mp3 = useMp3(transcriptId);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [endTopicCallback, setEndTopicCallback] = useState<() => void>();
|
const [endTopicCallback, setEndTopicCallback] = useState<() => void>();
|
||||||
@@ -56,11 +67,7 @@ const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
|||||||
setEndTopicCallback(
|
setEndTopicCallback(
|
||||||
() =>
|
() =>
|
||||||
function () {
|
function () {
|
||||||
if (
|
if (mp3.media && mp3.media.currentTime >= topicTime.end) {
|
||||||
mp3.media &&
|
|
||||||
topicTime.end &&
|
|
||||||
mp3.media.currentTime >= topicTime.end
|
|
||||||
) {
|
|
||||||
mp3.media.pause();
|
mp3.media.pause();
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
mp3.media.currentTime = topicTime.start;
|
mp3.media.currentTime = topicTime.start;
|
||||||
@@ -68,7 +75,7 @@ const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (mp3.media && topicTime) {
|
if (mp3.media) {
|
||||||
playButton.current?.focus();
|
playButton.current?.focus();
|
||||||
mp3.media?.pause();
|
mp3.media?.pause();
|
||||||
// there's no callback on pause but apparently changing the time while palying doesn't work... so here is a timeout
|
// there's no callback on pause but apparently changing the time while palying doesn't work... so here is a timeout
|
||||||
@@ -80,7 +87,7 @@ const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
|||||||
}, 10);
|
}, 10);
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
}
|
}
|
||||||
}, [topicTime, mp3.media]);
|
}, [!mp3.media, topicTime.start, topicTime.end]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
endTopicCallback &&
|
endTopicCallback &&
|
||||||
@@ -164,11 +171,9 @@ const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
|||||||
<div className="mb-4 grid grid-cols-3 gap-2">
|
<div className="mb-4 grid grid-cols-3 gap-2">
|
||||||
<SoundWaveCss playing={isPlaying} />
|
<SoundWaveCss playing={isPlaying} />
|
||||||
<div className="col-span-2">{showTime}</div>
|
<div className="col-span-2">{showTime}</div>
|
||||||
{topicTime && (
|
|
||||||
<button className="p-2 bg-blue-200 w-full" onClick={playTopic}>
|
<button className="p-2 bg-blue-200 w-full" onClick={playTopic}>
|
||||||
Play From Start
|
Play From Start
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
{!isPlaying ? (
|
{!isPlaying ? (
|
||||||
<button
|
<button
|
||||||
ref={playButton}
|
ref={playButton}
|
||||||
|
|||||||
@@ -1,22 +1,8 @@
|
|||||||
import {
|
import { Dispatch, SetStateAction, useEffect } from "react";
|
||||||
Dispatch,
|
|
||||||
SetStateAction,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import WaveformLoading from "../../waveformLoading";
|
import WaveformLoading from "../../waveformLoading";
|
||||||
import { UseParticipants } from "../../useParticipants";
|
import { UseParticipants } from "../../useParticipants";
|
||||||
import { Participant } from "../../../../api";
|
|
||||||
import { UseTopicWithWords } from "../../useTopicWithWords";
|
import { UseTopicWithWords } from "../../useTopicWithWords";
|
||||||
import {
|
import { TimeSlice, selectedTextIsTimeSlice } from "./types";
|
||||||
TimeSlice,
|
|
||||||
selectedTextIsSpeaker,
|
|
||||||
selectedTextIsTimeSlice,
|
|
||||||
} from "./page";
|
|
||||||
|
|
||||||
// TODO shortcuts ?
|
|
||||||
// TODO fix key (using indexes might act up, not sure as we don't re-order per say)
|
|
||||||
|
|
||||||
type TopicWordsProps = {
|
type TopicWordsProps = {
|
||||||
stateSelectedText: [
|
stateSelectedText: [
|
||||||
@@ -199,8 +185,9 @@ const topicWords = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (topicWithWords.loading) return <WaveformLoading />;
|
if (topicWithWords.loading || participants.loading)
|
||||||
if (topicWithWords.error) return <p>error</p>;
|
return <WaveformLoading />;
|
||||||
|
if (topicWithWords.error || participants.error) return <p>error</p>;
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
20
www/app/[domain]/transcripts/[transcriptId]/correct/types.ts
Normal file
20
www/app/[domain]/transcripts/[transcriptId]/correct/types.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export type TimeSlice = {
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SelectedText = number | TimeSlice | undefined;
|
||||||
|
|
||||||
|
export function selectedTextIsSpeaker(
|
||||||
|
selectedText: SelectedText,
|
||||||
|
): selectedText is number {
|
||||||
|
return typeof selectedText == "number";
|
||||||
|
}
|
||||||
|
export function selectedTextIsTimeSlice(
|
||||||
|
selectedText: SelectedText,
|
||||||
|
): selectedText is TimeSlice {
|
||||||
|
return (
|
||||||
|
typeof (selectedText as any)?.start == "number" &&
|
||||||
|
typeof (selectedText as any)?.end == "number"
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user