feat(web): role groups, last seen and unread markers in members, channels and spaces; lock channels without keys
This commit is contained in:
1 parent
5043c798ff
commit
d1ff8bab7d
23 files changed
+381
-34
No files matched your search
@@ -5,7 +5,8 @@ import { errorMessage } from '@/core-bridge/errors';
|
||||
import type { SenderInfo } from '@/core-bridge/types';
|
||||
import { useToast } from '@/store/toast';
|
||||
import { useWorkspace } from '@/store/workspace';
|
||||
import { permissionsOf } from '@/lib/roles';
|
||||
import { formatSeen } from '@/lib/format';
|
||||
import { OWNER_ROLE_ID, permissionsOf } from '@/lib/roles';
|
||||
import { useAccount } from '@/store/account';
|
||||
import { accountApi } from '@/core-bridge/account';
|
||||
import { Button } from '@/ui/Button';
|
||||
@@ -32,6 +33,7 @@ export function MembersPane({ owner, canRemove, refreshKey, onInvite }: Props) {
|
||||
const maxMembers = useWorkspace((s) => s.channel?.maxMembers);
|
||||
const people = useWorkspace((s) => s.people);
|
||||
const roles = useWorkspace((s) => s.roles);
|
||||
const creator = useWorkspace((s) => s.channel?.creatorAccount);
|
||||
const self = useAccount((s) => s.username);
|
||||
const listInvitations = useWorkspace((s) => s.listInvitations);
|
||||
const revoke = useWorkspace((s) => s.revokeInvitation);
|
||||
@@ -69,12 +71,15 @@ export function MembersPane({ owner, canRemove, refreshKey, onInvite }: Props) {
|
||||
|
||||
const online = members.filter((member) => member.online);
|
||||
const offline = members.filter((member) => !member.online);
|
||||
// 所在的身份组名(空间主在固定的 Owner 组里);待接受的邀请还没有身份
|
||||
const groupsOf = (username: string) => roles.filter((role) => (role.id === OWNER_ROLE_ID ? username === creator : role.members.includes(username))).map((role) => role.name);
|
||||
const row = (username: string, detail: string | undefined, invited: boolean, removable: boolean, isOwner = false, isOnline = false) => {
|
||||
const person = people[username];
|
||||
const name = person?.displayName || username;
|
||||
const groups = invited ? [] : groupsOf(username);
|
||||
return <li className={`${styles.row} ${invited || (!isOnline && !isOwner) ? styles.dim : ''}`} key={`${invited ? 'i' : 'm'}:${username}`}>
|
||||
<Avatar person={person} name={name} onOpen={() => setViewing(username)} />
|
||||
<span className={styles.who}><strong><NameWithBadges username={username}>{name}</NameWithBadges></strong>
|
||||
<span className={styles.who}><strong><NameWithBadges username={username}>{name}</NameWithBadges>{groups.map((group) => <em key={group}>{group}</em>)}</strong>
|
||||
{detail || name !== username ? <small>{[name !== username ? username : '', detail].filter(Boolean).join(' · ')}</small> : null}</span>
|
||||
{isOnline ? <span className={styles.online} role="img" aria-label="在线" /> : null}
|
||||
{removable ? <Button className={styles.remove} variant="ghost" disabled={busy} onClick={() => { void remove(username, invited); }}>{invited ? '撤销' : '移除'}</Button> : null}
|
||||
@@ -82,8 +87,9 @@ export function MembersPane({ owner, canRemove, refreshKey, onInvite }: Props) {
|
||||
};
|
||||
// 被授权的成员不能移除自己、空间主,也不能移除同样有移除权限的人
|
||||
const removable = (item: ChannelMember) => item.role !== 'owner' && (owner || (canRemove && item.username !== self && !permissionsOf(roles, item.username, false).has('members.remove')));
|
||||
// 在线的不用写时间;离线的写最后一次活跃(没登录过设备的老账号没有记录)
|
||||
const member = (item: ChannelMember) => row(item.username,
|
||||
item.devices > 1 || item.online_devices > 1 ? `${item.online_devices}/${item.devices} 台设备在线` : undefined,
|
||||
!item.online && item.last_seen > 0 ? `最后在线 ${formatSeen(item.last_seen)}` : undefined,
|
||||
false, removable(item), item.role === 'owner', item.online);
|
||||
|
||||
const viewed = viewing ? people[viewing] : undefined;
|
||||
|
||||
Reference in new issue
Block a user