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
@@ -1,4 +1,6 @@
|
||||
import { useChannels } from '@/store/channels';
|
||||
import { spaceSeqKey, useReadState } from '@/store/readState';
|
||||
import { useSpaceReads } from './useThreadReads';
|
||||
import { useAccount } from '@/store/account';
|
||||
import { useWorkspace } from '@/store/workspace';
|
||||
import { channelPath, navigate } from '@/app/navigation';
|
||||
@@ -11,13 +13,19 @@ interface RailProps { id?: string; onCreate: () => void; onJoin: () => void }
|
||||
export function SpaceRail({ id, onCreate, onJoin }: RailProps) {
|
||||
const channels = useChannels((s) => s.list);
|
||||
const username = useAccount((s) => s.username);
|
||||
const reads = useReadState((s) => s.reads);
|
||||
useSpaceReads(id);
|
||||
return <nav className={styles.rail} aria-label="空间列表">
|
||||
{channels.map((channel) => {
|
||||
const name = channel.name || channel.id.slice(0, 8);
|
||||
return <button key={channel.id} type="button" className={`${styles.space} ${channel.id === id ? styles.current : ''}`}
|
||||
aria-current={channel.id === id ? 'page' : undefined} title={name} aria-label={name} onClick={() => navigate(channelPath(channel.id))}>
|
||||
{channel.avatar ? <img className={styles.avatar} src={channel.avatar} alt="" /> : Array.from(name)[0]?.toLocaleUpperCase()}
|
||||
</button>;
|
||||
const fresh = channel.id !== id && channel.lastSeq !== undefined ? Math.max(0, channel.lastSeq - (reads[spaceSeqKey(channel.id)] ?? Infinity)) : 0;
|
||||
return <div key={channel.id} className={styles.slot}>
|
||||
<button type="button" className={`${styles.space} ${channel.id === id ? styles.current : ''}`}
|
||||
aria-current={channel.id === id ? 'page' : undefined} title={name} aria-label={fresh ? `${name}(${fresh} 条新动静)` : name} onClick={() => navigate(channelPath(channel.id))}>
|
||||
{channel.avatar ? <img className={styles.avatar} src={channel.avatar} alt="" /> : Array.from(name)[0]?.toLocaleUpperCase()}
|
||||
</button>
|
||||
{fresh ? <span className={styles.dot} aria-hidden="true">{fresh > 99 ? '99+' : fresh}</span> : null}
|
||||
</div>;
|
||||
})}
|
||||
<button type="button" className={`${styles.space} ${styles.action}`} title="新建空间" aria-label="新建空间" disabled={!username} onClick={onCreate}><Icon name="plus" size={20} /></button>
|
||||
<button type="button" className={`${styles.space} ${styles.action}`} title="加入空间" aria-label="加入空间" onClick={onJoin}><Icon name="scan" size={20} /></button>
|
||||
|
||||
Reference in new issue
Block a user