fix: client perms, @everyone/@channel, docs, unit tests
- usePermissions ORs current user roles + @everyone only (not all server roles) - cache myRolesByServer; load on active server; refresh after self role edit - gate/notify @everyone and @channel; plain @username push; special mention UI - refresh FEATURE_PARITY (DMs exist; drop stale critical gaps) - README production deploy notes dumpster.service - unit tests for permission bits and broadcast mention tokens
This commit is contained in:
@@ -55,7 +55,8 @@ export const PERMISSION_LABELS: Record<PermissionKey, string> = {
|
||||
};
|
||||
|
||||
export function hasPermission(perms: number, flag: number): boolean {
|
||||
return (perms & flag) !== 0;
|
||||
// Match backend permissions.Has: all required bits must be present.
|
||||
return (perms & flag) === flag;
|
||||
}
|
||||
|
||||
export interface ChannelOverride {
|
||||
|
||||
+57
-9
@@ -27,10 +27,13 @@ export interface UpdateRoleData {
|
||||
|
||||
interface RoleState {
|
||||
roles: Role[];
|
||||
/** Assigned roles for the current user, keyed by server id. Does not include @everyone. */
|
||||
myRolesByServer: Record<string, Role[]>;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
|
||||
fetchRoles: (serverId: string) => Promise<void>;
|
||||
fetchMyRoles: (serverId: string, userId: string) => Promise<void>;
|
||||
createRole: (serverId: string, data: CreateRoleData) => Promise<Role>;
|
||||
updateRole: (serverId: string, roleId: string, data: UpdateRoleData) => Promise<Role>;
|
||||
deleteRole: (serverId: string, roleId: string) => Promise<void>;
|
||||
@@ -40,6 +43,7 @@ interface RoleState {
|
||||
|
||||
export const useRoleStore = create<RoleState>((set) => ({
|
||||
roles: [],
|
||||
myRolesByServer: {},
|
||||
loading: false,
|
||||
error: null,
|
||||
|
||||
@@ -47,7 +51,14 @@ export const useRoleStore = create<RoleState>((set) => ({
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const roles = await api.get<Role[]>(`/servers/${serverId}/roles`);
|
||||
set({ roles: roles.sort((a, b) => b.position - a.position), loading: false });
|
||||
set((state) => ({
|
||||
// Keep roles from other servers if mixed; replace same-server entries.
|
||||
roles: [
|
||||
...state.roles.filter((r) => r.server_id !== serverId),
|
||||
...(Array.isArray(roles) ? roles : []),
|
||||
].sort((a, b) => b.position - a.position),
|
||||
loading: false,
|
||||
}));
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
@@ -56,6 +67,25 @@ export const useRoleStore = create<RoleState>((set) => ({
|
||||
}
|
||||
},
|
||||
|
||||
fetchMyRoles: async (serverId, userId) => {
|
||||
try {
|
||||
const roles = await api.get<Role[]>(`/servers/${serverId}/members/${userId}/roles`);
|
||||
set((state) => ({
|
||||
myRolesByServer: {
|
||||
...state.myRolesByServer,
|
||||
[serverId]: Array.isArray(roles) ? roles : [],
|
||||
},
|
||||
}));
|
||||
} catch {
|
||||
set((state) => ({
|
||||
myRolesByServer: {
|
||||
...state.myRolesByServer,
|
||||
[serverId]: [],
|
||||
},
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
createRole: async (serverId, data) => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
@@ -78,10 +108,19 @@ export const useRoleStore = create<RoleState>((set) => ({
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const role = await api.patch<Role>(`/servers/${serverId}/roles/${roleId}`, data);
|
||||
set((state) => ({
|
||||
roles: state.roles.map((r) => (r.id === roleId ? role : r)).sort((a, b) => b.position - a.position),
|
||||
loading: false,
|
||||
}));
|
||||
set((state) => {
|
||||
const myRoles = state.myRolesByServer[serverId];
|
||||
const nextMy = myRoles
|
||||
? myRoles.map((r) => (r.id === roleId ? role : r))
|
||||
: myRoles;
|
||||
return {
|
||||
roles: state.roles.map((r) => (r.id === roleId ? role : r)).sort((a, b) => b.position - a.position),
|
||||
myRolesByServer: nextMy
|
||||
? { ...state.myRolesByServer, [serverId]: nextMy }
|
||||
: state.myRolesByServer,
|
||||
loading: false,
|
||||
};
|
||||
});
|
||||
return role;
|
||||
} catch (error) {
|
||||
set({
|
||||
@@ -96,10 +135,19 @@ export const useRoleStore = create<RoleState>((set) => ({
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
await api.delete(`/servers/${serverId}/roles/${roleId}`);
|
||||
set((state) => ({
|
||||
roles: state.roles.filter((r) => r.id !== roleId),
|
||||
loading: false,
|
||||
}));
|
||||
set((state) => {
|
||||
const myRoles = state.myRolesByServer[serverId];
|
||||
return {
|
||||
roles: state.roles.filter((r) => r.id !== roleId),
|
||||
myRolesByServer: myRoles
|
||||
? {
|
||||
...state.myRolesByServer,
|
||||
[serverId]: myRoles.filter((r) => r.id !== roleId),
|
||||
}
|
||||
: state.myRolesByServer,
|
||||
loading: false,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
|
||||
Reference in New Issue
Block a user