Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lang/en/rooms.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,10 @@
'invalid_personal_link' => 'This personalised room link is invalid.',
'invitation' => [
'code' => 'Access code',
'copied' => 'Copied access information to clipboard',
'copy' => 'Copy',
'link_copied' => 'Copied link to clipboard',
'message_copied' => 'Copied invitation message to clipboard',
'copy_link' => 'Copy link',
'copy_invitation_message' => 'Copy invitation message',
'link' => 'Link',
'room' => 'Join ":roomname" with :platform',
'share' => 'Share',
Expand Down
87 changes: 41 additions & 46 deletions resources/js/components/RoomShareButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,41 @@
{{ $t("rooms.invitation.title") }}
</legend>
<div class="grow">
<IconField icon-position="left">
<InputIcon>
<InputGroup>
<InputGroupAddon>
<i
v-tooltip="$t('rooms.invitation.link')"
class="fa-solid fa-link"
:aria-label="$t('rooms.invitation.link')"
/>
</InputIcon>
</InputGroupAddon>
<InputText
id="invitationLink"
class="w-full text-ellipsis border-surface-0 shadow-none dark:border-surface-900"
:aria-label="$t('rooms.invitation.link')"
readonly
:value="roomUrl"
@focus="$event.target.select()"
/>
</IconField>

<IconField v-if="room.access_code" icon-position="left">
<InputIcon>
<i
v-tooltip="$t('rooms.invitation.code')"
class="fa-solid fa-key"
:aria-label="$t('rooms.invitation.code')"
<InputGroupAddon>
<Button
severity="secondary"
data-test="room-copy-invitation-link-button"
:label="$t('rooms.invitation.copy_link')"
icon="fa-solid fa-copy"
@click="copyLink"
/>
</InputIcon>
<InputText
id="invitationCode"
class="w-full border-surface-0 shadow-none dark:border-surface-900"
:aria-label="$t('rooms.invitation.code')"
readonly
:value="formattedAccessCode"
@focus="$event.target.select()"
/>
</IconField>
</InputGroupAddon>
</InputGroup>
</div>

<Button
data-test="room-copy-invitation-message-button"
:label="$t('rooms.invitation.copy_invitation_message')"
icon="fa-solid fa-copy"
autofocus
@click="copyInvitationText"
/>
</fieldset>
<Button
data-test="room-copy-invitation-button"
:label="$t('rooms.invitation.copy')"
icon="fa-solid fa-copy"
autofocus
@click="copyInvitationText"
/>
</div>
</Popover>
</template>
Expand Down Expand Up @@ -89,34 +80,38 @@
});

function copyInvitationText() {
let message =
navigator.clipboard.writeText(invitationMessage.value);
toast.success(t("rooms.invitation.message_copied"));
}

function copyLink() {
navigator.clipboard.writeText(roomUrl.value);
toast.success(t("rooms.invitation.link_copied"));
}

const invitationMessage = computed(() => {
return (
t("rooms.invitation.room", {
roomname: props.room.name,
platform: settingsStore.getSetting("general.name"),
}) + "\n";
message += t("rooms.invitation.link") + ": " + roomUrl.value;
// If room has access code, include access code in the message
if (props.room.access_code) {
message +=
"\n" + t("rooms.invitation.code") + ": " + formattedAccessCode.value;
}
navigator.clipboard.writeText(message);
toast.success(t("rooms.invitation.copied"));
}
}) +
"\n" +
t("rooms.invitation.link") +
": " +
roomUrl.value
);
});

const roomUrl = computed(() => {
return (
settingsStore.getSetting("general.base_url") +
router.resolve({
name: "rooms.view",
params: { id: props.room.id },
hash: props.room.access_code
? "#accessCode=" + props.room.access_code
: "",

Check warning on line 113 in resources/js/components/RoomShareButton.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/components/RoomShareButton.vue#L113

Added line #L113 was not covered by tests
}).href
);
});

const formattedAccessCode = computed(() => {
return String(props.room.access_code)
.match(/.{1,3}/g)
.join("-");
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ function copyLink() {
settingsStore.getSetting("general.base_url") +
router.resolve({
name: "rooms.view",
params: { id: props.roomId, token: props.token },
params: { id: props.roomId },
hash: "#token=" + props.token,
}).href;
navigator.clipboard.writeText(link);
toast.info(
Expand Down
19 changes: 17 additions & 2 deletions resources/js/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,29 @@ export const routes = [
},

{
path: "/rooms/:id/:token?",
path: "/rooms/:id",
name: "rooms.view",
component: RoomView,
meta: { redirectBackAfterLogin: true },
props: (route) => {
return {
id: route.params.id,
token: route.params.token,
};
},
},
{
path: "/rooms/:id/:token",
redirect: (route) => {
// Parse hash parameters
const searchParams = new URLSearchParams(route.hash.substring(1));

// Append or overwrite token parameter
searchParams.set("token", route.params.token);

return {
name: "rooms.view",
params: { id: route.params.id },
hash: "#" + searchParams.toString(),
};
},
},
Expand Down
73 changes: 59 additions & 14 deletions resources/js/views/RoomsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
:record-attendance="room.record_attendance"
:record="room.record"
:can-start="room.can_start"
:token="props.token"
:token="token"
:access-code="accessCode"
@invalid-code="handleInvalidCode"
@invalid-token="handleInvalidToken"
Expand Down Expand Up @@ -242,24 +242,24 @@
import RoomShareButton from "../components/RoomShareButton.vue";
import EventBus from "../services/EventBus.js";
import { EVENT_FORBIDDEN, EVENT_UNAUTHORIZED } from "../constants/events.js";
import { useUrlSearchParams } from "@vueuse/core";

const props = defineProps({
id: {
type: String,
required: true,
},
token: {
type: String,
default: null,
},
});

const hashParams = useUrlSearchParams("hash-params");

const reloadInterval = ref(null);
const loading = ref(false); // Room settings/details loading
const room = ref(null); // Room object
const accessCode = ref(null); // Access code to use for requests
const accessCodeInput = ref(""); // Access code input modal
const accessCodeInvalid = ref(null); // Is access code invalid
const token = ref(null); // Room token used for personalised room access links
const roomLoading = ref(false); // Room loading indicator for initial load
const tokenInvalid = ref(false); // Room token is invalid
const guestsNotAllowed = ref(false); // Access to room was forbidden
Expand All @@ -274,11 +274,20 @@
const api = useApi();

onMounted(() => {
// Prevent authenticated users from using a room token
if (props.token && authStore.isAuthenticated) {
toast.info(t("app.flash.guests_only"));
router.replace({ name: "home" });
return;
if (hashParams.accessCode) {
accessCodeInput.value = getHashParamValue("accessCode");
hashParams.accessCode = null;
login();

Check warning on line 280 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L278-L280

Added lines #L278 - L280 were not covered by tests
}

if (hashParams.token) {
// Prevent authenticated users from using a room token
if (hashParams.token && authStore.isAuthenticated) {
toast.info(t("app.flash.guests_only"));
router.replace({ name: "home" });
return;
}
token.value = getHashParamValue("token");
}

EventBus.on(EVENT_FORBIDDEN, reload);
Expand All @@ -294,6 +303,41 @@
clearInterval(reloadInterval.value);
});

watch(
() => hashParams.accessCode,
(value) => {
if (!value) return;
accessCodeInput.value = getHashParamValue("accessCode");
hashParams.accessCode = null;
login();

Check warning on line 312 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L308-L312

Added lines #L308 - L312 were not covered by tests
},
);

watch(
() => hashParams.token,
(value) => {
if (!value) return;

Check warning on line 319 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L318-L319

Added lines #L318 - L319 were not covered by tests

// Prevent authenticated users from using a room token
if (hashParams.token && authStore.isAuthenticated) {
toast.info(t("app.flash.guests_only"));
router.replace({ name: "home" });
return;

Check warning on line 325 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L322-L325

Added lines #L322 - L325 were not covered by tests
}

token.value = getHashParamValue("token");
clearInterval(reloadInterval.value);
tokenInvalid.value = false;
load();

Check warning on line 331 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L328-L331

Added lines #L328 - L331 were not covered by tests
},
);

function getHashParamValue(param) {
return Array.isArray(hashParams[param])
? hashParams[param][0]

Check warning on line 337 in resources/js/views/RoomsView.vue

View check run for this annotation

Codecov / codecov/patch

resources/js/views/RoomsView.vue#L337

Added line #L337 was not covered by tests
: hashParams[param];
}

/**
* Reload room details in a set interval, change in the .env
*/
Expand Down Expand Up @@ -362,8 +406,8 @@
// Build room api url, include access code if set
const config = {};

if (props.token) {
config.headers = { Token: props.token };
if (token.value) {
config.headers = { Token: token.value };
} else if (accessCode.value != null) {
config.headers = { "Access-Code": accessCode.value };
}
Expand Down Expand Up @@ -433,8 +477,8 @@
// Build room api url, include access code if set
const config = {};

if (props.token) {
config.headers = { Token: props.token };
if (token.value) {
config.headers = { Token: token.value };
} else if (accessCode.value != null) {
config.headers = { "Access-Code": accessCode.value };
}
Expand Down Expand Up @@ -521,6 +565,7 @@
function login() {
// Parse to int
accessCode.value = parseInt(accessCodeInput.value.replace(/[-]/g, ""));

// Reload the room with an access code
reload();
}
Expand Down
Loading
Loading