diff --git a/Kasztelan.xcodeproj/project.pbxproj b/Kasztelan.xcodeproj/project.pbxproj index 7b70fd5..57e5514 100644 --- a/Kasztelan.xcodeproj/project.pbxproj +++ b/Kasztelan.xcodeproj/project.pbxproj @@ -292,7 +292,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 12; + CURRENT_PROJECT_VERSION = 13; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Kasztelan/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.food-and-drink"; @@ -316,7 +316,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 12; + CURRENT_PROJECT_VERSION = 13; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Kasztelan/Info.plist; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.food-and-drink"; diff --git a/Kasztelan/CardView.swift b/Kasztelan/CardView.swift index 6561de8..037c247 100644 --- a/Kasztelan/CardView.swift +++ b/Kasztelan/CardView.swift @@ -89,7 +89,19 @@ struct CardView: View { } private func loyaltyCard(compact: Bool) -> some View { - return VStack(spacing: compact ? 10 : 20) { + GlassDepthDeck { + loyaltyCardFace(compact: compact) + } + .rotation3DEffect( + .degrees(-2.8), + axis: (x: 1, y: 0, z: 0), + anchor: .center, + perspective: 0.62 + ) + } + + private func loyaltyCardFace(compact: Bool) -> some View { + VStack(spacing: compact ? 10 : 20) { Image("Logo") .resizable() .scaledToFit() diff --git a/Kasztelan/MainTabView.swift b/Kasztelan/MainTabView.swift index dfe4268..f5db820 100644 --- a/Kasztelan/MainTabView.swift +++ b/Kasztelan/MainTabView.swift @@ -4,17 +4,25 @@ struct MainTabView: View { @EnvironmentObject private var model: AppModel var body: some View { - TabView(selection: $model.selectedTab) { - NavigationStack { WalletView() } - .tabItem { Label(model.text("Karta", "Card"), systemImage: "creditcard.fill") } - .tag(0) - NavigationStack { MessagesView() } - .tabItem { Label(model.text("Wiadomości", "Messages"), systemImage: "envelope.fill") } - .badge(model.messages.count) - .tag(1) - NavigationStack { ProfileView() } - .tabItem { Label(model.text("Profil", "Profile"), systemImage: "person.crop.circle.fill") } - .tag(2) + ZStack { + AmbientBackground() + + TabView(selection: $model.selectedTab) { + NavigationStack { WalletView() } + .toolbarBackground(.hidden, for: .navigationBar) + .tabItem { Label(model.text("Karta", "Card"), systemImage: "creditcard.fill") } + .tag(0) + NavigationStack { MessagesView() } + .toolbarBackground(.hidden, for: .navigationBar) + .tabItem { Label(model.text("Wiadomości", "Messages"), systemImage: "envelope.fill") } + .badge(model.messages.count) + .tag(1) + NavigationStack { ProfileView() } + .toolbarBackground(.hidden, for: .navigationBar) + .tabItem { Label(model.text("Profil", "Profile"), systemImage: "person.crop.circle.fill") } + .tag(2) + } + .background(.clear) } .tint(KTheme.orange) .toolbarBackground(.ultraThinMaterial, for: .tabBar) diff --git a/Kasztelan/MessagesView.swift b/Kasztelan/MessagesView.swift index 9e4d0af..c22c1c3 100644 --- a/Kasztelan/MessagesView.swift +++ b/Kasztelan/MessagesView.swift @@ -29,6 +29,7 @@ struct MessagesView: View { } .background(AmbientBackground()) .toolbar { AppToolbar() } + .toolbarBackground(.hidden, for: .navigationBar) .refreshable { await model.refresh() } .task { await model.refresh() } } diff --git a/Kasztelan/ProfileView.swift b/Kasztelan/ProfileView.swift index 1371ee7..2bc2b50 100644 --- a/Kasztelan/ProfileView.swift +++ b/Kasztelan/ProfileView.swift @@ -53,6 +53,7 @@ struct ProfileView: View { } .background(AmbientBackground()) .toolbar { AppToolbar() } + .toolbarBackground(.hidden, for: .navigationBar) .fullScreenCover(isPresented: $editingProfile) { EditProfileView() } .sheet(isPresented: $changingPassword) { ChangePasswordView() } .sheet(isPresented: $deletingAccount) { diff --git a/Kasztelan/Theme.swift b/Kasztelan/Theme.swift index 9108dee..463510d 100644 --- a/Kasztelan/Theme.swift +++ b/Kasztelan/Theme.swift @@ -59,6 +59,46 @@ struct AmbientBackground: View { } } +struct GlassDepthDeck: View { + private let cornerRadius: CGFloat + private let content: Content + + init(cornerRadius: CGFloat = 26, @ViewBuilder content: () -> Content) { + self.cornerRadius = cornerRadius + self.content = content() + } + + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .fill(.ultraThinMaterial) + .overlay { + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .stroke(KTheme.purple.opacity(0.34), lineWidth: 1) + } + .scaleEffect(x: 0.88, y: 0.95) + .offset(y: 24) + .blur(radius: 1.2) + .opacity(0.62) + + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .fill(.thinMaterial) + .overlay { + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .stroke(KTheme.pink.opacity(0.32), lineWidth: 1) + } + .scaleEffect(x: 0.95, y: 0.98) + .offset(y: 13) + .opacity(0.80) + + content + .shadow(color: .black.opacity(0.48), radius: 34, y: 22) + .shadow(color: KTheme.pink.opacity(0.26), radius: 22, y: 8) + } + .padding(.bottom, 24) + } +} + struct PanelModifier: ViewModifier { func body(content: Content) -> some View { content diff --git a/Kasztelan/WalletView.swift b/Kasztelan/WalletView.swift index a6279bb..5029932 100644 --- a/Kasztelan/WalletView.swift +++ b/Kasztelan/WalletView.swift @@ -15,8 +15,9 @@ struct WalletView: View { var body: some View { walletCarousel - .background(KTheme.background) + .background(.clear) .toolbar { walletToolbar } + .toolbarBackground(.hidden, for: .navigationBar) .onAppear { selectedPage = 0 } .task(id: model.user?.id) { await store.sync(using: model) } .fullScreenCover(isPresented: $scannerPresented) { scannerView } @@ -166,7 +167,11 @@ struct WalletView: View { } .padding(.horizontal, 12) .padding(.vertical, 8) - .background(.black.opacity(0.35), in: Capsule()) + .background(.ultraThinMaterial, in: Capsule()) + .overlay { + Capsule() + .stroke(.white.opacity(0.18), lineWidth: 1) + } .padding(.bottom, 2) .allowsHitTesting(false) } @@ -285,42 +290,44 @@ private struct DiscountCardView: View { .accessibilityLabel(model.text("Usuń kartę", "Delete card")) } - VStack(spacing: 18) { - StoredBarcodeView(value: card.payload, symbology: card.symbology) - .frame(maxWidth: .infinity) - .frame(height: card.isTwoDimensional ? 220 : 132) - .padding(14) - .background(.white) - .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) - Text(card.payload) - .font(.system(.headline, design: .monospaced).weight(.bold)) - .multilineTextAlignment(.center) - .textSelection(.enabled) - .lineLimit(3) - .minimumScaleFactor(0.65) - } - .frame(maxWidth: .infinity) - .padding(22) - .background( - ZStack { - LinearGradient( - colors: [KTheme.purple.opacity(0.92), KTheme.pink.opacity(0.88), KTheme.orange.opacity(0.86)], - startPoint: .topLeading, - endPoint: .bottomTrailing - ) - Circle() - .fill(.white.opacity(0.15)) - .frame(width: 170, height: 170) - .blur(radius: 8) - .offset(x: 145, y: -100) + GlassDepthDeck { + VStack(spacing: 18) { + StoredBarcodeView(value: card.payload, symbology: card.symbology) + .frame(maxWidth: .infinity) + .frame(height: card.isTwoDimensional ? 220 : 132) + .padding(14) + .background(.white) + .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous)) + Text(card.payload) + .font(.system(.headline, design: .monospaced).weight(.bold)) + .multilineTextAlignment(.center) + .textSelection(.enabled) + .lineLimit(3) + .minimumScaleFactor(0.65) + } + .frame(maxWidth: .infinity) + .padding(22) + .background( + ZStack { + LinearGradient( + colors: [KTheme.purple.opacity(0.92), KTheme.pink.opacity(0.88), KTheme.orange.opacity(0.86)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + Circle() + .fill(.white.opacity(0.15)) + .frame(width: 170, height: 170) + .blur(radius: 8) + .offset(x: 145, y: -100) + } + ) + .clipShape(RoundedRectangle(cornerRadius: 26, style: .continuous)) + .overlay { + RoundedRectangle(cornerRadius: 26, style: .continuous) + .stroke(.white.opacity(0.28), lineWidth: 1) } - ) - .clipShape(RoundedRectangle(cornerRadius: 26, style: .continuous)) - .overlay { - RoundedRectangle(cornerRadius: 26, style: .continuous) - .stroke(.white.opacity(0.28), lineWidth: 1) } - .shadow(color: KTheme.pink.opacity(0.24), radius: 28, y: 16) + .rotation3DEffect(.degrees(-2.8), axis: (x: 1, y: 0, z: 0), perspective: 0.62) Text(model.text("Dostępna offline • zaszyfrowana synchronizacja", "Available offline • encrypted sync")) .font(.footnote)