mirror of
https://github.com/discourse/discourse.git
synced 2026-08-06 13:08:40 +08:00
This commit adds support for embedding a Zoom webinar livestream directly within a Discourse Event via a URL in the location field of the event in the topic. Admins must supply a Zoom API key and secret in the plugin settings for this functionality to be available using these settings: * livestream_zoom_enabled * livestream_zoom_sdk_key * livestream_zoom_sdk_secret When a topic is opened with a Zoom URL in the event location, and the Livestream toggle is enabled, the Zoom library dependencies will be loaded asyncrhonously and the Zoom webinar will be embedded within the topic page using the Zoom component view. On mobile, a fullscreen Zoom client using the Zoom client view will be used, with a floating Chat button that opens the Discourse livestream chat in a modal which slides up from the bottom of the screen in an overlay. This client view is at `/t/:id/:slug/zoom` ### Webinar event date/time not reached <img width="880" height="612" alt="image" src="https://github.com/user-attachments/assets/4b112b59-eed0-4d63-b166-da825d8ebd0b" /> ### Within 30 minutes before and 10 minutes after the event date/time <img width="879" height="613" alt="image" src="https://github.com/user-attachments/assets/373191f8-6245-4b41-b2a8-161c935166be" /> ### Attendee joined before host, we will retry join every 30s <img width="735" height="817" alt="image" src="https://github.com/user-attachments/assets/519afa51-8099-4af0-954f-08da4c7e3297" /> ### Attendee joined <img width="733" height="831" alt="image" src="https://github.com/user-attachments/assets/f633d21f-956e-4bec-a2f8-f7dc8b95d974" /> --------- Co-authored-by: David Taylor <david@taylorhq.com> Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
156 lines
4.2 KiB
Text
Vendored
156 lines
4.2 KiB
Text
Vendored
import { getOwner } from "@ember/owner";
|
|
import { fillIn, render } from "@ember/test-helpers";
|
|
import { module, test } from "qunit";
|
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
|
import PostEventBuilder from "../../discourse/components/modal/post-event-builder";
|
|
import DiscoursePostEventEvent from "../../discourse/models/discourse-post-event-event";
|
|
|
|
module("Integration | Component | Modal | PostEventBuilder", function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
const store = getOwner(this).lookup("service:store");
|
|
this.user = store.createRecord("user", {
|
|
username: "tom",
|
|
id: 1,
|
|
admin: true,
|
|
});
|
|
|
|
getOwner(this).unregister("service:current-user");
|
|
getOwner(this).register("service:current-user", this.user, {
|
|
instantiate: false,
|
|
});
|
|
});
|
|
|
|
test("advanced screen renders an existing image upload as its URL", async function (assert) {
|
|
const event = DiscoursePostEventEvent.create({
|
|
name: "My event",
|
|
starts_at: "2022-07-01T10:00:00Z",
|
|
ends_at: "2022-07-01T11:00:00Z",
|
|
timezone: "UTC",
|
|
status: "public",
|
|
reminders: [],
|
|
raw_invitees: [],
|
|
custom_fields: {},
|
|
image_upload: {
|
|
url: "/uploads/default/original/1X/test-event-image.png",
|
|
short_url: "upload://test-event-image",
|
|
},
|
|
});
|
|
|
|
const model = {
|
|
event,
|
|
initialScreen: "advanced",
|
|
onUpdate: () => {},
|
|
toolbarEvent: {},
|
|
};
|
|
const closeModal = () => {};
|
|
|
|
await render(
|
|
<template>
|
|
<PostEventBuilder
|
|
@inline={{true}}
|
|
@model={{model}}
|
|
@closeModal={{closeModal}}
|
|
/>
|
|
</template>
|
|
);
|
|
|
|
assert
|
|
.dom(`[data-name="imageUpload"] .file-uploader`)
|
|
.hasClass("has-image", "the image control shows the uploaded image");
|
|
assert
|
|
.dom(`[data-name="imageUpload"] .file-uploader__preview`)
|
|
.hasAttribute(
|
|
"style",
|
|
/url\(\/uploads\/default\/original\/1X\/test-event-image\.png\)/,
|
|
"renders the upload url, not the raw object"
|
|
);
|
|
});
|
|
|
|
test("advanced screen renders a custom field whose name contains a dash", async function (assert) {
|
|
this.siteSettings.discourse_post_event_allowed_custom_fields = "field-aa";
|
|
|
|
const event = DiscoursePostEventEvent.create({
|
|
name: "My event",
|
|
starts_at: "2022-07-01T10:00:00Z",
|
|
ends_at: "2022-07-01T11:00:00Z",
|
|
timezone: "UTC",
|
|
status: "public",
|
|
reminders: [],
|
|
raw_invitees: [],
|
|
custom_fields: {},
|
|
});
|
|
|
|
const model = {
|
|
event,
|
|
initialScreen: "advanced",
|
|
onUpdate: () => {},
|
|
toolbarEvent: {},
|
|
};
|
|
const closeModal = () => {};
|
|
|
|
await render(
|
|
<template>
|
|
<PostEventBuilder
|
|
@inline={{true}}
|
|
@model={{model}}
|
|
@closeModal={{closeModal}}
|
|
/>
|
|
</template>
|
|
);
|
|
|
|
assert
|
|
.dom(`[data-name="customFields.field_aa"] input`)
|
|
.exists("renders the dashed custom field without crashing");
|
|
});
|
|
|
|
test("clears livestream when the location stops being a URL", async function (assert) {
|
|
this.siteSettings.chat_enabled = true;
|
|
|
|
const event = DiscoursePostEventEvent.create({
|
|
name: "My event",
|
|
starts_at: "2022-07-01T10:00:00Z",
|
|
ends_at: "2022-07-01T11:00:00Z",
|
|
timezone: "UTC",
|
|
status: "public",
|
|
reminders: [],
|
|
raw_invitees: [],
|
|
custom_fields: {},
|
|
location: "https://zoom.us/j/123456789",
|
|
livestream: true,
|
|
});
|
|
|
|
const model = {
|
|
event,
|
|
initialScreen: "advanced",
|
|
onUpdate: () => {},
|
|
toolbarEvent: {},
|
|
};
|
|
const closeModal = () => {};
|
|
|
|
await render(
|
|
<template>
|
|
<PostEventBuilder
|
|
@inline={{true}}
|
|
@model={{model}}
|
|
@closeModal={{closeModal}}
|
|
/>
|
|
</template>
|
|
);
|
|
|
|
assert
|
|
.dom(`[data-name="livestream"]`)
|
|
.exists("the livestream field shows while the location is a URL");
|
|
|
|
await fillIn(`[data-name="location"] input`, "Room 5");
|
|
|
|
assert
|
|
.dom(`[data-name="livestream"]`)
|
|
.doesNotExist("the livestream field is hidden for a non-URL location");
|
|
assert.false(
|
|
event.livestream,
|
|
"livestream is reset so it is not submitted for a non-URL location"
|
|
);
|
|
});
|
|
});
|