349d94aa8f
- 原生数据面 libwebrtc(PLAN 决策 F):新增 Engine/LibWebRtcEngine.swift(RTCPeerConnection,端口 engine.go/session.go 线协议 + 水位背压 + ack 完成 + 不冲突落盘);NativeTransfer.swift 的 P2PEventsBridge 改 conform P2PEngineDelegate(去 gomobile);EngineController 4 个 p2p RPC 改调新引擎(completion-based + resolveOnMain)。stasel/WebRTC M149 经 SPM(exactVersion 149.0.0)。环回端到端 20MB 逐字节测过(Tests/) - UX 改造(PARITY 残余项 1/2):新增 SendComposeSheet(发送预览=文件名 + 选设备,手动多选与分享共用);分享直入预览 + scenePhase .active 兜底(修“分享后必须重启 app”);重写 MessagesView(按设备会话 + 每设备未读红点 + 删单条 / 删整设备历史);EngineController 加 conversations / unread 模型 - 品牌 logo:新增 Shared/Brand.swift(BrandMark / Wordmark / BrandLockup / BrandHero)+ Brand.xcassets/LogoMark;登录页 / 传输空态 / 设置头 / 控制中心控件接 logo;Theme 加 cdropAmber - 字体(对齐 web,仅主 app):新增 BrandFonts.swift(Orbit Gothic + Maple Mono woff2 运行期 CTFontManager 注册 + UIKit 外观代理 + .oXxx / .maple 助手);Fredoka SemiBold 字标;7 视图 .font sweep。字体不入库——.gitignore 排除 + just ios-fonts 构建前从 CDN / Google Fonts 拉取,见 Sources/Fonts/README.md - 分享引导:新增 Share/ShareGuideView(吉祥物卡 + “打开 Commilitia Drop”);ShareViewController 健壮 staging(文件 / 数据双路)+ 响应链 IMP 唤起宿主 - 全平台平均速度:传输完成态显平均速度(总字节 / 总耗时)而非末次瞬时——web store/slices/transfer.ts + TransferRow.tsx + iOS RootView 卡片 / 详情;i18n 加 transfer.avgRate - 工程:project.yml 接 SPM WebRTC + CDropTests + Fredoka UIAppFonts + Share i18n;Justfile 加 ios-sim-build + ios-fonts / ios-fonts-fredoka 配方 - 文档:ios/PLAN.md(决策 F + §10 UX + §11 品牌/字体 + §12 平均速度)、ios/PARITY.md(字体分叉条目更新 + 2026-06-28 漂移行)
81 lines
2.5 KiB
Swift
81 lines
2.5 KiB
Swift
import SwiftUI
|
||
|
||
// 瞬时通知横幅(#2):覆盖在标签内容顶部,醒目、状态分明(成功绿 / 失败红 / 信息中性)、自动消失
|
||
// (EngineController.notify 安排 4s 后移除)、可点叉手动清除。取代原先散落在设备页 / 设置页、串页显示
|
||
// 且永不清除的灰色小字(deviceActionStatus / clipboardStatus)。由 EngineController.notices 驱动。
|
||
struct NoticeOverlay: View
|
||
{
|
||
@Environment(EngineController.self) private var engine
|
||
|
||
var body: some View
|
||
{
|
||
VStack(spacing: 8)
|
||
{
|
||
ForEach(engine.notices)
|
||
{ notice in
|
||
NoticeBanner(notice: notice) { engine.dismissNotice(notice.id) }
|
||
.transition(.move(edge: .top).combined(with: .opacity))
|
||
}
|
||
}
|
||
.padding(.horizontal, 16)
|
||
.padding(.top, 8)
|
||
.animation(.spring(duration: 0.3), value: engine.notices)
|
||
// 无通知时不拦截下方内容的点击;有通知时仅横幅自身(含叉按钮)可点。
|
||
.allowsHitTesting(!engine.notices.isEmpty)
|
||
}
|
||
}
|
||
|
||
private struct NoticeBanner: View
|
||
{
|
||
let notice: Notice
|
||
let onDismiss: () -> Void
|
||
|
||
var body: some View
|
||
{
|
||
HStack(spacing: 10)
|
||
{
|
||
Image(systemName: symbol)
|
||
.font(.oHeadline)
|
||
.foregroundStyle(tint)
|
||
Text(notice.text)
|
||
.font(.oSubheadline)
|
||
.foregroundStyle(.primary)
|
||
.lineLimit(2)
|
||
Spacer(minLength: 8)
|
||
Button { onDismiss() }
|
||
label:
|
||
{
|
||
Image(systemName: "xmark")
|
||
.font(.caption.weight(.bold))
|
||
.foregroundStyle(.secondary)
|
||
}
|
||
.buttonStyle(.plain)
|
||
}
|
||
.padding(.vertical, 12)
|
||
.padding(.horizontal, 14)
|
||
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 14))
|
||
.overlay(RoundedRectangle(cornerRadius: 14).strokeBorder(tint.opacity(0.35), lineWidth: 1))
|
||
.shadow(color: .black.opacity(0.12), radius: 8, y: 2)
|
||
}
|
||
|
||
private var symbol: String
|
||
{
|
||
switch notice.kind
|
||
{
|
||
case .success: return "checkmark.circle.fill"
|
||
case .error: return "exclamationmark.triangle.fill"
|
||
case .info: return "info.circle.fill"
|
||
}
|
||
}
|
||
|
||
private var tint: Color
|
||
{
|
||
switch notice.kind
|
||
{
|
||
case .success: return .green
|
||
case .error: return .red
|
||
case .info: return .cdropAccent
|
||
}
|
||
}
|
||
}
|