diff --git a/package.json b/package.json index 88d130d..9c04918 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devprotocol/huddle01-clubs-plugin", - "version": "0.1.4", + "version": "0.1.5", "type": "module", "description": "Repository for using Clubs-Huddle01 Plugin", "main": "dist/index.js", diff --git a/src/ApiHandler.ts b/src/ApiHandler.ts index 7563d78..d31c2c7 100644 --- a/src/ApiHandler.ts +++ b/src/ApiHandler.ts @@ -6,6 +6,9 @@ export type MeetingRequestJson = Readonly<{ description: string muteOnEntry: boolean videoOnEntry: boolean + startTime: string + expiryTime: string + tokenGated: boolean }> const API_KEY = process.env.HUDDLE_API_KEY @@ -14,23 +17,55 @@ export const meetingHandler = async ({ request, }: Readonly<{ request: Request }>) => { // eslint-disable-next-line functional/no-expression-statements - const { hostWallets, roomType, description, muteOnEntry, videoOnEntry } = - (await request.json()) as MeetingRequestJson + const { + hostWallets, + roomType, + description, + muteOnEntry, + videoOnEntry, + startTime, + expiryTime, + tokenGated, + } = (await request.json()) as MeetingRequestJson // eslint-disable-next-line functional/no-expression-statements - console.log(hostWallets, roomType, description, muteOnEntry, videoOnEntry) + console.log( + hostWallets, + roomType, + description, + muteOnEntry, + videoOnEntry, + startTime, + expiryTime, + tokenGated, + ) + const requestBody = tokenGated + ? { + title: 'Club-Huddle-Plugin:Token Gated Meeting Room', + tokenType: 'ERC721', + chain: 'POLYGON', + contractAddress: ['0x89904De861CDEd2567695271A511B3556659FfA2'], + roomType, + description, + muteOnEntry, + videoOnEntry, + hostWallets, + startTime, + expiryTime, + } + : { + title: 'Club-Huddle-Plugin:Meeting Room', + roomType, + description, + muteOnEntry, + videoOnEntry, + hostWallets, + startTime, + expiryTime, + } + const response = await axios.post( 'https://api.huddle01.com/api/v1/create-iframe-room', - { - title: 'Token Gated', - tokenType: 'ERC721', - chain: 'POLYGON', - contractAddress: ['0x89904De861CDEd2567695271A511B3556659FfA2'], - roomType: roomType, - description: description, - muteOnEntry: muteOnEntry, - videoOnEntry: videoOnEntry, - hostWallets: hostWallets, - }, + requestBody, { headers: { 'Content-Type': 'application/json', diff --git a/src/components/Button.vue b/src/components/Button.vue index 60f6bf1..688fddd 100644 --- a/src/components/Button.vue +++ b/src/components/Button.vue @@ -30,6 +30,6 @@ onClickToolbar('onRemoveMeetEvent', () => { type="button" @click="onClickImage" > - + diff --git a/src/components/Icons/VideoCamera.vue b/src/components/Icons/VideoCamera.vue index 07fc847..e9d74b9 100644 --- a/src/components/Icons/VideoCamera.vue +++ b/src/components/Icons/VideoCamera.vue @@ -1,13 +1,16 @@ + + + diff --git a/src/components/Meeting/Form.vue b/src/components/Meeting/Form.vue index 3c1e9d4..80c8f30 100644 --- a/src/components/Meeting/Form.vue +++ b/src/components/Meeting/Form.vue @@ -1,5 +1,5 @@ { AUDIO + + + + Start Time + + + + + + Expiry Time + + + + - Entry Settings - Camera - - - - - Mute - - - - + Entry Settings + + + Camera + + + + + + + Mute + + + + + + + Gated + + + + + + + import VideoCamera from '../Icons/VideoCamera.vue' +import { computed } from 'vue' +import { formatISOTimestamp, isFutureTimestamp } from '../../fixtures' const props = defineProps<{ redirectToUrl: (meetingLink: string | undefined) => void meetingLink: string | undefined roomType: string + startTime: string | undefined + expiryTime: string | undefined }>() + +const startTimeValid = computed( + () => props.startTime && !isNaN(new Date(props.startTime).getTime()), +) +const expiryTimeValid = computed( + () => props.expiryTime && !isNaN(new Date(props.expiryTime).getTime()), +) + +const meetingHasStarted = computed( + () => startTimeValid.value && !isFutureTimestamp(props.startTime as string), +) +const meetingIsActive = computed( + () => + startTimeValid.value && + (!expiryTimeValid.value || isFutureTimestamp(props.expiryTime as string)), +) + +const roomTypeDescription = computed(() => { + return `${props.roomType === 'VIDEO' ? 'Video Call' : 'Audio Space'}` +}) + +const meetingStatusMessage = computed(() => { + if (!startTimeValid.value) { + return 'Unkwown Start Time' + } + + if (meetingHasStarted.value && !meetingIsActive.value) { + return 'Expired' + } + + if (!meetingHasStarted.value) { + return `Starts @ ${formatISOTimestamp(props.startTime as string)}` + } + + return `Ends @ ${formatISOTimestamp(props.expiryTime as string)}` +}) { @@ -16,9 +60,16 @@ const props = defineProps<{ } " > - + - Join {{ roomType === 'VIDEO' ? 'Video' : 'Audio' }} - {{ roomType === 'VIDEO' ? 'Call' : 'Space' }} + Join {{ roomTypeDescription }} + + + {{ roomTypeDescription }} + {{ meetingStatusMessage }} + diff --git a/src/components/feed-after-post-content.vue b/src/components/feed-after-post-content.vue index 157d730..d4b1a5b 100644 --- a/src/components/feed-after-post-content.vue +++ b/src/components/feed-after-post-content.vue @@ -49,6 +49,8 @@ const redirectToUrl = (url: string | undefined) => { :redirectToUrl="redirectToUrl" :meetingLink="currentMeet.meetingLink" :roomType="currentMeet.roomType" + :startTime="currentMeet.startTime" + :expiryTime="currentMeet.expiryTime" /> diff --git a/src/fixtures/index.ts b/src/fixtures/index.ts new file mode 100644 index 0000000..604d1af --- /dev/null +++ b/src/fixtures/index.ts @@ -0,0 +1,41 @@ +export const formatUnixTimestampToISO = (timestamp: number) => { + const date = new Date(timestamp * 1000) // Convert seconds to milliseconds + const year = date.getFullYear() + const month = (date.getMonth() + 1).toString().padStart(2, '0') + const day = date.getDate().toString().padStart(2, '0') + const hours = date.getHours().toString().padStart(2, '0') + const minutes = date.getMinutes().toString().padStart(2, '0') + const dateObject = new Date(`${year}-${month}-${day}T${hours}:${minutes}`) + return dateObject.toISOString() +} + +export const formatISOTimestamp = (isoTimestamp: string) => { + const date = new Date(isoTimestamp) + + const day = String(date.getDate()).padStart(2, '0') + const month = String(date.getMonth() + 1).padStart(2, '0') // Months are 0-indexed in JavaScript + const year = date.getFullYear() + + const hours12 = date.getHours() % 12 || 12 // Converts 0 to 12 for midnight + const amPm = date.getHours() >= 12 ? 'PM' : 'AM' + + const hours = String(hours12).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + + const timeZone = new Intl.DateTimeFormat('en-US', { + timeZoneName: 'short', + }).resolvedOptions().timeZone + + return `${hours}:${minutes} ${amPm} (${day}:${month}:${year}) ${timeZone}` +} + +export const isFutureTimestamp = (isoTimestamp: string) => { + if (isoTimestamp === undefined) { + return true + } + const inputDate = new Date(isoTimestamp) + + const currentDate = new Date() + // Return true if the input date is in the future, false otherwise + return inputDate > currentDate +} diff --git a/src/types.ts b/src/types.ts index 0af2bbf..0d2d587 100644 --- a/src/types.ts +++ b/src/types.ts @@ -20,4 +20,6 @@ export interface Meet { muteOnEntry: boolean videoOnEntry: boolean meetingLink: string | undefined + startTime: string | undefined + expiryTime: string | undefined }
Entry Settings
Camera
Mute
Gated