import { MAvatarComponent } from '../../components/m-avatar/m-avatar';
import { ChangeDetectionStrategy, Component, computed, inject, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { UiMCell, UiMSearch, UiMTabs } from '../../../ui';
import { StatePanelComponent } from '../../../components/feedback/state-panel/state-panel';
import { StatusBadgeComponent } from '../../../components/domain/status-badge/status-badge';
import { Agent } from '../../../core/api/models';
import { ContactsService } from '../../../core/contacts.service';
import { LiveService } from '../../../core/live.service';
import { DeviceService } from '../../../core/device.service';
import { isMatrixReady } from '../../../core/format';
/**
* 移动·通讯录(SPEC FR-03)
* 三 Tab + 搜索 + 同事展开;行操作触达 ≥44px。
*/
@Component({
selector: 'app-mb-contacts-page',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [StatePanelComponent, StatusBadgeComponent, UiMSearch, MAvatarComponent, UiMCell, UiMTabs],
template: `
@switch (contacts.state()) {
@case ('loading') {
}
@case ('error') {
}
@case ('empty') {
}
@default {
@if (contacts.isEmpty()) {
} @else {
@if (contacts.tab() !== 'people') {
AI 数字生命
@for (a of contacts.visibleAgents(); track a.id) {
}
}
@if (contacts.tab() !== 'ai') {
同事
@for (p of contacts.visiblePeople(); track p.id) {
@if (contacts.expandedColleague() === p.id) {
@for (a of contacts.agentsOfPerson(p); track a.id) {
}
}
}
}
}
}
}
`,
styles: [
`
.m-page {
padding: var(--sp-5) var(--mob-gutter) var(--sp-6);
}
.m-head h1 {
margin: 0 0 var(--sp-4);
font-size: var(--fs-26);
color: var(--ink);
}
.m-search {
display: flex;
align-items: center;
gap: var(--sp-2);
min-height: 46px;
padding: 0 var(--sp-3);
margin-bottom: var(--sp-3);
border: 1px solid var(--edge);
border-radius: var(--r-md);
background: var(--surface);
color: var(--muted);
}
.m-search input {
flex: 1;
min-width: 0;
border: 0;
background: transparent;
color: var(--ink);
font-size: var(--fs-17);
}
.m-tabs {
display: flex;
gap: var(--sp-1);
padding: var(--sp-1);
margin-bottom: var(--sp-4);
border-radius: var(--r-md);
background: var(--surface-2);
}
.m-tab {
flex: 1;
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--sp-1);
min-height: var(--touch-min);
border: 0;
border-radius: var(--r-sm);
background: transparent;
color: var(--ink-2);
font-size: var(--fs-13);
cursor: pointer;
}
.m-tab.active {
background: var(--surface);
color: var(--ink);
font-weight: 600;
box-shadow: var(--shadow-soft);
}
.tab-count {
font-size: var(--fs-11);
opacity: 0.7;
}
.section-title {
margin: 0 0 var(--sp-3);
font-size: var(--fs-13);
color: var(--ink-2);
}
.section-title:not(:first-of-type) {
margin-top: var(--sp-5);
}
.rows {
display: grid;
gap: var(--sp-2);
}
.row {
display: flex;
align-items: center;
gap: var(--sp-3);
width: 100%;
min-height: 64px;
padding: var(--sp-2) var(--sp-3);
border: 1px solid var(--edge);
border-radius: var(--r-md);
background: var(--surface);
text-align: left;
cursor: pointer;
}
.row:active {
background: var(--surface-hover);
}
.row.child {
margin-left: var(--sp-4);
width: calc(100% - var(--sp-4));
background: var(--surface-2);
}
.row-body {
display: grid;
gap: 2px;
flex: 1;
min-width: 0;
}
.row-body strong {
font-size: var(--fs-15);
color: var(--ink);
}
.row-body small {
font-size: var(--fs-12);
color: var(--muted);
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`,
],
})
export class MbContactsPage implements OnInit {
readonly contacts = inject(ContactsService);
private readonly live = inject(LiveService);
private readonly router = inject(Router);
private readonly device = inject(DeviceService);
readonly tabs = [
{ key: 'all' as const, label: '全部' },
{ key: 'ai' as const, label: 'AI' },
{ key: 'people' as const, label: '同事' },
];
readonly tabItems = [{ label: '全部' }, { label: 'AI 数字生命' }, { label: '同事' }];
readonly tabIndex = computed(() => ({ all: 0, ai: 1, people: 2 })[this.contacts.tab()]);
onTabIndex(i: number): void {
this.contacts.setTab((['all', 'ai', 'people'] as const)[i] ?? 'all');
}
readonly counts = computed(() => {
const c = this.contacts.counts();
return { all: c.all, ai: c.ai, people: c.people };
});
async ngOnInit(): Promise