mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-20 20:29:06 +00:00
fix selection (for good?)
This commit is contained in:
@@ -4,7 +4,6 @@ import { 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 { unescapeLeadingUnderscores } from "typescript";
|
|
||||||
|
|
||||||
type ParticipantList = {
|
type ParticipantList = {
|
||||||
participants: UseParticipants;
|
participants: UseParticipants;
|
||||||
@@ -27,6 +26,7 @@ const ParticipantList = ({
|
|||||||
const [participantInput, setParticipantInput] = useState("");
|
const [participantInput, setParticipantInput] = useState("");
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [selectedSpeaker, setSelectedSpeaker] = stateSelectedSpeaker;
|
const [selectedSpeaker, setSelectedSpeaker] = stateSelectedSpeaker;
|
||||||
|
const [selectedParticipant, setSelectedParticipant] = useState<Participant>();
|
||||||
const [action, setAction] = useState<
|
const [action, setAction] = useState<
|
||||||
"Create" | "Create to rename" | "Create and assign" | "Rename" | null
|
"Create" | "Create to rename" | "Create and assign" | "Rename" | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -47,9 +47,11 @@ const ParticipantList = ({
|
|||||||
);
|
);
|
||||||
if (participant) {
|
if (participant) {
|
||||||
setParticipantInput(participant.name);
|
setParticipantInput(participant.name);
|
||||||
|
setSelectedParticipant(participant);
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setAction("Rename");
|
setAction("Rename");
|
||||||
} else {
|
} else if (!selectedParticipant) {
|
||||||
|
setSelectedParticipant(undefined);
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setAction("Create to rename");
|
setAction("Create to rename");
|
||||||
@@ -59,6 +61,10 @@ const ParticipantList = ({
|
|||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
setAction("Create and assign");
|
setAction("Create and assign");
|
||||||
|
setSelectedParticipant(undefined);
|
||||||
|
}
|
||||||
|
if (!selectedTime && !selectedSpeaker) {
|
||||||
|
setAction(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [selectedTime, selectedSpeaker]);
|
}, [selectedTime, selectedSpeaker]);
|
||||||
@@ -78,6 +84,12 @@ const ParticipantList = ({
|
|||||||
setOneMatch(undefined);
|
setOneMatch(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (participantInput && !action) {
|
||||||
|
setAction("Create");
|
||||||
|
}
|
||||||
|
if (!participantInput) {
|
||||||
|
setAction(null);
|
||||||
|
}
|
||||||
}, [participantInput]);
|
}, [participantInput]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -88,11 +100,15 @@ const ParticipantList = ({
|
|||||||
setOneMatch(undefined);
|
setOneMatch(undefined);
|
||||||
setParticipantInput("");
|
setParticipantInput("");
|
||||||
}
|
}
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
|
doAction();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const doAction = (e) => {
|
const doAction = (e?) => {
|
||||||
|
e?.preventDefault();
|
||||||
|
e?.stopPropagation();
|
||||||
if (!participants.response) return;
|
if (!participants.response) return;
|
||||||
if (action == "Rename") {
|
if (action == "Rename") {
|
||||||
const participant = participants.response.find(
|
const participant = participants.response.find(
|
||||||
@@ -124,6 +140,7 @@ const ParticipantList = ({
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
participants.refetch();
|
participants.refetch();
|
||||||
|
setParticipantInput("");
|
||||||
});
|
});
|
||||||
} else if (action == "Create and assign") {
|
} else if (action == "Create and assign") {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -138,11 +155,22 @@ const ParticipantList = ({
|
|||||||
.then((participant) => {
|
.then((participant) => {
|
||||||
assignTo(participant)();
|
assignTo(participant)();
|
||||||
participants.refetch();
|
participants.refetch();
|
||||||
|
setParticipantInput("");
|
||||||
|
});
|
||||||
|
} else if (action == "Create") {
|
||||||
|
setLoading(true);
|
||||||
|
api
|
||||||
|
?.v1TranscriptAddParticipant({
|
||||||
|
createParticipant: {
|
||||||
|
name: participantInput,
|
||||||
|
},
|
||||||
|
transcriptId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
participants.refetch();
|
||||||
|
setParticipantInput("");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
|
||||||
console.log(e);
|
|
||||||
console.log(action);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteParticipant = (participantId) => () => {
|
const deleteParticipant = (participantId) => () => {
|
||||||
@@ -162,6 +190,7 @@ const ParticipantList = ({
|
|||||||
(participant) => (e?: React.MouseEvent<HTMLButtonElement>) => {
|
(participant) => (e?: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
|
// fix participant that doesnt have a speaker (wait API)
|
||||||
if (selectedTime?.start == undefined || selectedTime?.end == undefined)
|
if (selectedTime?.start == undefined || selectedTime?.end == undefined)
|
||||||
return;
|
return;
|
||||||
api
|
api
|
||||||
@@ -179,22 +208,29 @@ const ParticipantList = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selectParticipant = (participant) => (e) => {
|
const selectParticipant = (participant) => (e) => {
|
||||||
console.log(participant, e);
|
setSelectedParticipant(participant);
|
||||||
setSelectedSpeaker(participant.speaker);
|
setSelectedSpeaker(participant.speaker);
|
||||||
|
setAction("Rename");
|
||||||
|
setParticipantInput(participant.name);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<form onSubmit={doAction}>
|
<div>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
onChange={(e) => setParticipantInput(e.target.value)}
|
onChange={(e) => setParticipantInput(e.target.value)}
|
||||||
value={participantInput}
|
value={participantInput}
|
||||||
/>
|
/>
|
||||||
<button type="submit">
|
{action && (
|
||||||
<FontAwesomeIcon icon={faArrowTurnDown} className="rotate-90 mr-2" />
|
<button onClick={doAction}>
|
||||||
{action}
|
<FontAwesomeIcon
|
||||||
</button>
|
icon={faArrowTurnDown}
|
||||||
</form>
|
className="rotate-90 mr-2"
|
||||||
|
/>
|
||||||
|
{action}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{participants.loading && (
|
{participants.loading && (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
@@ -214,7 +250,7 @@ const ParticipantList = ({
|
|||||||
participant.name.startsWith(participantInput)
|
participant.name.startsWith(participantInput)
|
||||||
? "bg-blue-100 "
|
? "bg-blue-100 "
|
||||||
: "") +
|
: "") +
|
||||||
(participant.speaker == selectedSpeaker
|
(participant.id == selectedParticipant?.id
|
||||||
? "border-blue-400 border"
|
? "border-blue-400 border"
|
||||||
: "")
|
: "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const TopicPlayer = ({ transcriptId, selectedTime, topicTime }) => {
|
|||||||
useState<() => void>();
|
useState<() => void>();
|
||||||
|
|
||||||
//TODO shortcuts
|
//TODO shortcuts
|
||||||
|
// TODO if selection changes while playing, don't play
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setEndTopicCallback(
|
setEndTopicCallback(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type Word = {
|
|||||||
|
|
||||||
type WordBySpeaker = { speaker: number; words: Word[] }[];
|
type WordBySpeaker = { speaker: number; words: Word[] }[];
|
||||||
|
|
||||||
// TODO shortcuts
|
// TODO shortcuts ?
|
||||||
// TODO fix key (using indexes might act up, not sure as we don't re-order per say)
|
// TODO fix key (using indexes might act up, not sure as we don't re-order per say)
|
||||||
|
|
||||||
type TopicWordsProps = {
|
type TopicWordsProps = {
|
||||||
@@ -63,6 +63,25 @@ const topicWords = ({
|
|||||||
}
|
}
|
||||||
}, [topicWithWords.response]);
|
}, [topicWithWords.response]);
|
||||||
|
|
||||||
|
const getStartTimeFromFirstNode = (node, offset, reverse) => {
|
||||||
|
// if the first element is a word
|
||||||
|
return node.parentElement?.dataset["start"]
|
||||||
|
? // but after of the word (like on the blank space right of the word)
|
||||||
|
node.textContent?.length == offset
|
||||||
|
? // if next element is a word, we need the start of it
|
||||||
|
(node.parentElement?.nextElementSibling as any)?.dataset?.["start"] ||
|
||||||
|
// otherwise we get the start of the first word of the next paragraph
|
||||||
|
(
|
||||||
|
node.parentElement?.parentElement?.nextElementSibling
|
||||||
|
?.childNodes[1] as any
|
||||||
|
)?.dataset?.["start"] ||
|
||||||
|
(reverse ? 0 : 99)
|
||||||
|
: // otherwise it's just somewhere in the word and we get the start of the word
|
||||||
|
node.parentElement?.dataset["start"]
|
||||||
|
: // otherwise selection start is on a name and we get the start of the next word
|
||||||
|
(node.parentElement?.nextElementSibling as any)?.dataset["start"];
|
||||||
|
};
|
||||||
|
|
||||||
const onMouseUp = (e) => {
|
const onMouseUp = (e) => {
|
||||||
let selection = window.getSelection();
|
let selection = window.getSelection();
|
||||||
if (
|
if (
|
||||||
@@ -102,35 +121,47 @@ const topicWords = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const anchorStart = anchorIsWord
|
const anchorStart = getStartTimeFromFirstNode(
|
||||||
? anchorNode.parentElement?.dataset["start"]
|
anchorNode,
|
||||||
: (selection.anchorNode.parentElement?.nextElementSibling as any)
|
selection.anchorOffset,
|
||||||
?.dataset["start"];
|
false,
|
||||||
|
);
|
||||||
|
// if selection end on a word, we get the end time from the span that contains it
|
||||||
const focusEnd =
|
const focusEnd =
|
||||||
selection.focusNode.parentElement?.dataset["end"] ||
|
selection.focusNode.parentElement?.dataset["end"] ||
|
||||||
|
// otherwise it was a name and we get the end of the last word of the previous paragraph
|
||||||
(
|
(
|
||||||
selection.focusNode.parentElement?.parentElement
|
selection.focusNode.parentElement?.parentElement
|
||||||
?.previousElementSibling?.lastElementChild as any
|
?.previousElementSibling?.lastElementChild as any
|
||||||
)?.dataset["end"];
|
)?.dataset["end"] ||
|
||||||
|
0;
|
||||||
|
|
||||||
const reverse = parseFloat(anchorStart) > parseFloat(focusEnd);
|
const reverse = parseFloat(anchorStart) > parseFloat(focusEnd);
|
||||||
|
|
||||||
if (!reverse) {
|
if (!reverse) {
|
||||||
setSelectedTime({ start: anchorStart, end: focusEnd });
|
setSelectedTime({
|
||||||
|
start: parseFloat(anchorStart),
|
||||||
|
end: parseFloat(focusEnd),
|
||||||
|
});
|
||||||
console.log("setting right");
|
console.log("setting right");
|
||||||
} else {
|
} else {
|
||||||
const anchorEnd = anchorIsWord
|
const anchorEnd =
|
||||||
? anchorNode.parentElement?.dataset["end"]
|
anchorNode.parentElement?.dataset["end"] ||
|
||||||
: (selection.anchorNode.parentElement?.nextElementSibling as any)
|
|
||||||
?.dataset["end"];
|
|
||||||
const focusStart =
|
|
||||||
selection.focusNode.parentElement?.dataset["start"] ||
|
|
||||||
(
|
(
|
||||||
selection.focusNode.parentElement?.parentElement
|
selection.anchorNode.parentElement?.parentElement
|
||||||
?.previousElementSibling?.lastElementChild as any
|
?.previousElementSibling?.lastElementChild as any
|
||||||
)?.dataset["start"];
|
)?.dataset["end"];
|
||||||
|
|
||||||
|
const focusStart = getStartTimeFromFirstNode(
|
||||||
|
focusNode,
|
||||||
|
selection.focusOffset,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
setSelectedTime({ start: focusStart, end: anchorEnd });
|
setSelectedTime({ start: focusStart, end: anchorEnd });
|
||||||
console.log("setting reverse");
|
console.log("setting reverse");
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedSpeaker();
|
setSelectedSpeaker();
|
||||||
selection.empty();
|
selection.empty();
|
||||||
}
|
}
|
||||||
@@ -147,7 +178,7 @@ const topicWords = ({
|
|||||||
|
|
||||||
if (!topicWithWords.loading && wordsBySpeaker && participants.response) {
|
if (!topicWithWords.loading && wordsBySpeaker && participants.response) {
|
||||||
return (
|
return (
|
||||||
<div onMouseUp={onMouseUp} onBlur={(e) => console.log(e)}>
|
<div onMouseUp={onMouseUp} className="p-5 h-full w-full">
|
||||||
{wordsBySpeaker?.map((speakerWithWords, index) => (
|
{wordsBySpeaker?.map((speakerWithWords, index) => (
|
||||||
<p key={index}>
|
<p key={index}>
|
||||||
<span
|
<span
|
||||||
|
|||||||
Reference in New Issue
Block a user