Compare commits

..

6 Commits

Author SHA1 Message Date
6ddfee0b4e chore(main): release 0.13.0 (#661) 2025-09-21 20:50:47 -06:00
Igor Monadical
47716f6e5d feat: room form edit with enter (#662)
* room form edit with enter

* mobile form enter do nothing

* restore overwritten older change

---------

Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
2025-09-19 15:14:40 -04:00
0abcebfc94 fix: invalid cleanup call (#660) 2025-09-18 10:02:30 -06:00
Igor Monadical
2b723da08b rooms-page-calendar-ics-room-name-fix (#659)
Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
2025-09-17 20:02:17 -04:00
6566e04300 chore(main): release 0.12.1 (#658) 2025-09-17 17:17:22 -06:00
870e860517 fix: production blocked because having existing meeting with room_id null (#657) 2025-09-17 17:09:54 -06:00
5 changed files with 455 additions and 425 deletions

View File

@@ -1,5 +1,24 @@
# Changelog # Changelog
## [0.13.0](https://github.com/Monadical-SAS/reflector/compare/v0.12.1...v0.13.0) (2025-09-19)
### Features
* room form edit with enter ([#662](https://github.com/Monadical-SAS/reflector/issues/662)) ([47716f6](https://github.com/Monadical-SAS/reflector/commit/47716f6e5ddee952609d2fa0ffabdfa865286796))
### Bug Fixes
* invalid cleanup call ([#660](https://github.com/Monadical-SAS/reflector/issues/660)) ([0abcebf](https://github.com/Monadical-SAS/reflector/commit/0abcebfc9491f87f605f21faa3e53996fafedd9a))
## [0.12.1](https://github.com/Monadical-SAS/reflector/compare/v0.12.0...v0.12.1) (2025-09-17)
### Bug Fixes
* production blocked because having existing meeting with room_id null ([#657](https://github.com/Monadical-SAS/reflector/issues/657)) ([870e860](https://github.com/Monadical-SAS/reflector/commit/870e8605171a27155a9cbee215eeccb9a8d6c0a2))
## [0.12.0](https://github.com/Monadical-SAS/reflector/compare/v0.11.0...v0.12.0) (2025-09-17) ## [0.12.0](https://github.com/Monadical-SAS/reflector/compare/v0.11.0...v0.12.0) (2025-09-17)

View File

@@ -8,7 +8,6 @@ Create Date: 2025-09-10 10:47:06.006819
from typing import Sequence, Union from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@@ -21,7 +20,6 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("meeting", schema=None) as batch_op: with op.batch_alter_table("meeting", schema=None) as batch_op:
batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=False)
batch_op.create_foreign_key( batch_op.create_foreign_key(
None, "room", ["room_id"], ["id"], ondelete="CASCADE" None, "room", ["room_id"], ["id"], ondelete="CASCADE"
) )
@@ -33,6 +31,5 @@ def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("meeting", schema=None) as batch_op: with op.batch_alter_table("meeting", schema=None) as batch_op:
batch_op.drop_constraint("meeting_room_id_fkey", type_="foreignkey") batch_op.drop_constraint("meeting_room_id_fkey", type_="foreignkey")
batch_op.alter_column("room_id", existing_type=sa.VARCHAR(), nullable=True)
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@@ -5,7 +5,6 @@ Deletes old anonymous transcripts and their associated meetings/recordings.
Transcripts are the main entry point - any associated data is also removed. Transcripts are the main entry point - any associated data is also removed.
""" """
import asyncio
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import TypedDict from typing import TypedDict
@@ -152,5 +151,5 @@ async def cleanup_old_public_data(
retry_kwargs={"max_retries": 3, "countdown": 300}, retry_kwargs={"max_retries": 3, "countdown": 300},
) )
@asynctask @asynctask
def cleanup_old_public_data_task(days: int | None = None): async def cleanup_old_public_data_task(days: int | None = None):
asyncio.run(cleanup_old_public_data(days=days)) await cleanup_old_public_data(days=days)

View File

@@ -27,7 +27,7 @@ import {
} from "../../../lib/utils"; } from "../../../lib/utils";
interface ICSSettingsProps { interface ICSSettingsProps {
roomName: NonEmptyString; roomName: NonEmptyString | null;
icsUrl?: string; icsUrl?: string;
icsEnabled?: boolean; icsEnabled?: boolean;
icsFetchInterval?: number; icsFetchInterval?: number;
@@ -85,7 +85,7 @@ export default function ICSSettings({
const handleCopyRoomUrl = async () => { const handleCopyRoomUrl = async () => {
try { try {
await navigator.clipboard.writeText( await navigator.clipboard.writeText(
roomAbsoluteUrl(assertExistsAndNonEmptyString(roomName)), roomAbsoluteUrl(assertExists(roomName)),
); );
setJustCopied(true); setJustCopied(true);
@@ -123,7 +123,7 @@ export default function ICSSettings({
const handleRoomUrlClick = () => { const handleRoomUrlClick = () => {
if (roomUrlInputRef.current) { if (roomUrlInputRef.current) {
roomUrlInputRef.current.select(); roomUrlInputRef.current.select();
handleCopyRoomUrl(); handleCopyRoomUrl().then(() => {});
} }
}; };
@@ -196,6 +196,7 @@ export default function ICSSettings({
To enable Reflector to recognize your calendar events as meetings, To enable Reflector to recognize your calendar events as meetings,
add this URL as the location in your calendar events add this URL as the location in your calendar events
</Field.HelperText> </Field.HelperText>
{roomName ? (
<HStack gap={0} position="relative" width="100%"> <HStack gap={0} position="relative" width="100%">
<Input <Input
ref={roomUrlInputRef} ref={roomUrlInputRef}
@@ -220,6 +221,7 @@ export default function ICSSettings({
</IconButton> </IconButton>
</HStack> </HStack>
</HStack> </HStack>
) : null}
</Field.Root> </Field.Root>
<Field.Root> <Field.Root>

View File

@@ -309,7 +309,7 @@ export default function RoomsList() {
setRoomInput(null); setRoomInput(null);
setIsEditing(false); setIsEditing(false);
setEditRoomId(""); setEditRoomId(null);
setNameError(""); setNameError("");
refetch(); refetch();
onClose(); onClose();
@@ -449,6 +449,13 @@ export default function RoomsList() {
</Dialog.CloseTrigger> </Dialog.CloseTrigger>
</Dialog.Header> </Dialog.Header>
<Dialog.Body> <Dialog.Body>
<form
id="room-form"
onSubmit={(e) => {
e.preventDefault();
handleSaveRoom();
}}
>
<Tabs.Root defaultValue="general"> <Tabs.Root defaultValue="general">
<Tabs.List> <Tabs.List>
<Tabs.Trigger value="general">General</Tabs.Trigger> <Tabs.Trigger value="general">General</Tabs.Trigger>
@@ -465,6 +472,7 @@ export default function RoomsList() {
placeholder="room-name" placeholder="room-name"
value={room.name} value={room.name}
onChange={handleRoomChange} onChange={handleRoomChange}
enterKeyHint="next"
/> />
<Field.HelperText> <Field.HelperText>
No spaces or special characters allowed No spaces or special characters allowed
@@ -496,7 +504,6 @@ export default function RoomsList() {
<Checkbox.Label>Locked room</Checkbox.Label> <Checkbox.Label>Locked room</Checkbox.Label>
</Checkbox.Root> </Checkbox.Root>
</Field.Root> </Field.Root>
<Field.Root mt={4}> <Field.Root mt={4}>
<Field.Label>Room size</Field.Label> <Field.Label>Room size</Field.Label>
<Select.Root <Select.Root
@@ -527,7 +534,6 @@ export default function RoomsList() {
</Select.Positioner> </Select.Positioner>
</Select.Root> </Select.Root>
</Field.Root> </Field.Root>
<Field.Root mt={4}> <Field.Root mt={4}>
<Field.Label>Recording type</Field.Label> <Field.Label>Recording type</Field.Label>
<Select.Root <Select.Root
@@ -565,13 +571,15 @@ export default function RoomsList() {
</Select.Positioner> </Select.Positioner>
</Select.Root> </Select.Root>
</Field.Root> </Field.Root>
<Field.Root mt={4}> <Field.Root mt={4}>
<Field.Label>Cloud recording start trigger</Field.Label> <Field.Label>Cloud recording start trigger</Field.Label>
<Select.Root <Select.Root
value={[room.recordingTrigger]} value={[room.recordingTrigger]}
onValueChange={(e) => onValueChange={(e) =>
setRoomInput({ ...room, recordingTrigger: e.value[0] }) setRoomInput({
...room,
recordingTrigger: e.value[0],
})
} }
collection={recordingTriggerCollection} collection={recordingTriggerCollection}
disabled={room.recordingType !== "cloud"} disabled={room.recordingType !== "cloud"}
@@ -622,34 +630,6 @@ export default function RoomsList() {
</Field.Root> </Field.Root>
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="calendar" pt={6}>
<ICSSettings
roomName={parseNonEmptyString(room.name)}
icsUrl={room.icsUrl}
icsEnabled={room.icsEnabled}
icsFetchInterval={room.icsFetchInterval}
onChange={(settings) => {
setRoomInput({
...room,
icsUrl:
settings.ics_url !== undefined
? settings.ics_url
: room.icsUrl,
icsEnabled:
settings.ics_enabled !== undefined
? settings.ics_enabled
: room.icsEnabled,
icsFetchInterval:
settings.ics_fetch_interval !== undefined
? settings.ics_fetch_interval
: room.icsFetchInterval,
});
}}
isOwner={true}
isEditing={isEditing}
/>
</Tabs.Content>
<Tabs.Content value="share" pt={6}> <Tabs.Content value="share" pt={6}>
<Field.Root> <Field.Root>
<Checkbox.Root <Checkbox.Root
@@ -675,7 +655,6 @@ export default function RoomsList() {
</Checkbox.Label> </Checkbox.Label>
</Checkbox.Root> </Checkbox.Root>
</Field.Root> </Field.Root>
<Field.Root mt={4}> <Field.Root mt={4}>
<Field.Label>Zulip stream</Field.Label> <Field.Label>Zulip stream</Field.Label>
<Select.Root <Select.Root
@@ -711,7 +690,6 @@ export default function RoomsList() {
</Select.Positioner> </Select.Positioner>
</Select.Root> </Select.Root>
</Field.Root> </Field.Root>
<Field.Root mt={4}> <Field.Root mt={4}>
<Field.Label>Zulip topic</Field.Label> <Field.Label>Zulip topic</Field.Label>
<Select.Root <Select.Root
@@ -750,10 +728,10 @@ export default function RoomsList() {
<Field.Label>Webhook URL</Field.Label> <Field.Label>Webhook URL</Field.Label>
<Input <Input
name="webhookUrl" name="webhookUrl"
type="url"
placeholder="https://example.com/webhook" placeholder="https://example.com/webhook"
value={room.webhookUrl} value={room.webhookUrl}
onChange={handleRoomChange} onChange={handleRoomChange}
enterKeyHint="next"
/> />
<Field.HelperText> <Field.HelperText>
Optional: URL to receive notifications when transcripts Optional: URL to receive notifications when transcripts
@@ -832,7 +810,8 @@ export default function RoomsList() {
maxWidth: "100%", maxWidth: "100%",
padding: "8px", padding: "8px",
borderRadius: "4px", borderRadius: "4px",
backgroundColor: webhookTestResult.startsWith( backgroundColor:
webhookTestResult.startsWith(
SUCCESS_EMOJI, SUCCESS_EMOJI,
) )
? "#f0fdf4" ? "#f0fdf4"
@@ -849,15 +828,49 @@ export default function RoomsList() {
</> </>
)} )}
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="calendar" pt={6}>
<Field.Root>
<ICSSettings
roomName={
room.name ? parseNonEmptyString(room.name) : null
}
icsUrl={room.icsUrl}
icsEnabled={room.icsEnabled}
icsFetchInterval={room.icsFetchInterval}
onChange={(settings) => {
setRoomInput({
...room,
icsUrl:
settings.ics_url !== undefined
? settings.ics_url
: room.icsUrl,
icsEnabled:
settings.ics_enabled !== undefined
? settings.ics_enabled
: room.icsEnabled,
icsFetchInterval:
settings.ics_fetch_interval !== undefined
? settings.ics_fetch_interval
: room.icsFetchInterval,
});
}}
isOwner={true}
isEditing={isEditing}
/>
</Field.Root>
</Tabs.Content>
</Tabs.Root> </Tabs.Root>
</form>
</Dialog.Body> </Dialog.Body>
<Dialog.Footer> <Dialog.Footer>
<Button variant="ghost" onClick={handleCloseDialog}> <Button variant="ghost" onClick={handleCloseDialog}>
Cancel Cancel
</Button> </Button>
<Button <Button
type="submit"
colorPalette="primary" colorPalette="primary"
onClick={handleSaveRoom} form="room-form"
disabled={ disabled={
!room.name || (room.zulipAutoPost && !room.zulipTopic) !room.name || (room.zulipAutoPost && !room.zulipTopic)
} }