Уведомления, Real-time синхронизация, фотки, reply and forward

This commit is contained in:
2026-03-17 03:51:29 +05:00
parent 624038915d
commit 1f442e1298
26 changed files with 2711 additions and 431 deletions

View File

@@ -383,44 +383,56 @@ private struct ToolbarTitleView: View {
}
}
/// Desktop parity: circular spinner + status text (Mantine `<Loader size={12}>` equivalent).
/// Status text label without spinner (spinner is in ToolbarStoriesAvatar).
private struct ToolbarStatusLabel: View {
let title: String
@State private var isSpinning = false
var body: some View {
HStack(spacing: 5) {
Circle()
.trim(from: 0.05, to: 0.75)
.stroke(RosettaColors.Adaptive.text, style: StrokeStyle(lineWidth: 1.5, lineCap: .round))
.frame(width: 12, height: 12)
.rotationEffect(.degrees(isSpinning ? 360 : 0))
.animation(.linear(duration: 1.2).repeatForever(autoreverses: false), value: isSpinning)
Text(title)
.font(.system(size: 17, weight: .semibold))
.foregroundStyle(RosettaColors.Adaptive.text)
}
.onAppear { isSpinning = true }
Text(title)
.font(.system(size: 17, weight: .semibold))
.foregroundStyle(RosettaColors.Adaptive.text)
}
}
// MARK: - Toolbar Stories Avatar (observation-isolated)
/// Reads `AccountManager` and `SessionManager` in its own observation scope.
/// Changes to these `@Observable` singletons only re-render this small view,
/// not the parent ChatListView / NavigationStack.
/// Reads `AccountManager`, `SessionManager`, and `ProtocolManager` in its own observation scope.
/// Shows a spinning arc loader during connecting/syncing, then crossfades to avatar.
private struct ToolbarStoriesAvatar: View {
@State private var isSpinning = false
var body: some View {
let pk = AccountManager.shared.currentAccount?.publicKey ?? ""
let state = ProtocolManager.shared.connectionState
let isSyncing = SessionManager.shared.syncBatchInProgress
let isLoading = state != .authenticated || isSyncing
let initials = RosettaColors.initials(
name: SessionManager.shared.displayName, publicKey: pk
)
let colorIdx = RosettaColors.avatarColorIndex(for: SessionManager.shared.displayName, publicKey: pk)
// Reading avatarVersion triggers observation re-renders when any avatar is saved/removed.
let _ = AvatarRepository.shared.avatarVersion
let avatar = AvatarRepository.shared.loadAvatar(publicKey: pk)
ZStack { AvatarView(initials: initials, colorIndex: colorIdx, size: 28, image: avatar) }
ZStack {
// Avatar visible when loaded
AvatarView(initials: initials, colorIndex: colorIdx, size: 28, image: avatar)
.opacity(isLoading ? 0 : 1)
// Spinning arc loader visible during connecting/syncing
Circle()
.trim(from: 0.05, to: 0.78)
.stroke(
RosettaColors.figmaBlue,
style: StrokeStyle(lineWidth: 2, lineCap: .round)
)
.frame(width: 20, height: 20)
.rotationEffect(.degrees(isSpinning ? 360 : 0))
.opacity(isLoading ? 1 : 0)
}
.animation(.easeInOut(duration: 0.3), value: isLoading)
.onAppear { isSpinning = true }
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: isSpinning)
}
}