Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Resell.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
Expand Down Expand Up @@ -1177,7 +1177,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
Expand All @@ -1195,7 +1195,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Resell/Resell.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_ASSET_PATHS = "\"Resell/Preview Content\"";
DEVELOPMENT_TEAM = ZGMCXU7X3U;
ENABLE_PREVIEWS = YES;
Expand All @@ -1208,7 +1208,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1230,7 +1230,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Resell/Resell.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_ASSET_PATHS = "\"Resell/Preview Content\"";
DEVELOPMENT_TEAM = ZGMCXU7X3U;
ENABLE_PREVIEWS = YES;
Expand All @@ -1243,7 +1243,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -1265,7 +1265,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = ZGMCXU7X3U;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.cornellappdev.ResellTests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -1284,7 +1284,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = ZGMCXU7X3U;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.cornellappdev.ResellTests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
89 changes: 68 additions & 21 deletions Resell/ViewModels/MainViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,74 @@ class MainViewModel: ObservableObject {
@Published var userDidLogin: Bool = false
@Published var selection = 0

/// When true, the tab bar minimize behavior is .never, which forces the bar to
/// expand. Kept false ("armed", .onScrollDown) at rest so any downward scroll
/// minimizes the bar natively at any speed; crossing back above the top
/// breakpoint fires a short .never pulse to force expansion, then re-arms.
@Published var expandsTabBar: Bool = false

/// Mirrors the tab bar's actual expanded/minimized presentation so floating
/// controls (the add button) stay exactly in sync with it.
@Published var isTabBarMinimized = false

/// Scroll offset below which the bar expands / above which it minimizes.
static let tabBarExpandBreakpoint: CGFloat = 16
static let tabBarMinimizeBreakpoint: CGFloat = 24

private var scrollIsAtTop = true
private var rearmTask: Task<Void, Never>?

/// Forces the tab bar to expand, then re-arms minimize-on-scroll shortly after.
func pulseExpandTabBar() {
rearmTask?.cancel()
if isTabBarMinimized {
isTabBarMinimized = false
}
if !expandsTabBar {
expandsTabBar = true
}
rearmTask = Task { [weak self] in
try? await Task.sleep(for: .seconds(0.45))
guard !Task.isCancelled else { return }
self?.expandsTabBar = false
}
}

/// Called when the user taps the minimized tab bar pill — the system expands
/// the bar itself; this keeps our mirrored state (and the add button) in sync.
func handleManualTabBarExpansion() {
if isTabBarMinimized {
isTabBarMinimized = false
}
}

/// Drives the tab bar from a tab root's scroll offset: expand when the scroll
/// crosses above the top breakpoint; past the breakpoint, minimize only on
/// downward movement (mirroring .onScrollDown, so a manually expanded bar
/// stays expanded until the user actually scrolls down again).
func updateTabBarForScroll(offset: CGFloat, previousOffset: CGFloat) {
if offset < Self.tabBarExpandBreakpoint {
if isTabBarMinimized {
isTabBarMinimized = false
}
if !scrollIsAtTop {
scrollIsAtTop = true
pulseExpandTabBar()
}
} else if offset > Self.tabBarMinimizeBreakpoint {
if scrollIsAtTop {
scrollIsAtTop = false
}
rearmTask?.cancel()
if expandsTabBar {
expandsTabBar = false
}
if offset > previousOffset + 2, !isTabBarMinimized {
isTabBarMinimized = true
}
}
}

@Published var hidesSignInButton = true

// MARK: - Persistent Storage
Expand Down Expand Up @@ -85,27 +153,6 @@ class MainViewModel: ObservableObject {
return history
}

func setupNavBar() {
let backButtonImage = UIImage(named: "chevron.left")?
.resized(to: CGSize(width: 38, height: 24))
.withRenderingMode(.alwaysOriginal)
.withTintColor(.black)

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .white
appearance.titleTextAttributes = [.foregroundColor: UIColor.black]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.black]

appearance.backButtonAppearance.normal.titlePositionAdjustment = UIOffset(horizontal: -100, vertical: 0)
appearance.backButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear]
appearance.setBackIndicatorImage(backButtonImage, transitionMaskImage: backButtonImage)

UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().compactAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
}

@objc func logout() {
// Clear any cached data
clearUserData()
Expand Down
20 changes: 6 additions & 14 deletions Resell/Views/Chats/ChatsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@ struct ChatsView: View {

@EnvironmentObject var router: Router
@EnvironmentObject var viewModel: ChatsViewModel
@EnvironmentObject var mainViewModel: MainViewModel
@EnvironmentObject private var mainViewModel: MainViewModel

// MARK: - UI

var body: some View {
VStack(alignment: .leading) {
headerView

filtersView

chatsView

Spacer()
}
.background(Constants.Colors.white)
.onScrollGeometryChange(for: CGFloat.self) { geometry in
geometry.contentOffset.y
} action: { oldValue, newValue in
mainViewModel.updateTabBarForScroll(offset: newValue, previousOffset: oldValue)
}
.emptyState(isEmpty: viewModel.checkEmptyState(), title: viewModel.emptyStateTitle(), text: viewModel.emptyStateMessage())
.refreshable {
viewModel.refreshChats()
Expand All @@ -39,17 +42,6 @@ struct ChatsView: View {
.loadingView(isLoading: viewModel.isLoading)
}

private var headerView: some View {
HStack {
Text("Messages")
.font(Constants.Fonts.h1)
.foregroundStyle(Constants.Colors.black)

Spacer()
}
.padding(.horizontal, 25)
}

private var filtersView: some View {
HStack {
ForEach(Constants.chats, id: \.id) { filter in
Expand Down
Loading