Skip to content

Commit 72735e3

Browse files
committed
Merge branch 'dev' into main
2 parents 5db1c13 + bf96a6f commit 72735e3

File tree

39 files changed

+128
-122
lines changed

39 files changed

+128
-122
lines changed

apps/client/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@deepnotes/client",
33
"description": "DeepNotes",
44
"homepage": "https://deepnotes.app",
5-
"version": "1.0.16",
5+
"version": "1.0.17",
66
"author": "Gustavo Toyota <[email protected]>",
77
"dependencies": {
88
"@_ueberdosis/prosemirror-tables": "~1.1.3",
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading

apps/client/src/code/pages/page/page.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -214,14 +214,22 @@ export class Page implements IPageRegion {
214214
return true;
215215
}
216216

217+
const userPlan = this.realtimeCtx.hget(
218+
'user',
219+
authStore().userId,
220+
'plan',
221+
);
222+
const pageIsFree = this.realtimeCtx.hget('page', this.id, 'free');
223+
217224
const groupMemberRole = this.realtimeCtx.hget(
218225
'group-member',
219226
`${this.react.groupId}:${authStore().userId}`,
220227
'role',
221228
);
222229

223-
return !(
224-
rolesMap()[groupMemberRole]?.permissions.editGroupPages ?? false
230+
return (
231+
!rolesMap()[groupMemberRole]?.permissions.editGroupPages ||
232+
(userPlan !== 'pro' && !pageIsFree)
225233
);
226234
}),
227235

apps/client/src/code/realtime/client.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,10 @@ export const RealtimeClient = once(
158158

159159
// Try to get subscribed value
160160

161+
const fullKey = getFullKey(prefix, suffix, field);
162+
161163
if (this.isSynced(prefix, suffix, field)) {
162-
return this.values[getFullKey(prefix, suffix, field)];
164+
return this.values[fullKey];
163165
}
164166

165167
// Add request to buffer
@@ -178,9 +180,7 @@ export const RealtimeClient = once(
178180
);
179181
const response = await resolvable;
180182

181-
this._logger
182-
.sub('hget response')
183-
.info(`${getFullKey(prefix, suffix, field)} (${response})`);
183+
this._logger.sub('hget response').info(`${fullKey} (${response})`);
184184

185185
return response;
186186
}

apps/client/src/code/tiptap/image-resize/NodeView.vue

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
/>
1212

1313
<div
14+
v-if="!internals.pages.react.page.react.readOnly"
1415
class="resize-handle"
1516
@pointerdown.left.stop.prevent="onResizeHandleLeftPointerDown"
1617
:style="{

apps/client/src/code/tiptap/youtube-video/NodeView.vue

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
></iframe>
1414

1515
<div
16+
v-if="!internals.pages.react.page.react.readOnly"
1617
class="resize-handle"
1718
@pointerdown.left.stop.prevent="onResizeHandleLeftPointerDown"
1819
:style="{

apps/client/src/layouts/PagesLayout/MainContent/DisplayPage/DisplayPage.vue

-28
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ watchEffect(() => {
4545
4646
componentLogger.info('Subscribing to required values');
4747
48-
const userPlan = realtimeCtx.hget('user', authStore().userId, 'plan');
49-
5048
const pageIsDeleted = !!realtimeCtx.hget(
5149
'page',
5250
props.page.id,
@@ -58,8 +56,6 @@ watchEffect(() => {
5856
: new Date() >
5957
realtimeCtx.hget('page', props.page.id, 'permanent-deletion-date');
6058
61-
const pageIsFree = !!realtimeCtx.hget('page', props.page.id, 'free');
62-
6359
const groupId = pageGroupIds()(props.page.id).get();
6460
6561
const groupIsDeleted =
@@ -73,11 +69,6 @@ watchEffect(() => {
7369
: new Date() >
7470
realtimeCtx.hget('group', groupId, 'permanent-deletion-date');
7571
76-
const groupIsPersonal =
77-
groupId == null
78-
? false
79-
: !!realtimeCtx.hget('group', groupId, 'is-personal');
80-
8172
const groupIsPublic =
8273
groupId == null ? false : realtimeCtx.hget('group', groupId, 'is-public');
8374
@@ -186,25 +177,6 @@ watchEffect(() => {
186177
return;
187178
}
188179
189-
componentLogger.info('Checking if user has a Pro plan');
190-
191-
if (
192-
!groupIsPersonal &&
193-
!groupIsPublic &&
194-
userPlan !== 'pro' &&
195-
groupId !== internals.personalGroupId
196-
) {
197-
props.page.setStatus('pro-plan-required');
198-
return;
199-
}
200-
201-
componentLogger.info('Checking if page is non-free');
202-
203-
if (!groupIsPublic && userPlan !== 'pro' && !pageIsFree) {
204-
props.page.setStatus('non-free-page');
205-
return;
206-
}
207-
208180
componentLogger.info('Checking if group is password protected');
209181
210182
if (groupContentKeyring?.topLayer === DataLayer.Symmetric) {

apps/client/src/layouts/PagesLayout/MainContent/DisplayPage/DisplayScreens/DisplayScreens.vue

-8
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@
77
<DisplayNonExistentScreen
88
v-else-if="page.react.status === 'page-nonexistent'"
99
/>
10-
<DisplayProPlanRequiredScreen
11-
v-else-if="page.react.status === 'pro-plan-required'"
12-
/>
13-
<DisplayNonFreePageScreen
14-
v-else-if="page.react.status === 'non-free-page'"
15-
/>
1610
<DisplayPageDeletedScreen
1711
v-else-if="page.react.status === 'page-deleted'"
1812
/>
@@ -36,10 +30,8 @@ import DisplayErrorScreen from './DisplayErrorScreen.vue';
3630
import DisplayGroupDeletedScreen from './DisplayGroupDeletedScreen.vue';
3731
import DisplayInvitedScreen from './DisplayInvitedScreen.vue';
3832
import DisplayNonExistentScreen from './DisplayNonExistentScreen.vue';
39-
import DisplayNonFreePageScreen from './DisplayNonFreePageScreen.vue';
4033
import DisplayPageDeletedScreen from './DisplayPageDeletedScreen.vue';
4134
import DisplayPasswordScreen from './DisplayPasswordScreen.vue';
42-
import DisplayProPlanRequiredScreen from './DisplayProPlanRequiredScreen.vue';
4335
import DisplayRejectedScreen from './DisplayRejectedScreen.vue';
4436
import DisplayUnauthorizedScreen from './DisplayUnauthorizedScreen.vue';
4537
import DisplayWorld from './DisplayWorld/DisplayWorld.vue';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<template>
2+
<div
3+
style="
4+
position: absolute;
5+
6+
bottom: 12px;
7+
right: 14px;
8+
9+
text-align: right;
10+
11+
pointer-events: none;
12+
13+
font-size: 12px;
14+
"
15+
>
16+
<div
17+
v-if="page.selection.react.elems.length > 0"
18+
class="selection-count"
19+
>
20+
{{ page.selection.react.elems.length }}
21+
item{{ page.selection.react.elems.length > 1 ? 's' : '' }} selected
22+
</div>
23+
24+
<div
25+
v-if="page.react.status === 'success' && page.react.readOnly"
26+
style="margin-top: 3px"
27+
>
28+
Read-only page{{ subscriptionExpired ? ' (Subscription expired)' : '' }}
29+
</div>
30+
</div>
31+
</template>
32+
33+
<script setup lang="ts">
34+
import { rolesMap } from '@deeplib/misc';
35+
import { useRealtimeContext } from 'src/code/realtime/context';
36+
37+
const page = computed(() => internals.pages.react.page);
38+
39+
const realtimeCtx = useRealtimeContext();
40+
41+
const subscriptionExpired = computed(
42+
() =>
43+
page.value.react.readOnly &&
44+
realtimeCtx.hget('user', authStore().userId, 'plan') !== 'pro' &&
45+
rolesMap()[
46+
realtimeCtx.hget(
47+
'group-member',
48+
`${page.value.react.groupId}:${authStore().userId}`,
49+
'role',
50+
)
51+
]?.permissions.editGroupPages,
52+
);
53+
</script>
54+
55+
<style scoped lang="scss">
56+
.selection-count {
57+
color: lighten(#006dd2, 23%);
58+
}
59+
</style>

apps/client/src/layouts/PagesLayout/MainContent/DisplayUI/DisplaySelectionCount.vue

-27
This file was deleted.

apps/client/src/layouts/PagesLayout/MainContent/DisplayUI/DisplayUI.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212

1313
<DisplayMobileAltBtn />
1414
<DisplayUserAvatars />
15-
<DisplaySelectionCount />
15+
<DisplayBottomRight />
1616
</template>
1717

1818
<script setup lang="ts">
19+
import DisplayBottomRight from './DisplayBottomRight.vue';
1920
import DisplayFindAndReplace from './DisplayFindAndReplace.vue';
2021
import DisplayHorizontalPath from './DisplayHorizontalPath.vue';
2122
import DisplayLeftBtns from './DisplayLeftBtns.vue';
2223
import DisplayMobileAltBtn from './DisplayMobileAltBtn.vue';
2324
import DisplayRightBtns from './DisplayRightBtns.vue';
24-
import DisplaySelectionCount from './DisplaySelectionCount.vue';
2525
import DisplayTopBtns from './DisplayTopBtns.vue';
2626
import DisplayUserAvatars from './DisplayUserAvatars.vue';
2727
</script>

apps/client/src/layouts/PagesLayout/RightSidebar/PageProperties/PageBacklinks.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
:icon="false"
3131
:page-id="backlinkPageId"
3232
prefer="absolute"
33-
@click="internals.pages.goToPage(backlinkPageId)"
33+
@click="
34+
internals.pages.goToPage(backlinkPageId, { fromParent: true })
35+
"
3436
/>
3537

3638
<DeepBtn

apps/collab-server/src/sockets.ts

+21-33
Original file line numberDiff line numberDiff line change
@@ -130,23 +130,11 @@ export class SocketAuxObject {
130130
}
131131

132132
if (!groupIsPublic) {
133-
const [[userPlan, personalGroupId], groupMemberRole] =
134-
await Promise.all([
135-
dataAbstraction().hmget('user', this.userId, [
136-
'plan',
137-
'personal-group-id',
138-
]),
139-
140-
dataAbstraction().hget(
141-
'group-member',
142-
`${groupId}:${this.userId}`,
143-
'role',
144-
),
145-
]);
146-
147-
if (userPlan !== 'pro' && groupId !== personalGroupId) {
148-
throw new Error('This requires a Pro plan subscription.');
149-
}
133+
const groupMemberRole = await dataAbstraction().hget(
134+
'group-member',
135+
`${groupId}:${this.userId}`,
136+
'role',
137+
);
150138

151139
if (groupMemberRole == null) {
152140
throw new Error('User is not a member of the group.');
@@ -399,13 +387,13 @@ export class SocketAuxObject {
399387
}
400388

401389
private async _handleMessage(messageBuffer: ArrayBuffer) {
402-
const [groupId, sessionInvalidated] = await Promise.all([
403-
dataAbstraction().hget('page', this.room.pageId, 'group-id'),
404-
405-
...(this.sessionId != null
406-
? [dataAbstraction().hget('session', this.sessionId, 'invalidated')]
407-
: []),
408-
]);
390+
const [sessionInvalidated, userPlan, pageGroupId, pageIsFree] =
391+
await Promise.all([
392+
dataAbstraction().hget('session', this.sessionId!, 'invalidated'),
393+
dataAbstraction().hget('user', this.userId!, 'plan'),
394+
dataAbstraction().hget('page', this.room.pageId, 'group-id'),
395+
dataAbstraction().hget('page', this.room.pageId, 'free'),
396+
]);
409397

410398
// Check if session is invalidated
411399

@@ -417,16 +405,16 @@ export class SocketAuxObject {
417405

418406
// Check if has permission to edit
419407

420-
const role =
421-
groupId != null && this.userId != null
422-
? await dataAbstraction().hget(
423-
'group-member',
424-
`${groupId}:${this.userId}`,
425-
'role',
426-
)
427-
: null;
408+
const role = await dataAbstraction().hget(
409+
'group-member',
410+
`${pageGroupId}:${this.userId}`,
411+
'role',
412+
);
428413

429-
if (!rolesMap()[role]?.permissions.editGroupPages) {
414+
if (
415+
!rolesMap()[role]?.permissions.editGroupPages ||
416+
(userPlan !== 'pro' && !pageIsFree)
417+
) {
430418
moduleLogger.info('Ignored message from unauthorized user');
431419
return;
432420
}

0 commit comments

Comments
 (0)