mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
This does a handful of things to improve the livestream chat experience: 1. RSVP prompt replaces the default join button. A new chat-channel-preview-card-content plugin outlet in core chat lets the calendar plugin's livestream-rsvp connector fill the redesigned preview card's content slots. For users who can RSVP, it swaps the default join button for a going button for the event. 2. Pinned reference message linking back to the event topic. When a livestream channel is created, a system message is posted and pinned, and its id is stored in a new `reference_message_id` column. It's hidden via CSS when the chat is embedded in that same topic because it would be redundant. 3. Access control for private events. `chat_channel_id` is only serialized to users who can access the event's chat (invitees/invited groups, or admins) 4. If the channel is already open and expanded in the chat drawer, the embedded chat (and the livestream page layout) are suppressed. 5. Livestream channels are created with the spiral calendar emoji 🗓️ as their icon. I also converted core chat's preview card from viewport media queries to container queries so its layout responds to the width of the chat column it's rendered in rather than the viewport. With livestream sidebar <img width="2962" height="1726" alt="image" src="https://github.com/user-attachments/assets/71dc3d3f-35e5-4a0d-9103-92a03d4bb08e" /> Isolated chat when you're not in the livestream topic <img width="2560" height="1726" alt="image" src="https://github.com/user-attachments/assets/51cef0b8-85b4-4be8-a4f3-bf669f4be4fb" /> --------- Co-authored-by: Martin Brennan <martin@discourse.org>
230 lines
6.6 KiB
Ruby
Vendored
230 lines
6.6 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe TopicViewSerializer do
|
|
subject(:serializer) do
|
|
described_class.new(TopicView.new(topic), scope: Guardian.new, root: false)
|
|
end
|
|
|
|
let(:topic) { Fabricate(:topic) }
|
|
let(:first_post) { Fabricate(:post, topic:) }
|
|
let(:parsed_json) { JSON.parse(serializer.to_json) }
|
|
|
|
before do
|
|
freeze_time(Time.utc(2020, 4, 24, 14, 10))
|
|
Jobs.run_immediately!
|
|
SiteSetting.calendar_enabled = true
|
|
SiteSetting.discourse_post_event_enabled = true
|
|
end
|
|
|
|
context "without timezone" do
|
|
before do
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: 1.hour.from_now,
|
|
original_ends_at: 2.hours.from_now,
|
|
)
|
|
end
|
|
|
|
describe "#event_starts_at" do
|
|
it "returns the start time of the event in proper format" do
|
|
expect(parsed_json["event_starts_at"]).to eq("2020-04-24T15:10:00.000Z")
|
|
end
|
|
end
|
|
|
|
describe "#event_ends_at" do
|
|
it "returns the end time of the event in proper format" do
|
|
expect(parsed_json["event_ends_at"]).to eq("2020-04-24T16:10:00.000Z")
|
|
end
|
|
end
|
|
|
|
describe "#event_timezone" do
|
|
it "is not included when event has no timezone" do
|
|
expect(parsed_json).not_to have_key("event_timezone")
|
|
end
|
|
end
|
|
|
|
describe "#event_show_local_time" do
|
|
it "returns false when show_local_time is not set" do
|
|
expect(parsed_json["event_show_local_time"]).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with timezone and show_local_time true" do
|
|
before do
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: 1.hour.from_now,
|
|
original_ends_at: 2.hours.from_now,
|
|
timezone: "Australia/Sydney",
|
|
show_local_time: true,
|
|
)
|
|
end
|
|
|
|
describe "#event_timezone" do
|
|
it "returns the timezone of the event" do
|
|
expect(parsed_json["event_timezone"]).to eq("Australia/Sydney")
|
|
end
|
|
end
|
|
|
|
describe "#event_show_local_time" do
|
|
it "returns true when show_local_time is set" do
|
|
expect(parsed_json["event_show_local_time"]).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with timezone and show_local_time false" do
|
|
before do
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: 1.hour.from_now,
|
|
original_ends_at: 2.hours.from_now,
|
|
timezone: "Australia/Sydney",
|
|
show_local_time: false,
|
|
)
|
|
end
|
|
|
|
describe "#event_timezone" do
|
|
it "returns the timezone of the event" do
|
|
expect(parsed_json["event_timezone"]).to eq("Australia/Sydney")
|
|
end
|
|
end
|
|
|
|
describe "#event_show_local_time" do
|
|
it "returns false when show_local_time is explicitly false" do
|
|
expect(parsed_json["event_show_local_time"]).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with all-day event" do
|
|
before do
|
|
SiteSetting.display_post_event_date_on_topic_title = true
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: Time.utc(2020, 4, 25),
|
|
original_ends_at: Time.utc(2020, 4, 27),
|
|
all_day: true,
|
|
)
|
|
end
|
|
|
|
describe "#event_all_day" do
|
|
it "returns true" do
|
|
expect(parsed_json["event_all_day"]).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "without all-day event" do
|
|
before do
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: 1.hour.from_now,
|
|
original_ends_at: 2.hours.from_now,
|
|
)
|
|
end
|
|
|
|
describe "#event_all_day" do
|
|
it "is not included" do
|
|
expect(parsed_json).not_to have_key("event_all_day")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#chat_channel_id" do
|
|
fab!(:category)
|
|
fab!(:viewer, :user)
|
|
let(:topic) { Fabricate(:topic, category:) }
|
|
|
|
before do
|
|
SiteSetting.chat_enabled = true
|
|
# Don't do onebox-warming job so it doesn't make a real request
|
|
Jobs.run_later!
|
|
first_post
|
|
end
|
|
|
|
def create_event(livestream:, status: :public, raw_invitees: nil)
|
|
DiscoursePostEvent::Event.create!(
|
|
id: first_post.id,
|
|
original_starts_at: 1.hour.from_now,
|
|
original_ends_at: 2.hours.from_now,
|
|
location: "https://example.com/live",
|
|
status: DiscoursePostEvent::Event.statuses[status],
|
|
raw_invitees:,
|
|
livestream:,
|
|
)
|
|
end
|
|
|
|
def parsed_json_for(user)
|
|
JSON.parse(
|
|
described_class.new(TopicView.new(topic), scope: Guardian.new(user), root: false).to_json,
|
|
)
|
|
end
|
|
|
|
it "is included while the event is a livestream" do
|
|
create_event(livestream: true)
|
|
|
|
expect(topic.topic_chat_channel).to be_present
|
|
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
|
|
topic.topic_chat_channel.chat_channel_id,
|
|
)
|
|
end
|
|
|
|
it "is not included once livestream is disabled, even if the channel row remains" do
|
|
event = create_event(livestream: true)
|
|
event.update!(livestream: false)
|
|
|
|
expect(topic.reload.topic_chat_channel).to be_present
|
|
expect(parsed_json_for(viewer)).not_to have_key("chat_channel_id")
|
|
end
|
|
|
|
it "is not included for users who cannot join the event" do
|
|
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
|
|
|
|
expect(parsed_json_for(viewer)).not_to have_key("chat_channel_id")
|
|
end
|
|
|
|
it "is not included for anonymous users on private events" do
|
|
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
|
|
|
|
expect(parsed_json_for(nil)).not_to have_key("chat_channel_id")
|
|
end
|
|
|
|
it "is included for anonymous users on public events" do
|
|
create_event(livestream: true)
|
|
|
|
expect(parsed_json_for(nil)["chat_channel_id"]).to eq(
|
|
topic.topic_chat_channel.chat_channel_id,
|
|
)
|
|
end
|
|
|
|
it "is still included once the event is expired" do
|
|
create_event(livestream: true)
|
|
|
|
freeze_time(3.hours.from_now)
|
|
|
|
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
|
|
topic.topic_chat_channel.chat_channel_id,
|
|
)
|
|
end
|
|
|
|
it "is included for users in a group that can join the event" do
|
|
group = Fabricate(:group)
|
|
group.add(viewer)
|
|
create_event(livestream: true, status: :private, raw_invitees: [group.name])
|
|
|
|
expect(parsed_json_for(viewer)["chat_channel_id"]).to eq(
|
|
topic.topic_chat_channel.chat_channel_id,
|
|
)
|
|
end
|
|
|
|
it "is included for admins even when they cannot join the event" do
|
|
create_event(livestream: true, status: :private, raw_invitees: ["some_group"])
|
|
|
|
expect(parsed_json_for(Fabricate(:admin))["chat_channel_id"]).to eq(
|
|
topic.topic_chat_channel.chat_channel_id,
|
|
)
|
|
end
|
|
end
|
|
end
|