Revert "hogar compartido: colaboracion entre cuentas sobre Firestore"
El hogar compartido cambia donde viven los datos, asi que se va a la 3.0 y la 2.x queda para correcciones. El trabajo sigue vivo en la rama 3.0.0 (163fd60). Importante para esta rama: esto tambien retira el entitlement com.apple.developer.applesignin, que sin la capability marcada en el App ID habria roto la firma al subir la 2.1.1 a TestFlight. This reverts commit163fd60. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013su1ttRiMeMYxkZJ1Y3246
This commit is contained in:
@@ -1,309 +0,0 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
import AuthenticationServices
|
||||
|
||||
/// Household screen: sign in, create or join a household, see who is in it and
|
||||
/// leave. The content sync itself runs in `HouseholdSyncService`.
|
||||
struct HouseholdView: View {
|
||||
@Environment(\.modelContext) private var context
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Query private var allSettings: [AppSettings]
|
||||
|
||||
@StateObject private var auth = AuthService.shared
|
||||
@StateObject private var households = HouseholdService.shared
|
||||
@StateObject private var sync = HouseholdSyncService.shared
|
||||
|
||||
@State private var householdName: String = ""
|
||||
@State private var joinCode: String = ""
|
||||
@State private var isWorking = false
|
||||
@State private var errorMessage: String?
|
||||
@State private var showJoinChoice = false
|
||||
@State private var showLeaveConfirm = false
|
||||
@State private var showPaywall = false
|
||||
|
||||
private var settings: AppSettings? { allSettings.first }
|
||||
private var isPremium: Bool { settings?.isPremium ?? false }
|
||||
private var isInHousehold: Bool { HouseholdRuntime.householdId != nil }
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
if HouseholdRuntime.needsRelaunch {
|
||||
Section {
|
||||
Label("household_relaunch_needed", systemImage: "arrow.clockwise.circle.fill")
|
||||
.font(.mealMoodSmall)
|
||||
.foregroundColor(.mealMoodWarning)
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
if !auth.isSignedIn {
|
||||
signInSection
|
||||
} else if isInHousehold {
|
||||
householdSection
|
||||
membersSection
|
||||
inviteSection
|
||||
leaveSection
|
||||
} else {
|
||||
createSection
|
||||
joinSection
|
||||
}
|
||||
|
||||
if let errorMessage {
|
||||
Section {
|
||||
Text(errorMessage)
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodWarning)
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
}
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(Color.mealMoodBackground.ignoresSafeArea())
|
||||
.tint(.mealMoodCoral)
|
||||
.navigationTitle("household_title")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.disabled(isWorking)
|
||||
.onAppear {
|
||||
households.start()
|
||||
AnalyticsService.logScreenView("Household")
|
||||
}
|
||||
.sheet(isPresented: $showPaywall) {
|
||||
if let settings {
|
||||
NavigationStack {
|
||||
PremiumView(settings: settings, source: "household")
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog("household_join_choice_title", isPresented: $showJoinChoice, titleVisibility: .visible) {
|
||||
Button("household_join_choice_merge") { join(bringingLocalContent: true) }
|
||||
Button("household_join_choice_replace", role: .destructive) { join(bringingLocalContent: false) }
|
||||
Button("reset_cancel", role: .cancel) {}
|
||||
} message: {
|
||||
Text("household_join_choice_message")
|
||||
}
|
||||
.alert("household_leave_confirm_title", isPresented: $showLeaveConfirm) {
|
||||
Button("reset_cancel", role: .cancel) {}
|
||||
Button("household_leave_confirm_confirm", role: .destructive) { leave() }
|
||||
} message: {
|
||||
Text("household_leave_confirm_message")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sections
|
||||
|
||||
private var signInSection: some View {
|
||||
Section {
|
||||
Text("household_intro")
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
|
||||
SignInWithAppleButton(.signIn) { request in
|
||||
auth.prepare(request: request)
|
||||
} onCompletion: { result in
|
||||
Task {
|
||||
do {
|
||||
try await auth.completeSignInWithApple(result)
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
.signInWithAppleButtonStyle(.black)
|
||||
.frame(height: 46)
|
||||
.listRowInsets(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
|
||||
} footer: {
|
||||
Text("household_sign_in_footer")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var createSection: some View {
|
||||
Section {
|
||||
TextField("household_name_placeholder", text: $householdName)
|
||||
Button {
|
||||
guard isPremium else {
|
||||
showPaywall = true
|
||||
AnalyticsService.logEvent("paywall_viewed", parameters: ["source": "household_create"])
|
||||
return
|
||||
}
|
||||
create()
|
||||
} label: {
|
||||
HStack {
|
||||
Label("household_create", systemImage: "house.fill")
|
||||
if !isPremium {
|
||||
Spacer()
|
||||
Image(systemName: "star.circle.fill").foregroundColor(.mealMoodCoral)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(householdName.trimmingCharacters(in: .whitespaces).isEmpty)
|
||||
} header: {
|
||||
Text("household_create_section")
|
||||
} footer: {
|
||||
if isPremium {
|
||||
Text("household_create_footer")
|
||||
} else {
|
||||
Text("household_create_premium_footer")
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var joinSection: some View {
|
||||
Section {
|
||||
TextField("household_code_placeholder", text: $joinCode)
|
||||
.textInputAutocapitalization(.characters)
|
||||
.autocorrectionDisabled()
|
||||
Button {
|
||||
showJoinChoice = true
|
||||
} label: {
|
||||
Label("household_join", systemImage: "person.badge.plus")
|
||||
}
|
||||
.disabled(!HouseholdService.isPlausibleCode(joinCode))
|
||||
} header: {
|
||||
Text("household_join_section")
|
||||
} footer: {
|
||||
Text("household_join_footer")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var householdSection: some View {
|
||||
Section {
|
||||
HStack {
|
||||
Label(households.household?.name ?? HouseholdRuntime.householdName ?? "", systemImage: "house.fill")
|
||||
Spacer()
|
||||
if sync.isSyncing {
|
||||
ProgressView()
|
||||
} else if let lastSyncedAt = sync.lastSyncedAt {
|
||||
Text(lastSyncedAt.formatted(date: .omitted, time: .shortened))
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
}
|
||||
}
|
||||
if let syncError = sync.lastError {
|
||||
Text(syncError)
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodWarning)
|
||||
}
|
||||
} header: {
|
||||
Text("household_section")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var membersSection: some View {
|
||||
Section {
|
||||
ForEach(households.members) { member in
|
||||
HStack {
|
||||
Label(
|
||||
member.displayName.isEmpty ? String(localized: "household_member_unnamed") : member.displayName,
|
||||
systemImage: member.role == "owner" ? "crown.fill" : "person.fill"
|
||||
)
|
||||
Spacer()
|
||||
if member.id == auth.uid {
|
||||
Text("household_member_you")
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("household_members_section")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var inviteSection: some View {
|
||||
Section {
|
||||
if let code = households.household?.inviteCode {
|
||||
HStack {
|
||||
Text(code)
|
||||
.font(.system(.title3, design: .monospaced))
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.mealMoodTextPrimary)
|
||||
Spacer()
|
||||
ShareLink(item: String(format: String(localized: "household_invite_share"), code)) {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
if let expiry = households.household?.inviteExpiresAt {
|
||||
Text(String(format: String(localized: "household_invite_expires"),
|
||||
expiry.formatted(date: .abbreviated, time: .omitted)))
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
regenerateCode()
|
||||
} label: {
|
||||
Label("household_invite_regenerate", systemImage: "arrow.triangle.2.circlepath")
|
||||
}
|
||||
} header: {
|
||||
Text("household_invite_section")
|
||||
} footer: {
|
||||
Text("household_invite_footer")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
private var leaveSection: some View {
|
||||
Section {
|
||||
Button(role: .destructive) {
|
||||
showLeaveConfirm = true
|
||||
} label: {
|
||||
Label("household_leave", systemImage: "rectangle.portrait.and.arrow.right")
|
||||
}
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
private func create() {
|
||||
run {
|
||||
let name = householdName.trimmingCharacters(in: .whitespaces)
|
||||
_ = try await households.createHousehold(name: name, displayName: auth.displayName ?? "")
|
||||
// Everything already planned on this device becomes the household's
|
||||
// starting point.
|
||||
await sync.seedNewHousehold()
|
||||
}
|
||||
}
|
||||
|
||||
private func join(bringingLocalContent: Bool) {
|
||||
run {
|
||||
_ = try await households.join(code: joinCode, displayName: auth.displayName ?? "")
|
||||
if !bringingLocalContent {
|
||||
sync.replaceLocalContent(context: context)
|
||||
}
|
||||
joinCode = ""
|
||||
}
|
||||
}
|
||||
|
||||
private func leave() {
|
||||
run {
|
||||
try await households.leaveHousehold()
|
||||
}
|
||||
}
|
||||
|
||||
private func regenerateCode() {
|
||||
run {
|
||||
try await households.regenerateInviteCode()
|
||||
}
|
||||
}
|
||||
|
||||
private func run(_ operation: @escaping () async throws -> Void) {
|
||||
isWorking = true
|
||||
errorMessage = nil
|
||||
Task {
|
||||
do {
|
||||
try await operation()
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
CrashlyticsService.record(error, context: "household_action")
|
||||
}
|
||||
isWorking = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,26 +227,6 @@ struct SettingsView: View {
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
.task { await refreshNotificationStatus() }
|
||||
|
||||
// Household section (2.2): shared planning across accounts.
|
||||
Section {
|
||||
NavigationLink(destination: HouseholdView()) {
|
||||
HStack {
|
||||
Text("household_title")
|
||||
Spacer()
|
||||
if let name = HouseholdRuntime.householdName, HouseholdRuntime.householdId != nil {
|
||||
Text(name)
|
||||
.font(.mealMoodCaption)
|
||||
.foregroundColor(.mealMoodTextSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Label("household_section", systemImage: "person.2")
|
||||
} footer: {
|
||||
Text("household_settings_footer")
|
||||
}
|
||||
.listRowBackground(Color.mealMoodSurface)
|
||||
|
||||
// Tags section
|
||||
Section {
|
||||
NavigationLink(destination: TagListView()) {
|
||||
|
||||
Reference in New Issue
Block a user