mirror of
https://github.com/luxfhe/example-ios-demo.git
synced 2026-10-30 18:13:24 +00:00
This app is ZAMA's BSD 3-Clause Clear demo. The license requires its notices to survive redistribution, and a rebrand had replaced every one of them with ours: the LICENSE text, 66 source headers and the app's copyright string. They are back, each with our line beside it for our changes. Co-authored-by: Hanzo Dev <[email protected]>
143 lines
4.9 KiB
Swift
143 lines
4.9 KiB
Swift
// Copyright © 2025 Zama. All rights reserved.
|
|
// Copyright © 2025 Lux Industries Inc.
|
|
|
|
import SwiftUI
|
|
|
|
#Preview {
|
|
SocialTimeline()
|
|
}
|
|
|
|
struct SocialTimeline: View {
|
|
@StateObject private var vm = ViewModel()
|
|
@Environment(\.openURL) private var openURL
|
|
|
|
var body: some View {
|
|
VStack {
|
|
titleBar
|
|
.padding(.top, 8)
|
|
|
|
if vm.dataVaultActionNeeded {
|
|
VStack(spacing: 40) {
|
|
let icon = Image(systemName: "exclamationmark.triangle.fill")
|
|
Text("\(icon)\n\nNo Profile found")
|
|
.customFont(.title3)
|
|
.multilineTextAlignment(.center)
|
|
|
|
OpenAppButton(.luxDataVault(tab: .profile))
|
|
.buttonStyle(.lux)
|
|
.padding(.horizontal, 20)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
} else {
|
|
ScrollView {
|
|
ForEach(vm.items) { item in
|
|
switch item {
|
|
case .post(let post): postView(post)
|
|
case .ad(let index, let hash): adView(position: index, profileHash: hash)
|
|
}
|
|
Divider()
|
|
.frame(height: 1)
|
|
.background(.black)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
}
|
|
}
|
|
}
|
|
.background(Color.luxGreyBackground)
|
|
.overlay(alignment: .topTrailing) {
|
|
if !vm.dataVaultActionNeeded {
|
|
LuxLink()
|
|
}
|
|
}
|
|
.onAppearAgain {
|
|
vm.refreshFromDisk()
|
|
}
|
|
}
|
|
|
|
private var titleBar: some View {
|
|
VStack(spacing: 4) {
|
|
Text("FHE Ads")
|
|
.customFont(.largeTitle)
|
|
.frame(maxWidth: .infinity)
|
|
.overlay(alignment: .leading) {
|
|
if !vm.dataVaultActionNeeded {
|
|
OpenAppButton(.luxDataVault(tab: .profile)) {
|
|
Image(systemName: "pencil.circle.fill")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 32, height: 32)
|
|
.padding(8)
|
|
}
|
|
.tint(.black)
|
|
}
|
|
}
|
|
|
|
if let report = vm.activityReport {
|
|
HStack(spacing: 2) {
|
|
switch report {
|
|
case .progress(let string):
|
|
Text(string)
|
|
ProgressView()
|
|
.scaleEffect(0.8)
|
|
|
|
case .error(let string):
|
|
Text("⚠️ " + string)
|
|
}
|
|
}
|
|
.customFont(.footnote)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func postView(_ post: Post) -> some View {
|
|
GroupBox {
|
|
HStack(alignment: .top) {
|
|
Image(systemName: "person.crop.circle.fill")
|
|
.resizable()
|
|
.frame(width: 32, height: 32)
|
|
.foregroundStyle([.yellow, .orange, .gray].randomElement()!)
|
|
|
|
VStack(alignment: .leading) {
|
|
HStack(alignment: .firstTextBaseline) {
|
|
Text(post.username)
|
|
.customFont(.headline)
|
|
|
|
Text(post.handle)
|
|
.customFont(.subheadline)
|
|
.foregroundColor(.gray)
|
|
|
|
Spacer()
|
|
|
|
Text(post.timestamp)
|
|
.customFont(.subheadline)
|
|
.foregroundColor(.gray)
|
|
}
|
|
.padding(.bottom, 2)
|
|
|
|
Text(post.content)
|
|
.customFont(.callout)
|
|
.foregroundColor(.primary)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func adView(position: Int, profileHash: String) -> some View {
|
|
FilePreview(url: Storage.url(for: .torusEncryptedResult, suffix: "\(profileHash)-\(position)"))
|
|
.frame(minHeight: 175)
|
|
.overlay {
|
|
Color.white.opacity(0.01) // Hack to allow scrolling from this view
|
|
}
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
.overlay(alignment: .bottomTrailing) {
|
|
Text("Privately Targeted Ad")
|
|
.customFont(.subheadline)
|
|
.foregroundColor(.gray)
|
|
.padding(4)
|
|
}
|
|
}
|
|
}
|