Skip to content

Commit 525f02e

Browse files
committed
Sync room notes to and from RCTogether
Added a column to the zoom room SQL table to associate each room with an ID of an RCTogether note block. Updated docs to reflect that. Forwarded RCTogether note data on startup and on change to the RCVerse UI. Disabled automatic note cleaning. Added Markdown formatting to RCVerse notes to reflect RCTogether's Markdown notes. Updated `/note` RCVerse API endpoint to call RCTogether REST API call to update the note there.
1 parent 77a50c8 commit 525f02e

6 files changed

+309
-78
lines changed

README.md

+41-27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Forked from [Recurse OAuth example](https://github.com/reedspool/recurse-oauth-example-node-express)
44

5+
## Setup Your Local Development Environment
6+
57
Node >=20 required (or see notes about Oslo installation for Node <20 [here](https://oslo.js.org))
68

79
```sh
@@ -12,19 +14,27 @@ To get your Client ID and Client Secret, go to <https://recurse.com/settings/app
1214

1315
Then make a copy of `config.env.template` named `config.env` and fill in the secrets there. For PostgreSQL, see the Neon section below.
1416

15-
Once you've done the above steps run the server locally with `npm start`.
17+
Once you've done the above steps, you can start your local dev environment.
18+
19+
## Run Local Development Environment
20+
21+
Once you've got all your local configuration done, including setting up and running a database, you should be able to run the development server with:
22+
23+
```sh
24+
npm start
25+
```
1626

1727
## Fly.io Deployment
1828

1929
`OAUTH_CLIENT_ID`, `OAUTH_CLIENT_SECRET` and `POSTGRES_CONNECTION` are set as Secrets inside the Fly App.
2030

2131
When you update those values, you'll need to run `fly deploy` to use the new versions.
2232

23-
## Neon deployment/PostGreSQL dev environment system
33+
## Neon deployment/PostgreSQL dev environment system
2434

25-
Technically you can use any PostgreSQL database, not just Neon, but this project is using Neon.
35+
RCVerse production uses [Neon](https://neon.tech/) to host a PostgreSQL database. You don't need to use Neon, any PostgreSQL server should work.
2636

27-
Sign up for a new account with Neon. Make a new database. Save the PostgreSQL connection string into the `config.env` variable `POSTGRES_CONNECTION` as well as in the Secrets section of your Fly App.
37+
If you want to use Neon, you'll need to sign up for a new account. Make a new database. Save the PostgreSQL connection string into the `config.env` variable `POSTGRES_CONNECTION` as well as in the Secrets section of your Fly App.
2838

2939
Create some tables. Run these queries separately in the Neon "SQL Editor":
3040

@@ -45,42 +55,44 @@ CREATE TABLE user_session (
4555
)
4656
```
4757

48-
Then create a table and fill it with all the standard RC room information.
58+
59+
Then create a table and fill it with all the standard RC room information. This table should be write-only most of the time, since RC rooms rarely change. You can locate the values of the `note_block_rctogether_id` field by watching the websocket messages in RCTogether when you save an edit to a note block.
4960

5061
```sql
5162
CREATE TABLE zoom_rooms (
5263
id serial PRIMARY KEY,
5364
room_name TEXT,
5465
location TEXT, /* Usually URL */
66+
note_block_rctogether_id TEXT, /* Associated note block ID in VirtualRC */
5567
visibility TEXT /* if not "visible", shouldn't appear in UI */
5668
);
5769

58-
INSERT INTO zoom_rooms (room_name, location, visibility)
70+
INSERT INTO zoom_rooms (room_name, location, note_block_rctogether_id, visibility)
5971
VALUES
60-
('Aegis','https://www.recurse.com/zoom/aegis','visible'),
61-
('Arca','https://www.recurse.com/zoom/arca','visible'),
62-
('Edos','https://www.recurse.com/zoom/edos','visible'),
63-
('Genera','https://www.recurse.com/zoom/genera','visible'),
64-
('Midori','https://www.recurse.com/zoom/midori','visible'),
65-
('Verve','https://www.recurse.com/zoom/verve','visible'),
66-
('Couches','https://www.recurse.com/zoom/couches','visible'),
67-
('Kitchen','https://www.recurse.com/zoom/kitchen','visible'),
68-
('Pairing Station 1','https://www.recurse.com/zoom/pairing_station_1','visible'),
69-
('Pairing Station 2','https://www.recurse.com/zoom/pairing_station_2','visible'),
70-
('Pairing Station 3','https://www.recurse.com/zoom/pairing_station_3','visible'),
71-
('Pairing Station 4','https://www.recurse.com/zoom/pairing_station_4','visible'),
72-
('Pairing Station 5','https://www.recurse.com/zoom/pairing_station_5','visible'),
73-
('Pairing Station 6','https://recurse.rctogether.com/zoom_meetings/35980/join','visible'),
74-
('Pairing Station 7','https://recurse.rctogether.com/zoom_meetings/35983/join','visible'),
75-
('Pomodoro Room','https://www.recurse.com/zoom/pomodoro_room','visible'),
76-
('Presentation Space','https://www.recurse.com/zoom/presentation_space','visible'),
77-
('Faculty Area','https://www.recurse.com/zoom/faculty_area','visible'),
78-
('Faculty Lounge','https://www.recurse.com/zoom/faculty_lounge','visible');
72+
('Aegis','https://www.recurse.com/zoom/aegis', NULL, 'visible'),
73+
('Arca','https://www.recurse.com/zoom/arca', NULL, 'visible'),
74+
('Edos','https://www.recurse.com/zoom/edos', NULL, 'visible'),
75+
('Genera','https://www.recurse.com/zoom/genera', NULL, 'visible'),
76+
('Midori','https://www.recurse.com/zoom/midori', NULL, 'visible'),
77+
('Verve','https://www.recurse.com/zoom/verve', NULL, 'visible'),
78+
('Couches','https://www.recurse.com/zoom/couches', '81750', 'visible'),
79+
('Kitchen','https://www.recurse.com/zoom/kitchen', NULL, 'visible'),
80+
('Pairing Station 1','https://www.recurse.com/zoom/pairing_station_1', '152190', 'visible'),
81+
('Pairing Station 2','https://www.recurse.com/zoom/pairing_station_2', '152189', 'visible'),
82+
('Pairing Station 3','https://www.recurse.com/zoom/pairing_station_3', '152193', 'visible'),
83+
('Pairing Station 4','https://www.recurse.com/zoom/pairing_station_4', '152191', 'visible'),
84+
('Pairing Station 5','https://www.recurse.com/zoom/pairing_station_5', '140538', 'visible'),
85+
('Pairing Station 6','https://recurse.rctogether.com/zoom_meetings/35980/join', '152198', 'visible'),
86+
('Pairing Station 7','https://recurse.rctogether.com/zoom_meetings/35983/join', '152192', 'visible'),
87+
('Pomodoro Room','https://www.recurse.com/zoom/pomodoro_room', NULL, 'visible'),
88+
('Presentation Space','https://www.recurse.com/zoom/presentation_space', NULL, 'visible'),
89+
('Faculty Area','https://www.recurse.com/zoom/faculty_area', NULL, 'visible'),
90+
('Faculty Lounge','https://www.recurse.com/zoom/faculty_lounge', NULL, 'visible');
7991
```
8092

81-
This table is meant mostly to be write-only mostly, but RC does change the rooms sometimes. To fill this table, please ask Reed. Mostly, it's not kept in this table since it may have personally identifying information in it.
93+
Above are all the visible rooms, but some rooms are "invisible" to RCVerse, like personal rooms of RC faculty. To add an invisible room, insert a new invisible row. The difference between simply leaving a room out and adding the invisible row to the database is that if there is no associated "invisible" row in the database, a log message will appear warning there's a "surprising" zoom room that's not tracked. You can ignore this though.
8294

83-
To add an invisible room (because maybe the name has PIID), insert a new invisible row. The only effect right now of invisible rooms is to squash a log message that "a surprising room showed up":
95+
To edit the rooms on production RCVerse, please ask Reed. But here's an example addition of an invisible room:
8496

8597
```sql
8698
INSERT INTO zoom_rooms (room_name, location, visibility)
@@ -90,6 +102,8 @@ VALUES
90102
91103
## RCTogether API (ActionCable)
92104
105+
[The documentation for RCTogether's APIs lives here.](https://docs.rctogether.com/#introduction)
106+
93107
Go to https://recurse.rctogether.com/apps and make a new application, then plug in your App ID and App secret into `ACTION_CABLE_APP_ID` and `ACTION_CABLE_APP_SECRET` in your `config.env`.
94108

95109
## Recurse.com Calendar

actioncable.js

+42-27
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,24 @@ export function connect(APP_ID, APP_SECRET, emitter) {
4343
console.error("Saw world data twice without a reconnect");
4444
hasSeenWorldDataWithoutReconnect = true;
4545
payload.entities.forEach((entity) => {
46-
const {
47-
type,
48-
zoom_user_display_name,
49-
person_name,
50-
image_path,
51-
last_seen_at,
52-
rc_hub_visit_today,
53-
flair,
54-
} = entity;
55-
if (type === "Avatar" && zoom_user_display_name !== null) {
46+
const { type, name, zoom_user_display_name } = entity;
47+
if (type === "Bot" && name?.match(/rcverse/i)) {
48+
console.error(`Uncleaned bot found: ${entity.id}`, entity);
49+
} else if (type === "Note") {
50+
const { id, note_text, note_updated_at } = entity;
51+
emitter.emit("room-note-data", {
52+
id: String(id),
53+
content: note_text,
54+
updatedTimestamp: note_updated_at,
55+
});
56+
} else if (type === "Avatar" && zoom_user_display_name !== null) {
57+
const {
58+
person_name,
59+
image_path,
60+
last_seen_at,
61+
rc_hub_visit_today,
62+
flair,
63+
} = entity;
5664
const lastSeenMillis = new Date(last_seen_at).getTime();
5765
const millisSinceLastSeen = Date.now() - lastSeenMillis;
5866
const hourInMillis = 1000 * 60 * 60;
@@ -75,23 +83,30 @@ export function connect(APP_ID, APP_SECRET, emitter) {
7583
}
7684
});
7785
} else if (type === "entity") {
78-
const {
79-
type,
80-
person_name,
81-
zoom_user_display_name,
82-
image_path,
83-
rc_hub_visit_today,
84-
flair,
85-
} = payload;
86-
if (type !== "Avatar") return;
87-
88-
emitter.emit("participant-room-data", {
89-
participantName: person_name,
90-
roomName: zoom_user_display_name,
91-
faceMarkerImagePath: image_path,
92-
inTheHub: rc_hub_visit_today,
93-
lastBatch: flair,
94-
});
86+
const { type } = payload;
87+
if (type === "Note") {
88+
const { id, note_text, note_updated_at } = payload;
89+
emitter.emit("room-note-data", {
90+
id: String(id),
91+
content: note_text,
92+
updatedTimestamp: note_updated_at,
93+
});
94+
} else if (type === "Avatar") {
95+
const {
96+
person_name,
97+
zoom_user_display_name,
98+
image_path,
99+
rc_hub_visit_today,
100+
flair,
101+
} = payload;
102+
emitter.emit("participant-room-data", {
103+
participantName: person_name,
104+
roomName: zoom_user_display_name,
105+
faceMarkerImagePath: image_path,
106+
inTheHub: rc_hub_visit_today,
107+
lastBatch: flair,
108+
});
109+
}
95110
}
96111
},
97112
});

html.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,14 @@ export const Room = ({
337337
${Participants({ participants, countPhrase })}
338338
339339
<p>
340+
Join
340341
<a
341342
class="room__join"
342343
href="${roomLocation}"
343344
target="_blank"
344345
rel="noopener noreferrer"
345346
>
346-
Join ${roomName}
347+
${roomName}
347348
</a>
348349
</p>
349350
@@ -419,7 +420,11 @@ export const Note = ({
419420
</div>
420421
`;
421422

422-
export const EditNoteForm = ({ roomName, noteContent }) => html`
423+
export const EditNoteForm = ({
424+
roomName,
425+
noteContent,
426+
hasVirtualRCConnectedBlock,
427+
}) => html`
423428
<form
424429
method="POST"
425430
action="/note"
@@ -439,6 +444,18 @@ export const EditNoteForm = ({ roomName, noteContent }) => html`
439444
>
440445
${noteContent}</textarea
441446
>
447+
<p>
448+
Use
449+
<a
450+
href="https://daringfireball.net/projects/markdown/basics"
451+
target="_blank"
452+
>Markdown</a
453+
>
454+
for links, bold, and italics.
455+
${hasVirtualRCConnectedBlock
456+
? "Changes synced to VirtualRC."
457+
: "This note is only on RCVerse."}
458+
</p>
442459
<div>
443460
<button type="submit">Update note</button>
444461
</div>

0 commit comments

Comments
 (0)