diff --git a/app/components/WebsocketProvider.tsx b/app/components/WebsocketProvider.tsx index b0c980c27..08c2b1ee9 100644 --- a/app/components/WebsocketProvider.tsx +++ b/app/components/WebsocketProvider.tsx @@ -18,6 +18,7 @@ import Pin from "~/models/Pin"; import Star from "~/models/Star"; import Subscription from "~/models/Subscription"; import Team from "~/models/Team"; +import User from "~/models/User"; import withStores from "~/components/withStores"; import { PartialWithId, @@ -460,6 +461,13 @@ class WebsocketProvider extends React.Component { } ); + this.socket.on("users.demote", async (event: PartialWithId) => { + if (auth.user && event.id === auth.user.id) { + documents.all.forEach((document) => policies.remove(document.id)); + await collections.fetchAll(); + } + }); + // received a message from the API server that we should request // to join a specific room. Forward that to the ws server. this.socket.on("join", (event) => { diff --git a/app/stores/base/Store.ts b/app/stores/base/Store.ts index cdf06ab54..059ce0d8f 100644 --- a/app/stores/base/Store.ts +++ b/app/stores/base/Store.ts @@ -1,8 +1,10 @@ import invariant from "invariant"; +import flatten from "lodash/flatten"; import lowerFirst from "lodash/lowerFirst"; import orderBy from "lodash/orderBy"; import { observable, action, computed, runInAction } from "mobx"; import pluralize from "pluralize"; +import { Pagination } from "@shared/constants"; import RootStore from "~/stores/RootStore"; import Policy from "~/models/Policy"; import Model from "~/models/base/Model"; @@ -268,6 +270,20 @@ export default abstract class Store { } }; + @action + fetchAll = async (): Promise => { + const limit = Pagination.defaultLimit; + const response = await this.fetchPage({ limit }); + const pages = Math.ceil(response[PAGINATION_SYMBOL].total / limit); + const fetchPages = []; + for (let page = 1; page < pages; page++) { + fetchPages.push(this.fetchPage({ offset: page * limit, limit })); + } + + const results = await Promise.all(fetchPages); + return flatten(results); + }; + @computed get orderedData(): T[] { return orderBy(Array.from(this.data.values()), "createdAt", "desc"); diff --git a/server/models/Event.ts b/server/models/Event.ts index a1227984b..53503ebb5 100644 --- a/server/models/Event.ts +++ b/server/models/Event.ts @@ -139,6 +139,7 @@ class Event extends IdModel { "documents.restore", "revisions.create", "users.create", + "users.demote", ]; static AUDIT_EVENTS: TEvent["name"][] = [ diff --git a/server/queues/processors/WebsocketsProcessor.ts b/server/queues/processors/WebsocketsProcessor.ts index e48ac0369..0c0f7d820 100644 --- a/server/queues/processors/WebsocketsProcessor.ts +++ b/server/queues/processors/WebsocketsProcessor.ts @@ -616,6 +616,12 @@ export default class WebsocketsProcessor { .emit(event.name, presentTeam(team)); } + case "users.demote": { + return socketio + .to(`user-${event.userId}`) + .emit(event.name, { id: event.userId }); + } + default: return; } diff --git a/server/routes/api/collections/collections.ts b/server/routes/api/collections/collections.ts index a8fcb8aaa..4374ea227 100644 --- a/server/routes/api/collections/collections.ts +++ b/server/routes/api/collections/collections.ts @@ -759,17 +759,20 @@ router.post( teamId: user.teamId, id: collectionIds, }; - const collections = await Collection.scope({ - method: ["withMembership", user.id], - }).findAll({ - where, - order: [ - Sequelize.literal('"collection"."index" collate "C"'), - ["updatedAt", "DESC"], - ], - offset: ctx.state.pagination.offset, - limit: ctx.state.pagination.limit, - }); + const [collections, total] = await Promise.all([ + Collection.scope({ + method: ["withMembership", user.id], + }).findAll({ + where, + order: [ + Sequelize.literal('"collection"."index" collate "C"'), + ["updatedAt", "DESC"], + ], + offset: ctx.state.pagination.offset, + limit: ctx.state.pagination.limit, + }), + Collection.count({ where }), + ]); const nullIndex = collections.findIndex( (collection) => collection.index === null @@ -783,7 +786,7 @@ router.post( } ctx.body = { - pagination: ctx.state.pagination, + pagination: { ...ctx.state.pagination, total }, data: collections.map(presentCollection), policies: presentPolicies(user, collections), }; diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index acfbbef7b..e8d16da63 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -152,12 +152,15 @@ router.post( sort = "updatedAt"; } - const documents = await Document.defaultScopeWithUser(user.id).findAll({ - where, - order: [[sort, direction]], - offset: ctx.state.pagination.offset, - limit: ctx.state.pagination.limit, - }); + const [documents, total] = await Promise.all([ + Document.defaultScopeWithUser(user.id).findAll({ + where, + order: [[sort, direction]], + offset: ctx.state.pagination.offset, + limit: ctx.state.pagination.limit, + }), + Document.count({ where }), + ]); // index sort is special because it uses the order of the documents in the // collection.documentStructure rather than a database column @@ -172,7 +175,7 @@ router.post( ); const policies = presentPolicies(user, documents); ctx.body = { - pagination: ctx.state.pagination, + pagination: { ...ctx.state.pagination, total }, data, policies, };