chore: Move to Typescript (#2783)
This PR moves the entire project to Typescript. Due to the ~1000 ignores this will lead to a messy codebase for a while, but the churn is worth it – all of those ignore comments are places that were never type-safe previously. closes #1282
This commit is contained in:
275
app/stores/UsersStore.ts
Normal file
275
app/stores/UsersStore.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
import invariant from "invariant";
|
||||
import { filter, orderBy } from "lodash";
|
||||
import { observable, computed, action, runInAction } from "mobx";
|
||||
import { Role } from "@shared/types";
|
||||
import User from "~/models/User";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
import BaseStore from "./BaseStore";
|
||||
import RootStore from "./RootStore";
|
||||
|
||||
export default class UsersStore extends BaseStore<User> {
|
||||
@observable
|
||||
counts: {
|
||||
active: number;
|
||||
admins: number;
|
||||
all: number;
|
||||
invited: number;
|
||||
suspended: number;
|
||||
viewers: number;
|
||||
} = {
|
||||
active: 0,
|
||||
admins: 0,
|
||||
all: 0,
|
||||
invited: 0,
|
||||
suspended: 0,
|
||||
viewers: 0,
|
||||
};
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
super(rootStore, User);
|
||||
}
|
||||
|
||||
@computed
|
||||
get active(): User[] {
|
||||
return this.orderedData.filter(
|
||||
(user) => !user.isSuspended && user.lastActiveAt
|
||||
);
|
||||
}
|
||||
|
||||
@computed
|
||||
get suspended(): User[] {
|
||||
return this.orderedData.filter((user) => user.isSuspended);
|
||||
}
|
||||
|
||||
@computed
|
||||
get activeOrInvited(): User[] {
|
||||
return this.orderedData.filter((user) => !user.isSuspended);
|
||||
}
|
||||
|
||||
@computed
|
||||
get invited(): User[] {
|
||||
return this.orderedData.filter((user) => user.isInvited);
|
||||
}
|
||||
|
||||
@computed
|
||||
get admins(): User[] {
|
||||
return this.orderedData.filter((user) => user.isAdmin);
|
||||
}
|
||||
|
||||
@computed
|
||||
get viewers(): User[] {
|
||||
return this.orderedData.filter((user) => user.isViewer);
|
||||
}
|
||||
|
||||
@computed
|
||||
get all(): User[] {
|
||||
return this.orderedData.filter((user) => user.lastActiveAt);
|
||||
}
|
||||
|
||||
@computed
|
||||
get orderedData(): User[] {
|
||||
return orderBy(Array.from(this.data.values()), "name", "asc");
|
||||
}
|
||||
|
||||
@action
|
||||
promote = async (user: User) => {
|
||||
try {
|
||||
this.updateCounts("admin", user.role);
|
||||
await this.actionOnUser("promote", user);
|
||||
} catch {
|
||||
this.updateCounts(user.role, "admin");
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
demote = async (user: User, to: Role) => {
|
||||
try {
|
||||
this.updateCounts(to, user.role);
|
||||
await this.actionOnUser("demote", user, to);
|
||||
} catch {
|
||||
this.updateCounts(user.role, to);
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
suspend = async (user: User) => {
|
||||
try {
|
||||
this.counts.suspended += 1;
|
||||
this.counts.active -= 1;
|
||||
await this.actionOnUser("suspend", user);
|
||||
} catch {
|
||||
this.counts.suspended -= 1;
|
||||
this.counts.active += 1;
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
activate = async (user: User) => {
|
||||
try {
|
||||
this.counts.suspended -= 1;
|
||||
this.counts.active += 1;
|
||||
await this.actionOnUser("activate", user);
|
||||
} catch {
|
||||
this.counts.suspended += 1;
|
||||
this.counts.active -= 1;
|
||||
}
|
||||
};
|
||||
|
||||
@action
|
||||
invite = async (
|
||||
invites: {
|
||||
email: string;
|
||||
name: string;
|
||||
role: Role;
|
||||
}[]
|
||||
) => {
|
||||
const res = await client.post(`/users.invite`, {
|
||||
invites,
|
||||
});
|
||||
invariant(res && res.data, "Data should be available");
|
||||
runInAction(`invite`, () => {
|
||||
res.data.users.forEach(this.add);
|
||||
this.counts.invited += res.data.sent.length;
|
||||
this.counts.all += res.data.sent.length;
|
||||
});
|
||||
return res.data;
|
||||
};
|
||||
|
||||
@action
|
||||
fetchCounts = async (teamId: string): Promise<any> => {
|
||||
const res = await client.post(`/users.count`, {
|
||||
teamId,
|
||||
});
|
||||
invariant(res && res.data, "Data should be available");
|
||||
this.counts = res.data.counts;
|
||||
return res.data;
|
||||
};
|
||||
|
||||
@action
|
||||
async delete(user: User, options: Record<string, any> = {}) {
|
||||
super.delete(user, options);
|
||||
|
||||
if (!user.isSuspended && user.lastActiveAt) {
|
||||
this.counts.active -= 1;
|
||||
}
|
||||
|
||||
if (user.isInvited) {
|
||||
this.counts.invited -= 1;
|
||||
}
|
||||
|
||||
if (user.isAdmin) {
|
||||
this.counts.admins -= 1;
|
||||
}
|
||||
|
||||
if (user.isSuspended) {
|
||||
this.counts.suspended -= 1;
|
||||
}
|
||||
|
||||
if (user.isViewer) {
|
||||
this.counts.viewers -= 1;
|
||||
}
|
||||
|
||||
this.counts.all -= 1;
|
||||
}
|
||||
|
||||
@action
|
||||
updateCounts = (to: Role, from: Role) => {
|
||||
if (to === "admin") {
|
||||
this.counts.admins += 1;
|
||||
|
||||
if (from === "viewer") {
|
||||
this.counts.viewers -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (to === "viewer") {
|
||||
this.counts.viewers += 1;
|
||||
|
||||
if (from === "admin") {
|
||||
this.counts.admins -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (to === "member") {
|
||||
if (from === "viewer") {
|
||||
this.counts.viewers -= 1;
|
||||
}
|
||||
|
||||
if (from === "admin") {
|
||||
this.counts.admins -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
notInCollection = (collectionId: string, query = "") => {
|
||||
const memberships = filter(
|
||||
this.rootStore.memberships.orderedData,
|
||||
(member) => member.collectionId === collectionId
|
||||
);
|
||||
const userIds = memberships.map((member) => member.userId);
|
||||
const users = filter(
|
||||
this.activeOrInvited,
|
||||
(user) => !userIds.includes(user.id)
|
||||
);
|
||||
if (!query) return users;
|
||||
return queriedUsers(users, query);
|
||||
};
|
||||
|
||||
inCollection = (collectionId: string, query?: string) => {
|
||||
const memberships = filter(
|
||||
this.rootStore.memberships.orderedData,
|
||||
(member) => member.collectionId === collectionId
|
||||
);
|
||||
const userIds = memberships.map((member) => member.userId);
|
||||
const users = filter(this.activeOrInvited, (user) =>
|
||||
userIds.includes(user.id)
|
||||
);
|
||||
if (!query) return users;
|
||||
return queriedUsers(users, query);
|
||||
};
|
||||
|
||||
notInGroup = (groupId: string, query = "") => {
|
||||
const memberships = filter(
|
||||
this.rootStore.groupMemberships.orderedData,
|
||||
(member) => member.groupId === groupId
|
||||
);
|
||||
const userIds = memberships.map((member) => member.userId);
|
||||
const users = filter(
|
||||
this.activeOrInvited,
|
||||
(user) => !userIds.includes(user.id)
|
||||
);
|
||||
if (!query) return users;
|
||||
return queriedUsers(users, query);
|
||||
};
|
||||
|
||||
inGroup = (groupId: string, query?: string) => {
|
||||
const groupMemberships = filter(
|
||||
this.rootStore.groupMemberships.orderedData,
|
||||
(member) => member.groupId === groupId
|
||||
);
|
||||
const userIds = groupMemberships.map((member) => member.userId);
|
||||
const users = filter(this.activeOrInvited, (user) =>
|
||||
userIds.includes(user.id)
|
||||
);
|
||||
if (!query) return users;
|
||||
return queriedUsers(users, query);
|
||||
};
|
||||
|
||||
actionOnUser = async (action: string, user: User, to?: Role) => {
|
||||
const res = await client.post(`/users.${action}`, {
|
||||
id: user.id,
|
||||
to,
|
||||
});
|
||||
invariant(res && res.data, "Data should be available");
|
||||
runInAction(`UsersStore#${action}`, () => {
|
||||
this.addPolicies(res.policies);
|
||||
this.add(res.data);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function queriedUsers(users: User[], query: string) {
|
||||
return filter(users, (user) =>
|
||||
user.name.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user