Skip to content

Commit

Permalink
Noah/favorite foods (#311)
Browse files Browse the repository at this point in the history
* Add favorites to filters

* Separated food items in menu tableview cells and added favorites indicator.

* Updating API to include favorite food items

* Added favorites to menus

* Integrated TableViewHeaders and Favorite Foods in User Defaults

* Implements favorites in the account view (when logged in)

* Added favorites to Extended menus and analytics. Refactored according to feedback from my PR Draft. Added static methods to DefaultsKeys and static constants to uiimage. Made minor UI tweaks to match figma.
  • Loading branch information
Npikielny authored May 4, 2021
1 parent d80f02d commit a48cfbc
Show file tree
Hide file tree
Showing 34 changed files with 484 additions and 98 deletions.
30 changes: 26 additions & 4 deletions Eatery Watch App Extension/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public final class CampusEateriesQuery: GraphQLQuery {
__typename
item
healthy
favorite
}
}
}
Expand All @@ -77,6 +78,7 @@ public final class CampusEateriesQuery: GraphQLQuery {
__typename
item
healthy
favorite
price
choices {
__typename
Expand Down Expand Up @@ -758,6 +760,7 @@ public final class CampusEateriesQuery: GraphQLQuery {
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("item", type: .nonNull(.scalar(String.self))),
GraphQLField("healthy", type: .nonNull(.scalar(Bool.self))),
GraphQLField("favorite", type: .nonNull(.scalar(Bool.self))),
]
}

Expand All @@ -767,8 +770,8 @@ public final class CampusEateriesQuery: GraphQLQuery {
self.resultMap = unsafeResultMap
}

public init(item: String, healthy: Bool) {
self.init(unsafeResultMap: ["__typename": "FoodItemType", "item": item, "healthy": healthy])
public init(item: String, healthy: Bool, favorite: Bool) {
self.init(unsafeResultMap: ["__typename": "FoodItemType", "item": item, "healthy": healthy, "favorite": favorite])
}

public var __typename: String {
Expand Down Expand Up @@ -797,6 +800,15 @@ public final class CampusEateriesQuery: GraphQLQuery {
resultMap.updateValue(newValue, forKey: "healthy")
}
}

public var favorite: Bool {
get {
return resultMap["favorite"]! as! Bool
}
set {
resultMap.updateValue(newValue, forKey: "favorite")
}
}
}
}
}
Expand Down Expand Up @@ -896,6 +908,7 @@ public final class CampusEateriesQuery: GraphQLQuery {
GraphQLField("__typename", type: .nonNull(.scalar(String.self))),
GraphQLField("item", type: .nonNull(.scalar(String.self))),
GraphQLField("healthy", type: .nonNull(.scalar(Bool.self))),
GraphQLField("favorite", type: .nonNull(.scalar(Bool.self))),
GraphQLField("price", type: .nonNull(.scalar(String.self))),
GraphQLField("choices", type: .list(.object(Choice.selections))),
]
Expand All @@ -907,8 +920,8 @@ public final class CampusEateriesQuery: GraphQLQuery {
self.resultMap = unsafeResultMap
}

public init(item: String, healthy: Bool, price: String, choices: [Choice?]? = nil) {
self.init(unsafeResultMap: ["__typename": "DescriptiveFoodItemType", "item": item, "healthy": healthy, "price": price, "choices": choices.flatMap { (value: [Choice?]) -> [ResultMap?] in value.map { (value: Choice?) -> ResultMap? in value.flatMap { (value: Choice) -> ResultMap in value.resultMap } } }])
public init(item: String, healthy: Bool, favorite: Bool, price: String, choices: [Choice?]? = nil) {
self.init(unsafeResultMap: ["__typename": "DescriptiveFoodItemType", "item": item, "healthy": healthy, "favorite": favorite, "price": price, "choices": choices.flatMap { (value: [Choice?]) -> [ResultMap?] in value.map { (value: Choice?) -> ResultMap? in value.flatMap { (value: Choice) -> ResultMap in value.resultMap } } }])
}

public var __typename: String {
Expand Down Expand Up @@ -938,6 +951,15 @@ public final class CampusEateriesQuery: GraphQLQuery {
}
}

public var favorite: Bool {
get {
return resultMap["favorite"]! as! Bool
}
set {
resultMap.updateValue(newValue, forKey: "favorite")
}
}

public var price: String {
get {
return resultMap["price"]! as! String
Expand Down
2 changes: 2 additions & 0 deletions Eatery Watch App Extension/Queries/campusEateries.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ query CampusEateries {
items {
item
healthy
favorite
}
}
}
Expand All @@ -55,6 +56,7 @@ query CampusEateries {
items {
item
healthy
favorite
price
choices {
options
Expand Down
8 changes: 8 additions & 0 deletions Eatery.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

/* Begin PBXBuildFile section */
1E1A152810ED5A664C671483 /* Pods_Eatery_Watch_App_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7E776653956F9723D00D92DD /* Pods_Eatery_Watch_App_Extension.framework */; };
30DA20D82625282000928E17 /* FavoriteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DA20D72625282000928E17 /* FavoriteTableViewCell.swift */; };
30FCEDBF262E0DC000EDCBC5 /* MenuCollectionViewHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FCEDBE262E0DC000EDCBC5 /* MenuCollectionViewHeaderView.swift */; };
5D05FF3623C2C14400EF8CC9 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D05FF3423C2C14400EF8CC9 /* Interface.storyboard */; };
5D05FF3823C2C14600EF8CC9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D05FF3723C2C14600EF8CC9 /* Assets.xcassets */; };
5D05FF3F23C2C14600EF8CC9 /* Eatery Watch App Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5D05FF3E23C2C14600EF8CC9 /* Eatery Watch App Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -232,6 +234,8 @@
0D19AE5E4967A26F6D705EA4 /* Pods-Eatery Watch App.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Eatery Watch App.testflight.xcconfig"; path = "Pods/Target Support Files/Pods-Eatery Watch App/Pods-Eatery Watch App.testflight.xcconfig"; sourceTree = "<group>"; };
0E7A3B80D08EC745DCB310AD /* Pods-Eatery.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Eatery.release.xcconfig"; path = "Pods/Target Support Files/Pods-Eatery/Pods-Eatery.release.xcconfig"; sourceTree = "<group>"; };
19538C3B4917B0F73E5B6D62 /* Pods_Eatery.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Eatery.framework; sourceTree = BUILT_PRODUCTS_DIR; };
30DA20D72625282000928E17 /* FavoriteTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteTableViewCell.swift; sourceTree = "<group>"; };
30FCEDBE262E0DC000EDCBC5 /* MenuCollectionViewHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCollectionViewHeaderView.swift; sourceTree = "<group>"; };
3DE936D6D5412F4A2DC0E11B /* Pods-Eatery.testflight.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Eatery.testflight.xcconfig"; path = "Pods/Target Support Files/Pods-Eatery/Pods-Eatery.testflight.xcconfig"; sourceTree = "<group>"; };
42B1F4EE1BEAD5EE004C382A /* Eatery.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Eatery.entitlements; sourceTree = "<group>"; };
47535FDDB39E9BD71EDAF886 /* Pods-Eatery Watch App Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Eatery Watch App Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-Eatery Watch App Extension/Pods-Eatery Watch App Extension.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -556,6 +560,7 @@
isa = PBXGroup;
children = (
5D44519B231D45AE0035164F /* BRBBalanceTableViewCell.swift */,
30DA20D72625282000928E17 /* FavoriteTableViewCell.swift */,
5D44519D231D45C30035164F /* BRBHistoryTableViewCell.swift */,
5D44519F231D51FE0035164F /* BRBLoginErrorView.swift */,
5DEF1AA1222B2FCE00855902 /* DescriptionTableViewCell.swift */,
Expand Down Expand Up @@ -763,6 +768,7 @@
5DE1D42E225EC710008054F4 /* PillView.swift */,
6502AF861DCFB9ED00390EAA /* FilterBar.swift */,
5D218C2C21FBE39700327516 /* EateriesCollectionViewHeaderView.swift */,
30FCEDBE262E0DC000EDCBC5 /* MenuCollectionViewHeaderView.swift */,
5D218C2921FBE39700327516 /* EateryCollectionViewCell.swift */,
5D0D8E0E223B286D001ADB85 /* EateriesFailedToLoadView.swift */,
5D5B8CC02450F1B2004E05EF /* SearchResultsTableViewCell.swift */,
Expand Down Expand Up @@ -1479,6 +1485,7 @@
5D44519C231D45AE0035164F /* BRBBalanceTableViewCell.swift in Sources */,
5D218C4821FC136E00327516 /* CLLocation+Distance.swift in Sources */,
5DEF1AA4222B328800855902 /* SwitchTableViewCell.swift in Sources */,
30DA20D82625282000928E17 /* FavoriteTableViewCell.swift in Sources */,
5D2790362336BC1400AA513B /* UINavigationBarAppearance+Eatery.swift in Sources */,
D930FBD8218BC00200B0B1CE /* BRBAccount.swift in Sources */,
5D4451A0231D51FE0035164F /* BRBLoginErrorView.swift in Sources */,
Expand Down Expand Up @@ -1542,6 +1549,7 @@
C0497B131B64AA1800AC1C6C /* UIColor+ColorScheme.swift in Sources */,
5D218C2F21FBE39700327516 /* MealStationTableViewCell.swift in Sources */,
5D218C3121FBE39700327516 /* MealItemTableViewCell.swift in Sources */,
30FCEDBF262E0DC000EDCBC5 /* MenuCollectionViewHeaderView.swift in Sources */,
5DE1D42F225EC710008054F4 /* PillView.swift in Sources */,
9538E75E255CC758000D2F34 /* ScrollableTextTabBarControl.swift in Sources */,
95D485A4259D0B2A00B312D3 /* TabbedPageViewController.swift in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions Eatery/Analytics/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ struct NearestFilterPressPayload: Payload {
let eventName = "nearest_filter_press"
}

/// Log whenever the Favorite Items filter is pressed
struct FavoriteItemsPressPayload: Payload {
let eventName = "favorite_items_filter_press"
}

/// Log whenever the North filter is pressed
struct NorthFilterPressPayload: Payload {
let eventName = "north_filter_press"
Expand Down
42 changes: 36 additions & 6 deletions Eatery/Controllers/BRB/BRBAccountViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2019 CUAppDev. All rights reserved.
//

import SwiftyUserDefaults
import UIKit

protocol BRBAccountViewControllerDelegate: AnyObject {
Expand All @@ -16,6 +17,7 @@ class BRBAccountViewController: UIViewController {

private enum CellIdentifiers {
static let balance = "balance"
static let favorites = "favorite"
static let history = "history"
}

Expand All @@ -24,6 +26,8 @@ class BRBAccountViewController: UIViewController {

private var tableView: UITableView!

private var favoriteItems = Defaults[\.favoriteFoods]

init(account: BRBAccount) {
self.account = account

Expand All @@ -39,8 +43,8 @@ class BRBAccountViewController: UIViewController {

tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(BRBBalanceTableViewCell.self, forCellReuseIdentifier: CellIdentifiers.balance)
tableView.register(FavoriteTableViewCell.self, forCellReuseIdentifier: CellIdentifiers.favorites)
tableView.register(BRBHistoryTableViewCell.self, forCellReuseIdentifier: CellIdentifiers.history)
tableView.allowsSelection = false
tableView.separatorColor = .wash
tableView.dataSource = self
tableView.delegate = self
Expand All @@ -65,12 +69,14 @@ class BRBAccountViewController: UIViewController {
extension BRBAccountViewController: UITableViewDataSource {

func numberOfSections(in tableView: UITableView) -> Int {
2
favoriteItems.count > 0 ? 3 : 2
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 3
} else if section == 1 && favoriteItems.count > 0 {
return favoriteItems.count
}

return account.history.count
Expand All @@ -80,14 +86,19 @@ extension BRBAccountViewController: UITableViewDataSource {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.balance)
as! BRBBalanceTableViewCell

switch indexPath.row {
case 0: cell.configure(title: "BRBs", subtitle: "$\(account.brbs)")
case 1: cell.configure(title: "Laundry", subtitle: "$\(account.laundry)")
case 2: cell.configure(title: "City Bucks", subtitle: "$\(account.cityBucks)")
default: break
}

return cell
} else if indexPath.section == 1 && favoriteItems.count > 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.favorites)
as! FavoriteTableViewCell
let name = favoriteItems[indexPath.item]
cell.configure(name: name, restaurants: nil, favorited: DefaultsKeys.isFavoriteFood(name))
return cell
}

Expand All @@ -101,22 +112,41 @@ extension BRBAccountViewController: UITableViewDataSource {
)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? FavoriteTableViewCell else {
return
}
cell.favorited.toggle()
DefaultsKeys.toggleFavoriteFood(favoriteItems[indexPath.item])
}

}

extension BRBAccountViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 1 {
if section == 0 {
return nil
}
if section == 1 && favoriteItems.count > 0 {
let header = EateriesCollectionViewHeaderView()
header.titleLabel.text = "History"
header.titleLabel.text = "Favorites"
return header
}
let header = EateriesCollectionViewHeaderView()
header.titleLabel.text = "History"
return header

return nil
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
section == 0 ? 30 : UITableViewAutomaticDimension
}
}

extension BRBAccountViewController: Reloadable {
func reload() {
favoriteItems = Defaults[\.favoriteFoods]
tableView.reloadData()
}
}
6 changes: 6 additions & 0 deletions Eatery/Controllers/BRB/BRBViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,9 @@ extension BRBViewController: AboutTableViewControllerDelegate {
}

}

extension BRBViewController: Reloadable {
func reload() {
accountViewController?.reload()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ class CampusEateriesViewController: EateriesViewController {

private let locationManager: CLLocationManager = CLLocationManager()

public var presentingMenu: CampusMenuViewController?

override func viewDidLoad() {
super.viewDidLoad()

delegate = self

availableFilters = [
.nearest,
.favorites,
.north,
.west,
.central,
Expand Down Expand Up @@ -123,12 +126,14 @@ class CampusEateriesViewController: EateriesViewController {
} else {
payload = CampusCafeCellPressPayload(cafeName: eatery.displayName)
}
presentingMenu = menuViewController
AppDevAnalytics.shared.logFirebase(payload)
}

override func filterBar(_ filterBar: FilterBar, filterWasSelected filter: Filter) {
switch filter {
case .nearest: AppDevAnalytics.shared.logFirebase(NearestFilterPressPayload())
case .favorites: AppDevAnalytics.shared.logFirebase(FavoriteItemsPressPayload())
case .north: AppDevAnalytics.shared.logFirebase(NorthFilterPressPayload())
case .west: AppDevAnalytics.shared.logFirebase(WestFilterPressPayload())
case .central: AppDevAnalytics.shared.logFirebase(CentralFilterPressPayload())
Expand Down Expand Up @@ -251,11 +256,16 @@ extension CampusEateriesViewController: EateriesViewControllerDelegate {
}

filteredEateries = filteredEateries.filter {
if filters.contains(.swipes) { return $0.paymentMethods.contains(.swipes) }
if filters.contains(.brb) { return $0.paymentMethods.contains(.brb) }
if $0.paymentMethods.contains(.brb) && filters.contains(.brb) { return true }
if $0.paymentMethods.contains(.swipes) && filters.contains(.swipes) { return true }
if filters.contains(.brb) || filters.contains(.swipes) { return false }
return true
}

if filters.contains(.favorites) {
filteredEateries = filteredEateries.filter { $0.hasFavorite }
}

if !filters.isDisjoint(with: Filter.areaFilters) {
filteredEateries = filteredEateries.filter {
guard let area = $0.area else {
Expand Down Expand Up @@ -305,3 +315,9 @@ extension CampusEateriesViewController: CLLocationManagerDelegate {
self.userLocation = userLocation
}
}

extension CampusEateriesViewController: Reloadable {
func reload() {
presentingMenu?.reload()
}
}
Loading

0 comments on commit a48cfbc

Please sign in to comment.