Skip to content

联系人服务

管理用户的联系人和地址簿


概述

联系人服务提供地址簿管理功能,支持:

  • 一个联系人保存多个地址(不同链)
  • 自动识别地址格式
  • 地址输入时的联系人建议
  • 联系人搜索和过滤

数据模型

Contact(联系人)

typescript
interface Contact {
  id: string                    // UUID
  name: string                  // 联系人名称
  avatar?: string               // 头像(可选,emoji 或图片URL)
  addresses: ContactAddress[]   // 多个地址
  memo?: string                 // 备注
  createdAt: number            // 创建时间
  updatedAt: number            // 更新时间
}

ContactAddress(联系人地址)

typescript
interface ContactAddress {
  id: string                    // UUID
  address: string               // 地址
  chainType: ChainType          // 链类型(自动检测或手动指定)
  label?: string                // 地址标签(如"主地址"、"交易所")
  isDefault?: boolean           // 是否默认地址
}

地址格式识别

支持的格式

链类型地址格式示例
Ethereum0x + 40位hex0x742d35Cc...
BSC同 Ethereum0x742d35Cc...
TRONT + 33位base58TJYs...
Bitcoin Legacy1 + 25-34位base581A1zP1...
Bitcoin P2SH3 + 25-34位base583J98t1...
Bitcoin Bech32bc1 + 25-89位bc1qar0...
BFMetab + base58checkb7ADmv...
CCChainc + base58checkcXXXXX...
PMChainp + base58checkpXXXXX...

检测算法

typescript
function detectAddressFormat(address: string): AddressFormatInfo {
  // 1. 检查 0x 前缀 → Ethereum/BSC
  // 2. 检查 T 前缀 + 长度 → TRON
  // 3. 检查 1/3/bc1 前缀 → Bitcoin
  // 4. 检查 BioForest 前缀 (b/c/p/B/g/w/e/m) → 对应链
  // 5. 返回 unknown
}

服务接口

IContactService

typescript
interface IContactService {
  // 联系人 CRUD
  getContacts(): Contact[]
  getContactById(id: string): Contact | undefined
  addContact(contact: Omit<Contact, 'id' | 'createdAt' | 'updatedAt'>): Contact
  updateContact(id: string, updates: Partial<Contact>): void
  deleteContact(id: string): void
  
  // 地址操作
  addAddressToContact(contactId: string, address: ContactAddress): void
  removeAddressFromContact(contactId: string, addressId: string): void
  
  // 搜索和建议
  searchContacts(query: string): Contact[]
  suggestContacts(partialAddress: string): ContactSuggestion[]
  getContactByAddress(address: string): Contact | undefined
  
  // 导入导出
  importContacts(contacts: Contact[]): number
  exportContacts(): Contact[]
}

ContactSuggestion(联系人建议)

typescript
interface ContactSuggestion {
  contact: Contact
  matchedAddress: ContactAddress
  matchType: 'exact' | 'prefix' | 'name'
  score: number  // 匹配分数,用于排序
}

地址输入增强

AddressInput 组件增强

┌──────────────────────────────────────────────┐
│ [输入地址...]                    [扫描] [粘贴] │
├──────────────────────────────────────────────┤
│ 👤 Alice                                      │
│    b7ADmv... (BFMeta)                        │
├──────────────────────────────────────────────┤
│ 👤 Bob                                        │
│    0x742d... (Ethereum)                       │
├──────────────────────────────────────────────┤
│ [👥 查看全部联系人...]                         │
└──────────────────────────────────────────────┘

交互流程

  1. 聚焦即展开:用户聚焦输入框时,自动展开建议面板
  2. 默认显示:即使无输入,显示最近使用 + 所有联系人地址
  3. 链类型过滤:根据当前选择的链类型过滤显示的地址
  4. 实时搜索:输入时实时过滤匹配的联系人(按名称或地址前缀)
  5. 选择填入:用户选择联系人后,自动填入对应地址
  6. 联系人入口:面板底部显示"查看全部联系人"按钮,跳转到 ContactPickerActivity

建议排序规则

  1. 精确匹配(地址完全相同)→ 最高优先级
  2. 前缀匹配(地址以输入开头)
  3. 名称匹配(联系人名称包含输入)
  4. 最近使用(按 updatedAt 降序)
  5. 同链优先(当前链的地址排在前面)

ContactPickerActivity

联系人选择器页面,用于完整浏览和选择联系人。

功能

  • 显示所有联系人列表
  • 支持搜索过滤
  • 按链类型过滤(可选)
  • 选择联系人地址后返回到调用页面

入口

  • AddressInput 建议面板底部的"查看全部联系人"按钮
  • 可通过 push('ContactPickerActivity', { chainType, onSelect }) 调用

UI 结构

┌──────────────────────────────────────────────┐
│ ← 选择联系人                      [链过滤 ▼] │
├──────────────────────────────────────────────┤
│ [🔍 搜索联系人...]                            │
├──────────────────────────────────────────────┤
│ 👤 Alice                                      │
│    b7ADmv... (BFMeta) [默认]                 │
│    0x742d... (Ethereum)                       │
├──────────────────────────────────────────────┤
│ 👤 Bob                                        │
│    TJYs... (TRON)                            │
└──────────────────────────────────────────────┘

存储

使用 localStorage 持久化,key 为 bfm_contacts

typescript
// 存储格式
{
  version: 2,  // 数据版本,用于迁移
  contacts: Contact[]
}

数据迁移

从 v1(单地址)迁移到 v2(多地址):

typescript
function migrateV1ToV2(v1Contact: V1Contact): Contact {
  return {
    ...v1Contact,
    addresses: [{
      id: crypto.randomUUID(),
      address: v1Contact.address,
      chainType: v1Contact.chain ?? detectAddressFormat(v1Contact.address).chainType,
    }]
  }
}

与转账流程集成

发送页面

  1. 地址输入框显示联系人建议
  2. 选择联系人时,如果有多个地址,弹出地址选择器
  3. 如果联系人只有一个地址,直接填入

交易历史

  1. 显示交易对方时,优先显示联系人名称
  2. 提供"添加到联系人"操作

联系人分享与扫码添加

联系人协议

支持通过二维码分享和添加联系人。

JSON 格式(推荐)

json
{
  "type": "contact",
  "name": "张三",
  "addresses": [
    { "chainType": "ethereum", "address": "0x742d35Cc..." },
    { "chainType": "bitcoin", "address": "bc1qar0..." }
  ],
  "memo": "好友",
  "avatar": "👨‍💼"
}

URI 格式

contact://张三?eth=0x742d35Cc...&btc=bc1qar0...&memo=好友

分享名片流程

  1. 在通讯录页面,点击联系人菜单选择"分享"
  2. 打开 ContactShareJob 显示联系人二维码
  3. 可选择下载图片或分享给他人

扫码添加流程

  1. 扫描联系人二维码
  2. 解析为 contact 类型
  3. 打开 ContactAddConfirmJob 确认界面
  4. 用户确认后保存到通讯录

相关组件

  • ContactShareJob: 分享联系人名片二维码
  • ContactAddConfirmJob: 扫码后确认添加联系人
  • 详见 Scanner 组件

安全考虑

  • 联系人数据不加密(非敏感数据)
  • 删除联系人需要验证钱包锁(防止误操作)
  • 导出功能可选加密

相关链接

Released under the MIT License.