mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2025-12-21 12:49:06 +00:00
room url copy button for ics
This commit is contained in:
@@ -11,15 +11,18 @@ import {
|
|||||||
createListCollection,
|
createListCollection,
|
||||||
Spinner,
|
Spinner,
|
||||||
Box,
|
Box,
|
||||||
|
IconButton,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { LuRefreshCw } from "react-icons/lu";
|
import { LuRefreshCw, LuCopy, LuCheck } from "react-icons/lu";
|
||||||
import { FaCheckCircle, FaExclamationCircle } from "react-icons/fa";
|
import { FaCheckCircle, FaExclamationCircle } from "react-icons/fa";
|
||||||
import { useRoomIcsSync, useRoomIcsStatus } from "../../../lib/apiHooks";
|
import { useRoomIcsSync, useRoomIcsStatus } from "../../../lib/apiHooks";
|
||||||
|
import { toaster } from "../../../components/ui/toaster";
|
||||||
|
import { roomAbsoluteUrl } from "../../../lib/routesClient";
|
||||||
|
import { assertExists } from "../../../lib/utils";
|
||||||
|
|
||||||
interface ICSSettingsProps {
|
interface ICSSettingsProps {
|
||||||
roomId?: string;
|
roomName: string;
|
||||||
roomName?: string;
|
|
||||||
icsUrl?: string;
|
icsUrl?: string;
|
||||||
icsEnabled?: boolean;
|
icsEnabled?: boolean;
|
||||||
icsFetchInterval?: number;
|
icsFetchInterval?: number;
|
||||||
@@ -45,7 +48,6 @@ const fetchIntervalOptions = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function ICSSettings({
|
export default function ICSSettings({
|
||||||
roomId,
|
|
||||||
roomName,
|
roomName,
|
||||||
icsUrl = "",
|
icsUrl = "",
|
||||||
icsEnabled = false,
|
icsEnabled = false,
|
||||||
@@ -66,6 +68,8 @@ export default function ICSSettings({
|
|||||||
eventsCreated: number;
|
eventsCreated: number;
|
||||||
eventsUpdated: number;
|
eventsUpdated: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const [justCopied, setJustCopied] = useState(false);
|
||||||
|
const roomUrlInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const syncMutation = useRoomIcsSync();
|
const syncMutation = useRoomIcsSync();
|
||||||
|
|
||||||
@@ -73,6 +77,51 @@ export default function ICSSettings({
|
|||||||
items: fetchIntervalOptions,
|
items: fetchIntervalOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleCopyRoomUrl = async () => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(
|
||||||
|
roomAbsoluteUrl(assertExists(roomName)),
|
||||||
|
);
|
||||||
|
setJustCopied(true);
|
||||||
|
|
||||||
|
toaster
|
||||||
|
.create({
|
||||||
|
placement: "top",
|
||||||
|
duration: 3000,
|
||||||
|
render: ({ dismiss }) => (
|
||||||
|
<Box
|
||||||
|
bg="green.500"
|
||||||
|
color="white"
|
||||||
|
px={4}
|
||||||
|
py={3}
|
||||||
|
borderRadius="md"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
gap={2}
|
||||||
|
boxShadow="lg"
|
||||||
|
>
|
||||||
|
<LuCheck />
|
||||||
|
<Text>Room URL copied to clipboard!</Text>
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.then(() => {});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setJustCopied(false);
|
||||||
|
}, 2000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to copy room url:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRoomUrlClick = () => {
|
||||||
|
if (roomUrlInputRef.current) {
|
||||||
|
roomUrlInputRef.current.select();
|
||||||
|
handleCopyRoomUrl();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Clear sync results when dialog closes
|
// Clear sync results when dialog closes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isEditing) {
|
if (!isEditing) {
|
||||||
@@ -136,6 +185,39 @@ export default function ICSSettings({
|
|||||||
|
|
||||||
{icsEnabled && (
|
{icsEnabled && (
|
||||||
<>
|
<>
|
||||||
|
<Field.Root>
|
||||||
|
<Field.Label>Room URL</Field.Label>
|
||||||
|
<Field.HelperText>
|
||||||
|
To enable Reflector to recognize your calendar events as meetings,
|
||||||
|
add this URL as the location in your calendar events
|
||||||
|
</Field.HelperText>
|
||||||
|
<HStack gap={0} position="relative" width="100%">
|
||||||
|
<Input
|
||||||
|
ref={roomUrlInputRef}
|
||||||
|
value={roomAbsoluteUrl(roomName)}
|
||||||
|
readOnly
|
||||||
|
onClick={handleRoomUrlClick}
|
||||||
|
cursor="pointer"
|
||||||
|
bg="gray.100"
|
||||||
|
_hover={{ bg: "gray.200" }}
|
||||||
|
_focus={{ bg: "gray.200" }}
|
||||||
|
pr="48px"
|
||||||
|
width="100%"
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
aria-label="Copy room URL"
|
||||||
|
onClick={handleCopyRoomUrl}
|
||||||
|
variant="ghost"
|
||||||
|
position="absolute"
|
||||||
|
right="4px"
|
||||||
|
size="sm"
|
||||||
|
zIndex={1}
|
||||||
|
>
|
||||||
|
{justCopied ? <LuCheck /> : <LuCopy />}
|
||||||
|
</IconButton>
|
||||||
|
</HStack>
|
||||||
|
</Field.Root>
|
||||||
|
|
||||||
<Field.Root>
|
<Field.Root>
|
||||||
<Field.Label>ICS Calendar URL</Field.Label>
|
<Field.Label>ICS Calendar URL</Field.Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { RoomList } from "./_components/RoomList";
|
|||||||
import { PaginationPage } from "../browse/_components/Pagination";
|
import { PaginationPage } from "../browse/_components/Pagination";
|
||||||
import { assertExists } from "../../lib/utils";
|
import { assertExists } from "../../lib/utils";
|
||||||
import ICSSettings from "./_components/ICSSettings";
|
import ICSSettings from "./_components/ICSSettings";
|
||||||
|
import { roomAbsoluteUrl } from "../../lib/routesClient";
|
||||||
|
|
||||||
type Room = components["schemas"]["Room"];
|
type Room = components["schemas"]["Room"];
|
||||||
|
|
||||||
@@ -187,13 +188,12 @@ export default function RoomsList() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleCopyUrl = (roomName: string) => {
|
const handleCopyUrl = (roomName: string) => {
|
||||||
const roomUrl = `${window.location.origin}/${roomName}`;
|
navigator.clipboard.writeText(roomAbsoluteUrl(roomName)).then(() => {
|
||||||
navigator.clipboard.writeText(roomUrl);
|
|
||||||
setLinkCopied(roomName);
|
setLinkCopied(roomName);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLinkCopied("");
|
setLinkCopied("");
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseDialog = () => {
|
const handleCloseDialog = () => {
|
||||||
@@ -620,7 +620,6 @@ export default function RoomsList() {
|
|||||||
|
|
||||||
<Tabs.Content value="calendar" pt={6}>
|
<Tabs.Content value="calendar" pt={6}>
|
||||||
<ICSSettings
|
<ICSSettings
|
||||||
roomId={editRoomId ?? undefined}
|
|
||||||
roomName={room.name}
|
roomName={room.name}
|
||||||
icsUrl={room.icsUrl}
|
icsUrl={room.icsUrl}
|
||||||
icsEnabled={room.icsEnabled}
|
icsEnabled={room.icsEnabled}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Flex, Link, Button, Text, HStack } from "@chakra-ui/react";
|
|||||||
import NextLink from "next/link";
|
import NextLink from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { roomUrl } from "../lib/routes";
|
||||||
|
|
||||||
interface MeetingMinimalHeaderProps {
|
interface MeetingMinimalHeaderProps {
|
||||||
roomName: string;
|
roomName: string;
|
||||||
@@ -30,7 +31,7 @@ export default function MeetingMinimalHeader({
|
|||||||
if (onLeave) {
|
if (onLeave) {
|
||||||
onLeave();
|
onLeave();
|
||||||
} else {
|
} else {
|
||||||
router.push(`/${roomName}`);
|
router.push(roomUrl(roomName));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
1
www/app/lib/routes.ts
Normal file
1
www/app/lib/routes.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const roomUrl = (roomName: string) => `/${roomName}`;
|
||||||
4
www/app/lib/routesClient.ts
Normal file
4
www/app/lib/routesClient.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { roomUrl } from "./routes";
|
||||||
|
|
||||||
|
export const roomAbsoluteUrl = (roomName: string) =>
|
||||||
|
`${window.location.origin}${roomUrl(roomName)}`;
|
||||||
Reference in New Issue
Block a user