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: 12 additions & 4 deletions berkeley-mobile.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
01CDBBF125CA6F58006B93BD /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CDBBF025CA6F58006B93BD /* RequestError.swift */; };
01CDFF6A257C614900D9FBD6 /* Colors+Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01CDFF69257C614900D9FBD6 /* Colors+Resource.swift */; };
01D11B8E2504453B00BDF660 /* ScrollingStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D11B8D2504453B00BDF660 /* ScrollingStackView.swift */; };
01D11B902504560700BDF660 /* GymDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D11B8F2504560700BDF660 /* GymDetailViewController.swift */; };
01D269932544D86C000377B4 /* Apercu Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 01D2698A2544D86B000377B4 /* Apercu Light.otf */; };
01D269942544D86C000377B4 /* Apercu Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 01D2698B2544D86B000377B4 /* Apercu Italic.otf */; };
01D269952544D86C000377B4 /* Apercu Light Italic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 01D2698C2544D86B000377B4 /* Apercu Light Italic.otf */; };
Expand Down Expand Up @@ -79,6 +78,9 @@
13EA64CA2399CE5B00FD8E13 /* SearchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA64C92399CE5B00FD8E13 /* SearchItem.swift */; };
13EA64CD2399CEDA00FD8E13 /* Gym.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA64CC2399CEDA00FD8E13 /* Gym.swift */; };
13EA64D02399D50C00FD8E13 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA64CF2399D50C00FD8E13 /* DataManager.swift */; };
1D2013F22DC4684E00E636DF /* DescriptionCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2013F12DC4684E00E636DF /* DescriptionCard.swift */; };
1D2013F42DC4685E00E636DF /* CategoryOverviewCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2013F32DC4685E00E636DF /* CategoryOverviewCard.swift */; };
1D7DD6E02DC2EB5B00A6BBA7 /* GymDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7DD6DF2DC2EB5B00A6BBA7 /* GymDetailView.swift */; };
1DB006AD2D71C8D6001CC870 /* ResourcesSectionDropdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB006AC2D71C8C0001CC870 /* ResourcesSectionDropdown.swift */; };
1DB88F6D2D94DF78007713F7 /* OpenTimesCardSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB88F6C2D94DF78007713F7 /* OpenTimesCardSwiftUIView.swift */; };
29061D41241C450E002BC9D9 /* HasLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29061D40241C450E002BC9D9 /* HasLocation.swift */; };
Expand Down Expand Up @@ -217,7 +219,6 @@
01CDBBF025CA6F58006B93BD /* RequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = "<group>"; };
01CDFF69257C614900D9FBD6 /* Colors+Resource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Colors+Resource.swift"; sourceTree = "<group>"; };
01D11B8D2504453B00BDF660 /* ScrollingStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingStackView.swift; sourceTree = "<group>"; };
01D11B8F2504560700BDF660 /* GymDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GymDetailViewController.swift; sourceTree = "<group>"; };
01D2698A2544D86B000377B4 /* Apercu Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Apercu Light.otf"; sourceTree = "<group>"; };
01D2698B2544D86B000377B4 /* Apercu Italic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Apercu Italic.otf"; sourceTree = "<group>"; };
01D2698C2544D86B000377B4 /* Apercu Light Italic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Apercu Light Italic.otf"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -271,6 +272,9 @@
13EA64C92399CE5B00FD8E13 /* SearchItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchItem.swift; sourceTree = "<group>"; };
13EA64CC2399CEDA00FD8E13 /* Gym.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gym.swift; sourceTree = "<group>"; };
13EA64CF2399D50C00FD8E13 /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = "<group>"; };
1D2013F12DC4684E00E636DF /* DescriptionCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionCard.swift; sourceTree = "<group>"; };
1D2013F32DC4685E00E636DF /* CategoryOverviewCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryOverviewCard.swift; sourceTree = "<group>"; };
1D7DD6DF2DC2EB5B00A6BBA7 /* GymDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GymDetailView.swift; sourceTree = "<group>"; };
1DB006AC2D71C8C0001CC870 /* ResourcesSectionDropdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcesSectionDropdown.swift; sourceTree = "<group>"; };
1DB88F6C2D94DF78007713F7 /* OpenTimesCardSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenTimesCardSwiftUIView.swift; sourceTree = "<group>"; };
29061D40241C450E002BC9D9 /* HasLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HasLocation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -521,12 +525,12 @@
children = (
13EA64C62399CDF900FD8E13 /* GymDataSource */,
1336A31D241C400F00949F32 /* GymClassDataSource */,
01D11B8F2504560700BDF660 /* GymDetailViewController.swift */,
13E25DFC238C949E00B670B5 /* FitnessViewController.swift */,
135D7F78243AA6B1003F8BD1 /* Fitness+Controllers */,
E83B6DA42D7A85D500AA9422 /* GymOccupancyScrapper.swift */,
E83B6DA62D7A85F200AA9422 /* GymOccupancyView.swift */,
E83B6DA82D7A860E00AA9422 /* GymOccupancyViewModel.swift */,
1D7DD6DF2DC2EB5B00A6BBA7 /* GymDetailView.swift */,
);
path = Fitness;
sourceTree = "<group>";
Expand Down Expand Up @@ -555,6 +559,8 @@
1396013123865E2E005E4788 /* CardView.swift */,
298EE26F25BB82A2002BAF0F /* CardTableViewCell.swift */,
1396013223865E2E005E4788 /* TagView.swift */,
1D2013F32DC4685E00E636DF /* CategoryOverviewCard.swift */,
1D2013F12DC4684E00E636DF /* DescriptionCard.swift */,
018B982525327358004C3B26 /* ActionButton.swift */,
01B250272516AD4F00CBA459 /* IconPairView.swift */,
554CB99F23F3A83F00BB1715 /* EventTableViewCell.swift */,
Expand Down Expand Up @@ -1176,9 +1182,11 @@
13135BB3241244370056B169 /* FilterViewCell.swift in Sources */,
016A56D42519E96800531A12 /* CampusCalendarViewController.swift in Sources */,
298EE27025BB82A2002BAF0F /* CardTableViewCell.swift in Sources */,
1D2013F22DC4684E00E636DF /* DescriptionCard.swift in Sources */,
13580AF62437D7E700D309AA /* LibraryDetailViewController.swift in Sources */,
135D7F77243A9BD1003F8BD1 /* Colors+GymClass.swift in Sources */,
55DCF7A123724835001B01B8 /* MaterialButton.swift in Sources */,
1D2013F42DC4685E00E636DF /* CategoryOverviewCard.swift in Sources */,
297678892426D2C500FDD1EB /* SearchDrawerViewController.swift in Sources */,
29CB280A2404DD71009A2CFB /* FilterTableViewCell.swift in Sources */,
55DCF79623723CF2001B01B8 /* UIView+Extensions.swift in Sources */,
Expand All @@ -1187,6 +1195,7 @@
01D2699D2544E005000377B4 /* AcademicCalendarViewController.swift in Sources */,
1336A31C241C400800949F32 /* GymClass.swift in Sources */,
E8B5975F2CA62D6F006DFBD5 /* SegmentedControlView.swift in Sources */,
1D7DD6E02DC2EB5B00A6BBA7 /* GymDetailView.swift in Sources */,
13EA64C82399CE0800FD8E13 /* GymDataSource.swift in Sources */,
2E1C227D2D835A9D0021803C /* SearchBarView.swift in Sources */,
298EE26A25BB6C33002BAF0F /* Colors+StudyPact.swift in Sources */,
Expand Down Expand Up @@ -1235,7 +1244,6 @@
017C0B26251018BA00BFA80A /* Colors+MapMarker.swift in Sources */,
E83B6DA92D7A860E00AA9422 /* GymOccupancyViewModel.swift in Sources */,
29345E2724A7E76300859A88 /* OverviewCardView.swift in Sources */,
01D11B902504560700BDF660 /* GymDetailViewController.swift in Sources */,
55AF442D2453ACE600F13232 /* DiningLocation.swift in Sources */,
1336A329241DA56100949F32 /* DiningHallDataSource.swift in Sources */,
136DC97B2398B4D1009B1810 /* UIViewController+Extensions.swift in Sources */,
Expand Down
131 changes: 131 additions & 0 deletions berkeley-mobile/Common/CategoryOverviewCard.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// CategoryOverviewCard.swift
// berkeley-mobile
//
// Created by Yihang Chen on 5/1/25.
// Copyright © 2025 ASUC OCTO. All rights reserved.
//

import SwiftUI

struct BMContactInfoRow: View {
let iconName: String
let text: String
var lineLimit: Int? = 1

var body: some View {
HStack(alignment: .top, spacing: 10) {
Image(systemName: iconName)
.frame(width: 18, height: 18)

Text(text)
.font(Font(BMFont.light(12)))
.lineLimit(lineLimit)
.fixedSize(horizontal: false, vertical: true)
}
}
}

struct BMCategoryOverviewCard: View {
let category: SearchItem & HasLocation & HasImage

var body: some View {
HStack(alignment: .top, spacing: 16) {
VStack(alignment: .leading) {
titleView

Spacer(minLength: 20)

contactInfoView
}

Spacer()

imageView
}
.padding(12)
.background(Color(BMColor.cardBackground))
.cornerRadius(12)
.shadow(color: Color(uiColor: .label).opacity(0.15), radius: 5, x: 0, y: 0)
.padding(.vertical, 8)
.padding(.horizontal, 4)
}

private var titleView: some View {
Text(category.name)
.font(Font(BMFont.bold(23)))
.foregroundColor(Color(BMColor.blackText))
.lineLimit(3)
.padding(.top, 8)
}

private var contactInfoView: some View {
VStack(alignment: .leading, spacing: 12) {
if let address = category.address, !address.isEmpty {
BMContactInfoRow(
iconName: "location.fill",
text: address,
lineLimit: nil
)
}

if let hasPhone = category as? HasPhoneNumber,
let phoneNumber = hasPhone.phoneNumber,
!phoneNumber.isEmpty {
BMContactInfoRow(
iconName: "phone.fill",
text: phoneNumber
)
}

if let distance = category.distanceToUser {
BMContactInfoRow(
iconName: "figure.walk",
text: String(format: "%.1f miles", distance)
)
}
}
.foregroundColor(Color(BMColor.blackText))
}

private var imageView: some View {
Group {
if let imageURL = category.imageURL {
AsyncImage(url: imageURL) { phase in
switch phase {
case .empty:
defaultImage
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 200)
.clipShape(RoundedRectangle(cornerRadius: 8))
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(uiColor: .systemGray4), lineWidth: 0.5)
)
case .failure:
defaultImage
@unknown default:
EmptyView()
}
}
} else {
defaultImage
}
}
}

private var defaultImage: some View {
Image(uiImage: Constants.doeGladeImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 200)
.clipShape(RoundedRectangle(cornerRadius: 8))
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(uiColor: .systemGray4), lineWidth: 0.5)
)
}
}
29 changes: 29 additions & 0 deletions berkeley-mobile/Common/DescriptionCard.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// DescriptionCard.swift
// berkeley-mobile
//
// Created by Yihang Chen on 5/1/25.
// Copyright © 2025 ASUC OCTO. All rights reserved.
//

import SwiftUI

struct BMDescriptionCard: View {
let description: String

var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text("Description")
.font(Font(BMFont.bold(16)))
.foregroundColor(Color(BMColor.blackText))

Text(description)
.font(Font(BMFont.light(12)))
.foregroundColor(Color(BMColor.blackText))
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(BMColor.cardBackground))
.cornerRadius(12)
}
}
15 changes: 13 additions & 2 deletions berkeley-mobile/Fitness/GymDataSource/Gym.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,20 @@ class Gym: SearchItem, HasLocation, CanFavorite, HasPhoneNumber, HasImage, HasOp
self.phoneNumber = phoneNumber
self.weeklyHours = weeklyHours
self.name = name.trimmingCharacters(in: .whitespacesAndNewlines)
self.imageURL = URL(string: imageLink ?? "")

if let imgLink = imageLink, !imgLink.isEmpty, let url = URL(string: imgLink) {
self.imageURL = url
} else {
self.imageURL = nil
}

self.icon = UIImage(named: "Walk")?.colored(BMColor.blackText)
self.website = URL(string: link ?? "")

if let webLink = link, !webLink.isEmpty, let url = URL(string: webLink) {
self.website = url
} else {
self.website = nil
}
}

}
Expand Down
Loading