screen layout and poll fix
This commit is contained in:
@@ -143,10 +143,10 @@ sudo DEPLOY_NGINX=1 ./scripts/deploy-vps.sh
|
|||||||
For current `config.js`, branding, and asset changes, an nginx reload is enough. JWT/auth and Prosody plugin changes require restarting Prosody and Jicofo, and usually Jitsi Videobridge.
|
For current `config.js`, branding, and asset changes, an nginx reload is enough. JWT/auth and Prosody plugin changes require restarting Prosody and Jicofo, and usually Jitsi Videobridge.
|
||||||
|
|
||||||
The `politictalk_roles` Prosody module should be configured with the PgApi
|
The `politictalk_roles` Prosody module should be configured with the PgApi
|
||||||
inactive-room callback from `templates/prosody-token-auth.cfg.lua.example`.
|
inactive-room and occupancy callbacks from
|
||||||
That callback clears the event `meetingCode` when the last host leaves, so
|
`templates/prosody-token-auth.cfg.lua.example`. The inactive callback clears the
|
||||||
participants remain on the platform waiting screen instead of reaching a
|
event `meetingCode` when the last host leaves, and the occupancy callback keeps
|
||||||
blocked Jitsi room.
|
the platform room cards updated with the current participant count.
|
||||||
|
|
||||||
## Token Auth Rollout
|
## Token Auth Rollout
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"inviteDomain": "politictalk.parallelglobe.io",
|
"inviteDomain": "politictalk.parallelglobe.io",
|
||||||
"backgroundColor": "#101820",
|
"backgroundColor": "#101820",
|
||||||
|
"pollCreationRequiresPermission": true,
|
||||||
"logoClickUrl": "https://parallelglobe.io/politictalk",
|
"logoClickUrl": "https://parallelglobe.io/politictalk",
|
||||||
"logoImageUrl": "/images/politictalk/pgLogo.svg",
|
"logoImageUrl": "/images/politictalk/pgLogo.svg",
|
||||||
"premeetingBackground": "url(/images/politictalk/pg_bg.png)",
|
"premeetingBackground": "url(/images/politictalk/pg_bg.png)",
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ services:
|
|||||||
prosody:
|
prosody:
|
||||||
environment:
|
environment:
|
||||||
- POLITICTALK_ROOM_INACTIVE_CALLBACK_URL=${POLITICTALK_ROOM_INACTIVE_CALLBACK_URL:-http://host.docker.internal:9000/events/politictalk/jitsi/room-inactive}
|
- POLITICTALK_ROOM_INACTIVE_CALLBACK_URL=${POLITICTALK_ROOM_INACTIVE_CALLBACK_URL:-http://host.docker.internal:9000/events/politictalk/jitsi/room-inactive}
|
||||||
|
- POLITICTALK_ROOM_OCCUPANCY_CALLBACK_URL=${POLITICTALK_ROOM_OCCUPANCY_CALLBACK_URL:-http://host.docker.internal:9000/events/politictalk/jitsi/room-occupancy}
|
||||||
- POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET=${POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET:-politictalk-local-lifecycle-secret}
|
- POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET=${POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET:-politictalk-local-lifecycle-secret}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ local ROOM_INACTIVE_CALLBACK_URL = module:get_option_string(
|
|||||||
"politictalk_room_inactive_callback_url",
|
"politictalk_room_inactive_callback_url",
|
||||||
os.getenv("POLITICTALK_ROOM_INACTIVE_CALLBACK_URL")
|
os.getenv("POLITICTALK_ROOM_INACTIVE_CALLBACK_URL")
|
||||||
);
|
);
|
||||||
|
local ROOM_OCCUPANCY_CALLBACK_URL = module:get_option_string(
|
||||||
|
"politictalk_room_occupancy_callback_url",
|
||||||
|
os.getenv("POLITICTALK_ROOM_OCCUPANCY_CALLBACK_URL")
|
||||||
|
);
|
||||||
local ROOM_INACTIVE_CALLBACK_SECRET = module:get_option_string(
|
local ROOM_INACTIVE_CALLBACK_SECRET = module:get_option_string(
|
||||||
"politictalk_room_inactive_callback_secret",
|
"politictalk_room_inactive_callback_secret",
|
||||||
os.getenv("POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET")
|
os.getenv("POLITICTALK_ROOM_INACTIVE_CALLBACK_SECRET")
|
||||||
@@ -26,6 +30,18 @@ local function get_user_id(session)
|
|||||||
return user and user.id or nil;
|
return user and user.id or nil;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_participant_limit(session)
|
||||||
|
local user = get_user(session);
|
||||||
|
local limit = user and (user.participantLimit or user.participant_limit) or nil;
|
||||||
|
local numeric_limit = tonumber(limit);
|
||||||
|
|
||||||
|
if numeric_limit and numeric_limit > 0 then
|
||||||
|
return math.floor(numeric_limit);
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
local function is_moderator_session(session)
|
local function is_moderator_session(session)
|
||||||
local user = get_user(session);
|
local user = get_user(session);
|
||||||
local moderator = user and user.moderator;
|
local moderator = user and user.moderator;
|
||||||
@@ -77,6 +93,16 @@ local function remove_value(values, value)
|
|||||||
return filtered;
|
return filtered;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function table_count(values)
|
||||||
|
local count = 0;
|
||||||
|
|
||||||
|
for _ in ipairs(values or {}) do
|
||||||
|
count = count + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
return count;
|
||||||
|
end
|
||||||
|
|
||||||
local function ensure_room_data(room)
|
local function ensure_room_data(room)
|
||||||
if not room then
|
if not room then
|
||||||
return false;
|
return false;
|
||||||
@@ -86,6 +112,7 @@ local function ensure_room_data(room)
|
|||||||
room._data.moderators = room._data.moderators or {};
|
room._data.moderators = room._data.moderators or {};
|
||||||
room._data.participants = room._data.participants or {};
|
room._data.participants = room._data.participants or {};
|
||||||
room._data.politictalk_host_jids = room._data.politictalk_host_jids or {};
|
room._data.politictalk_host_jids = room._data.politictalk_host_jids or {};
|
||||||
|
room._data.politictalk_participant_jids = room._data.politictalk_participant_jids or {};
|
||||||
room._data.politictalk_jid_user_ids = room._data.politictalk_jid_user_ids or {};
|
room._data.politictalk_jid_user_ids = room._data.politictalk_jid_user_ids or {};
|
||||||
-- Enables Jitsi AV moderation automatically: hosts can speak, participants
|
-- Enables Jitsi AV moderation automatically: hosts can speak, participants
|
||||||
-- must be explicitly allowed by a host before they can unmute.
|
-- must be explicitly allowed by a host before they can unmute.
|
||||||
@@ -119,6 +146,7 @@ local function mark_host(room, occupant, user_id)
|
|||||||
|
|
||||||
ensure_room_data(room);
|
ensure_room_data(room);
|
||||||
room._data.politictalk_host_jids = add_unique(room._data.politictalk_host_jids, occupant.bare_jid);
|
room._data.politictalk_host_jids = add_unique(room._data.politictalk_host_jids, occupant.bare_jid);
|
||||||
|
room._data.politictalk_participant_jids = remove_value(room._data.politictalk_participant_jids, occupant.bare_jid);
|
||||||
room._data.politictalk_jid_user_ids[occupant.bare_jid] = user_id;
|
room._data.politictalk_jid_user_ids[occupant.bare_jid] = user_id;
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -128,6 +156,7 @@ local function mark_participant(room, occupant, user_id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
ensure_room_data(room);
|
ensure_room_data(room);
|
||||||
|
room._data.politictalk_participant_jids = add_unique(room._data.politictalk_participant_jids, occupant.bare_jid);
|
||||||
room._data.politictalk_jid_user_ids[occupant.bare_jid] = user_id;
|
room._data.politictalk_jid_user_ids[occupant.bare_jid] = user_id;
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -141,6 +170,7 @@ local function unmark_occupant(room, occupant)
|
|||||||
local user_id = room._data.politictalk_jid_user_ids[occupant.bare_jid];
|
local user_id = room._data.politictalk_jid_user_ids[occupant.bare_jid];
|
||||||
room._data.politictalk_jid_user_ids[occupant.bare_jid] = nil;
|
room._data.politictalk_jid_user_ids[occupant.bare_jid] = nil;
|
||||||
room._data.politictalk_host_jids = remove_value(room._data.politictalk_host_jids, occupant.bare_jid);
|
room._data.politictalk_host_jids = remove_value(room._data.politictalk_host_jids, occupant.bare_jid);
|
||||||
|
room._data.politictalk_participant_jids = remove_value(room._data.politictalk_participant_jids, occupant.bare_jid);
|
||||||
|
|
||||||
return user_id;
|
return user_id;
|
||||||
end
|
end
|
||||||
@@ -161,6 +191,36 @@ local function has_active_host(room)
|
|||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_active_host_count(room)
|
||||||
|
if not room then
|
||||||
|
return 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
ensure_room_data(room);
|
||||||
|
|
||||||
|
local count = 0;
|
||||||
|
for _, occupant in room:each_occupant() do
|
||||||
|
if table_contains(room._data.politictalk_host_jids, occupant.bare_jid) then
|
||||||
|
count = count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return count;
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_occupant_count(room)
|
||||||
|
if not room then
|
||||||
|
return 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
local count = 0;
|
||||||
|
for _ in room:each_occupant() do
|
||||||
|
count = count + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
return count;
|
||||||
|
end
|
||||||
|
|
||||||
local function deny_join(event, reason)
|
local function deny_join(event, reason)
|
||||||
if event.origin and event.stanza then
|
if event.origin and event.stanza then
|
||||||
event.origin.send(st.error_reply(event.stanza, "cancel", "not-allowed", reason));
|
event.origin.send(st.error_reply(event.stanza, "cancel", "not-allowed", reason));
|
||||||
@@ -225,6 +285,66 @@ local function notify_room_inactive(room, reason)
|
|||||||
end);
|
end);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function notify_room_occupancy(room, reason)
|
||||||
|
if
|
||||||
|
not ROOM_OCCUPANCY_CALLBACK_URL
|
||||||
|
or ROOM_OCCUPANCY_CALLBACK_URL == ""
|
||||||
|
or not ROOM_INACTIVE_CALLBACK_SECRET
|
||||||
|
or ROOM_INACTIVE_CALLBACK_SECRET == ""
|
||||||
|
then
|
||||||
|
module:log("warn", "Skipping PoliticTalk room-occupancy callback because callback URL or secret is missing");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local meeting_code = get_room_meeting_code(room);
|
||||||
|
if not meeting_code then
|
||||||
|
module:log("warn", "Skipping PoliticTalk room-occupancy callback because room meeting code is missing");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
ensure_room_data(room);
|
||||||
|
|
||||||
|
local participant_count = table_count(room._data.politictalk_participant_jids);
|
||||||
|
local moderator_count = table_count(room._data.moderators);
|
||||||
|
local occupant_count = get_occupant_count(room);
|
||||||
|
local host_count = get_active_host_count(room);
|
||||||
|
|
||||||
|
http.request(ROOM_OCCUPANCY_CALLBACK_URL, {
|
||||||
|
method = "POST";
|
||||||
|
headers = {
|
||||||
|
["Content-Type"] = "application/json";
|
||||||
|
["X-PoliticTalk-Jitsi-Secret"] = ROOM_INACTIVE_CALLBACK_SECRET;
|
||||||
|
};
|
||||||
|
body = json.encode({
|
||||||
|
meetingCode = meeting_code;
|
||||||
|
reason = reason or "occupancy_changed";
|
||||||
|
roomJid = room.jid;
|
||||||
|
participantCount = participant_count;
|
||||||
|
moderatorCount = moderator_count;
|
||||||
|
occupantCount = occupant_count;
|
||||||
|
hostCount = host_count;
|
||||||
|
});
|
||||||
|
}, function(_, code)
|
||||||
|
if code and code >= 200 and code < 300 then
|
||||||
|
module:log(
|
||||||
|
"info",
|
||||||
|
"Updated PoliticTalk room occupancy in PgApi: room=%s participants=%s occupants=%s hosts=%s",
|
||||||
|
tostring(meeting_code),
|
||||||
|
tostring(participant_count),
|
||||||
|
tostring(occupant_count),
|
||||||
|
tostring(host_count)
|
||||||
|
);
|
||||||
|
else
|
||||||
|
module:log(
|
||||||
|
"warn",
|
||||||
|
"Failed to update PoliticTalk room occupancy in PgApi: room=%s status=%s",
|
||||||
|
tostring(meeting_code),
|
||||||
|
tostring(code)
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
end
|
||||||
|
|
||||||
module:hook("muc-room-pre-create", function(event)
|
module:hook("muc-room-pre-create", function(event)
|
||||||
if event.stanza and event.stanza.attr and is_admin(event.stanza.attr.from) then
|
if event.stanza and event.stanza.attr and is_admin(event.stanza.attr.from) then
|
||||||
return;
|
return;
|
||||||
@@ -260,6 +380,26 @@ module:hook("muc-occupant-pre-join", function(event)
|
|||||||
return deny_join(event, "PoliticTalk room is waiting for the host");
|
return deny_join(event, "PoliticTalk room is waiting for the host");
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not is_moderator then
|
||||||
|
ensure_room_data(room);
|
||||||
|
local participant_limit = get_participant_limit(session);
|
||||||
|
local active_participant_count = table_count(room._data.politictalk_participant_jids);
|
||||||
|
if
|
||||||
|
participant_limit
|
||||||
|
and active_participant_count >= participant_limit
|
||||||
|
then
|
||||||
|
module:log(
|
||||||
|
"warn",
|
||||||
|
"Blocking participant %s because PoliticTalk room is full: room=%s active=%s limit=%s",
|
||||||
|
tostring(user_id),
|
||||||
|
tostring(room and room.jid),
|
||||||
|
tostring(active_participant_count),
|
||||||
|
tostring(participant_limit)
|
||||||
|
);
|
||||||
|
return deny_join(event, "PoliticTalk room is full");
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ROLE_JOINING[occupant_jid] = {
|
ROLE_JOINING[occupant_jid] = {
|
||||||
is_moderator = is_moderator;
|
is_moderator = is_moderator;
|
||||||
user_id = user_id;
|
user_id = user_id;
|
||||||
@@ -298,6 +438,8 @@ module:hook("muc-occupant-joined", function(event)
|
|||||||
room:set_affiliation(true, occupant.bare_jid, "member");
|
room:set_affiliation(true, occupant.bare_jid, "member");
|
||||||
module:log("info", "PoliticTalk participant joined as member: %s room=%s", tostring(role.user_id), tostring(room.jid));
|
module:log("info", "PoliticTalk participant joined as member: %s room=%s", tostring(role.user_id), tostring(room.jid));
|
||||||
end
|
end
|
||||||
|
|
||||||
|
notify_room_occupancy(room, "occupant_joined");
|
||||||
end, 1);
|
end, 1);
|
||||||
|
|
||||||
module:hook("muc-occupant-left", function(event)
|
module:hook("muc-occupant-left", function(event)
|
||||||
@@ -317,6 +459,8 @@ module:hook("muc-occupant-left", function(event)
|
|||||||
module:log("info", "Destroying PoliticTalk room after host left: %s", tostring(room.jid));
|
module:log("info", "Destroying PoliticTalk room after host left: %s", tostring(room.jid));
|
||||||
notify_room_inactive(room, "host_left");
|
notify_room_inactive(room, "host_left");
|
||||||
room:destroy(nil, "The host has left the PoliticTalk room");
|
room:destroy(nil, "The host has left the PoliticTalk room");
|
||||||
|
else
|
||||||
|
notify_room_occupancy(room, "occupant_left");
|
||||||
end
|
end
|
||||||
end, -20);
|
end, -20);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ VirtualHost "politictalk.parallelglobe.io"
|
|||||||
|
|
||||||
Component "conference.politictalk.parallelglobe.io" "muc"
|
Component "conference.politictalk.parallelglobe.io" "muc"
|
||||||
politictalk_room_inactive_callback_url = "https://api.parallelglobe.is/events/politictalk/jitsi/room-inactive"
|
politictalk_room_inactive_callback_url = "https://api.parallelglobe.is/events/politictalk/jitsi/room-inactive"
|
||||||
|
politictalk_room_occupancy_callback_url = "https://api.parallelglobe.is/events/politictalk/jitsi/room-occupancy"
|
||||||
politictalk_room_inactive_callback_secret = "POLITICTALK_JITSI_LIFECYCLE_SECRET"
|
politictalk_room_inactive_callback_secret = "POLITICTALK_JITSI_LIFECYCLE_SECRET"
|
||||||
|
|
||||||
modules_enabled = {
|
modules_enabled = {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
top: max(20px, env(safe-area-inset-top));
|
top: max(20px, env(safe-area-inset-top));
|
||||||
width: auto;
|
width: auto;
|
||||||
z-index: 2147483000;
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.politictalk-room-logo img {
|
.politictalk-room-logo img {
|
||||||
@@ -42,6 +42,10 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.politictalk-direct-access-blocked body > *:not(.politictalk-direct-access):not(.politictalk-room-logo) {
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
.politictalk-direct-access {
|
.politictalk-direct-access {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(13, 15, 16, 0.48);
|
background: rgba(13, 15, 16, 0.48);
|
||||||
@@ -114,24 +118,33 @@
|
|||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.politictalk-room-logo {
|
.politictalk-room-logo {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 52px;
|
height: 48px;
|
||||||
left: max(12px, env(safe-area-inset-left));
|
left: max(14px, env(safe-area-inset-left));
|
||||||
top: max(12px, env(safe-area-inset-top));
|
top: max(18px, env(safe-area-inset-top));
|
||||||
|
max-width: calc(100vw - 28px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.politictalk-room-logo img {
|
.politictalk-room-logo img {
|
||||||
flex-basis: 52px;
|
flex-basis: 48px;
|
||||||
height: 52px;
|
height: 48px;
|
||||||
width: 52px;
|
width: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.politictalk-room-logo__text {
|
.politictalk-room-logo__text {
|
||||||
font-size: 20px;
|
display: block;
|
||||||
|
font-size: 21px;
|
||||||
|
max-width: calc(100vw - 86px);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-container {
|
||||||
|
top: calc(max(18px, env(safe-area-inset-top)) + 58px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.politictalk-direct-access {
|
.politictalk-direct-access {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 132px 18px 18px;
|
padding: 150px 18px 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.politictalk-direct-access__dialog {
|
.politictalk-direct-access__dialog {
|
||||||
|
|||||||
Reference in New Issue
Block a user