mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-22 03:40:17 +00:00
wip
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// BadgeUserPreview.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 30.07.2026.
|
||||
// Copyright © 2026 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
// Shows the user's avatar and display name with a synthesized preview badge at the selected
|
||||
// level, using the same NameWithBadge helper that renders badges everywhere else in the app —
|
||||
// so the preview matches what will actually appear on the user's name. Any picker affordance
|
||||
// (e.g. a chevron) is supplied by the caller via `trailing`, and is laid out next to the
|
||||
// name row without leaking into this view.
|
||||
struct BadgeUserPreview<Trailing: View>: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
let level: BadgeLevel
|
||||
let trailing: () -> Trailing
|
||||
|
||||
init(level: BadgeLevel, @ViewBuilder trailing: @escaping () -> Trailing = { EmptyView() }) {
|
||||
self.level = level
|
||||
self.trailing = trailing
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let user = chatModel.currentUser
|
||||
let displayName = user?.displayName ?? NSLocalizedString("My nickname", comment: "badges preview placeholder")
|
||||
let previewBadge = LocalBadge(
|
||||
badge: BadgeInfo(badgeType: level.badgeType),
|
||||
status: .active
|
||||
)
|
||||
return VStack(spacing: 12) {
|
||||
ProfileImage(imageStr: user?.image, size: 128)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
NameWithBadge(Text(displayName).font(.largeTitle), previewBadge, .largeTitle)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
trailing()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ enum BadgePeriod: String, CaseIterable, Identifiable {
|
||||
}
|
||||
|
||||
struct BadgesPayView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
let level: BadgeLevel
|
||||
@State private var selectedPeriod: BadgePeriod = .subscribe
|
||||
@@ -49,7 +48,7 @@ struct BadgesPayView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
userPreview()
|
||||
BadgeUserPreview(level: level)
|
||||
.padding(.top, 4)
|
||||
|
||||
Text(level.tagline)
|
||||
@@ -86,23 +85,6 @@ struct BadgesPayView: View {
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
|
||||
private func userPreview() -> some View {
|
||||
let user = chatModel.currentUser
|
||||
return VStack(spacing: 12) {
|
||||
ProfileImage(imageStr: user?.image, size: 128)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Text(user?.displayName ?? NSLocalizedString("My nickname", comment: "badges preview placeholder"))
|
||||
.font(.largeTitle)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
Image(level.badgeAsset)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 28, height: 28)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func periodCard(_ period: BadgePeriod) -> some View {
|
||||
let isSelected = period == selectedPeriod
|
||||
return Button {
|
||||
|
||||
@@ -72,10 +72,16 @@ enum BadgeLevel: String, CaseIterable, Identifiable {
|
||||
case .legend: "badge-legend"
|
||||
}
|
||||
}
|
||||
|
||||
var badgeType: BadgeType {
|
||||
switch self {
|
||||
case .supporter: .supporter
|
||||
case .legend: .legend
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BadgesYourLevelView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@State private var selectedLevel: BadgeLevel = .supporter
|
||||
@State private var continueActive = false
|
||||
@@ -92,8 +98,12 @@ struct BadgesYourLevelView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
userPreview()
|
||||
.padding(.top, 4)
|
||||
BadgeUserPreview(level: selectedLevel) {
|
||||
Image(systemName: "chevron.down")
|
||||
.font(.body)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
}
|
||||
.padding(.top, 4)
|
||||
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
levelCard(.supporter)
|
||||
@@ -118,29 +128,6 @@ struct BadgesYourLevelView: View {
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
|
||||
// The avatar + name preview shows the user how their profile will look with the selected badge.
|
||||
// Uses the current user's real profile image and display name; when SIMPLEX_ASSETS is absent the
|
||||
// avatar falls back to ProfileImage's own default. TODO [badges] wire real LocalBadge preview.
|
||||
private func userPreview() -> some View {
|
||||
let user = chatModel.currentUser
|
||||
return VStack(spacing: 12) {
|
||||
ProfileImage(imageStr: user?.image, size: 128)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Text(user?.displayName ?? NSLocalizedString("My nickname", comment: "badges preview placeholder"))
|
||||
.font(.largeTitle)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
Image(selectedLevel.badgeAsset)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 28, height: 28)
|
||||
Image(systemName: "chevron.down")
|
||||
.font(.body)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func levelCard(_ level: BadgeLevel) -> some View {
|
||||
let isSelected = level == selectedLevel
|
||||
return Button {
|
||||
|
||||
@@ -123,13 +123,27 @@ struct SupportSimpleXBanner: View {
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func gradientBackground() -> some View {
|
||||
let stops = colorScheme == .light ? OnboardingCardView.lightStops : OnboardingCardView.darkStops
|
||||
return LinearGradient(
|
||||
colors: [stops.first!.color, stops.last!.color],
|
||||
startPoint: .bottomLeading,
|
||||
endPoint: .topTrailing
|
||||
)
|
||||
// Light: just the first and last stops of OnboardingCardView.lightStops as a simple
|
||||
// 2-color gradient — the intermediate stops there include a hard blue plateau (0.0–0.5)
|
||||
// that reads as "almost entirely blue" on this wide-short banner.
|
||||
// Dark: the full OnboardingCardView.darkStops (all 5 locations) — the intermediate blue-to-
|
||||
// navy transitions land nicely on this shape, giving depth we'd lose with only 2 colors.
|
||||
if colorScheme == .light {
|
||||
let stops = OnboardingCardView.lightStops
|
||||
LinearGradient(
|
||||
colors: [stops.first!.color, stops.last!.color],
|
||||
startPoint: .bottomLeading,
|
||||
endPoint: .topTrailing
|
||||
)
|
||||
} else {
|
||||
LinearGradient(
|
||||
stops: OnboardingCardView.darkStops,
|
||||
startPoint: .bottomLeading,
|
||||
endPoint: .topTrailing
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -256,6 +256,7 @@
|
||||
E5BADE0107000000BADE0004 /* BadgesRedeemCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BADE0108000000BADE0004 /* BadgesRedeemCodeView.swift */; };
|
||||
E5BADE0109000000BADE0005 /* BadgesHowItWorksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BADE010A000000BADE0005 /* BadgesHowItWorksView.swift */; };
|
||||
E5BADE010B000000BADE0006 /* SupportSimpleXBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BADE010C000000BADE0006 /* SupportSimpleXBanner.swift */; };
|
||||
E5BADE010D000000BADE0007 /* BadgeUserPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BADE010E000000BADE0007 /* BadgeUserPreview.swift */; };
|
||||
E559A0A12E3F77EE00B26F74 /* CommandsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E559A0A02E3F77EE00B26F74 /* CommandsMenuView.swift */; };
|
||||
E5A0B0012F960000AAAA0001 /* YourNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A0B0022F960000AAAA0001 /* YourNetwork.swift */; };
|
||||
E5AEC0AB2F91A6EB00270665 /* CIChatLinkHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5AEC0AA2F91A6EA00270665 /* CIChatLinkHeader.swift */; };
|
||||
@@ -638,6 +639,7 @@
|
||||
E5BADE0108000000BADE0004 /* BadgesRedeemCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgesRedeemCodeView.swift; sourceTree = "<group>"; };
|
||||
E5BADE010A000000BADE0005 /* BadgesHowItWorksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgesHowItWorksView.swift; sourceTree = "<group>"; };
|
||||
E5BADE010C000000BADE0006 /* SupportSimpleXBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportSimpleXBanner.swift; sourceTree = "<group>"; };
|
||||
E5BADE010E000000BADE0007 /* BadgeUserPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeUserPreview.swift; sourceTree = "<group>"; };
|
||||
E559A0A02E3F77EE00B26F74 /* CommandsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandsMenuView.swift; sourceTree = "<group>"; };
|
||||
E5A0B0022F960000AAAA0001 /* YourNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YourNetwork.swift; sourceTree = "<group>"; };
|
||||
E5AEC0AA2F91A6EA00270665 /* CIChatLinkHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIChatLinkHeader.swift; sourceTree = "<group>"; };
|
||||
@@ -1064,6 +1066,7 @@
|
||||
E5BADE0108000000BADE0004 /* BadgesRedeemCodeView.swift */,
|
||||
E5BADE010A000000BADE0005 /* BadgesHowItWorksView.swift */,
|
||||
E5BADE010C000000BADE0006 /* SupportSimpleXBanner.swift */,
|
||||
E5BADE010E000000BADE0007 /* BadgeUserPreview.swift */,
|
||||
);
|
||||
path = Badges;
|
||||
sourceTree = "<group>";
|
||||
@@ -1563,6 +1566,7 @@
|
||||
E5BADE0107000000BADE0004 /* BadgesRedeemCodeView.swift in Sources */,
|
||||
E5BADE0109000000BADE0005 /* BadgesHowItWorksView.swift in Sources */,
|
||||
E5BADE010B000000BADE0006 /* SupportSimpleXBanner.swift in Sources */,
|
||||
E5BADE010D000000BADE0007 /* BadgeUserPreview.swift in Sources */,
|
||||
5C65DAF929D0CC20003CEE45 /* DeveloperView.swift in Sources */,
|
||||
5C36027327F47AD5009F19D9 /* AppDelegate.swift in Sources */,
|
||||
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */,
|
||||
|
||||
@@ -275,11 +275,22 @@ public struct BadgeInfo: Codable, Hashable {
|
||||
public var badgeType: BadgeType
|
||||
public var badgeExpiry: Date?
|
||||
public var badgeExtra: String
|
||||
|
||||
public init(badgeType: BadgeType, badgeExpiry: Date? = nil, badgeExtra: String = "") {
|
||||
self.badgeType = badgeType
|
||||
self.badgeExpiry = badgeExpiry
|
||||
self.badgeExtra = badgeExtra
|
||||
}
|
||||
}
|
||||
|
||||
public struct LocalBadge: Codable, Hashable {
|
||||
public var badge: BadgeInfo
|
||||
public var status: BadgeStatus
|
||||
|
||||
public init(badge: BadgeInfo, status: BadgeStatus) {
|
||||
self.badge = badge
|
||||
self.status = status
|
||||
}
|
||||
}
|
||||
|
||||
// the wire proof carried on a profile - opaque to the UI, only round-tripped back to the core (apiPrepareContact)
|
||||
|
||||
Reference in New Issue
Block a user