Skip to content

Commit fc650a2

Browse files
authored
Merge pull request #406 from pennlabs/development
Merge for 6.6.3
2 parents e0a18c8 + 5e67823 commit fc650a2

49 files changed

Lines changed: 817 additions & 793 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

PennMobile.xcodeproj/project.pbxproj

Lines changed: 48 additions & 20 deletions
Large diffs are not rendered by default.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "Event@1x.png",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"filename" : "Event@2x.png",
10+
"idiom" : "universal",
11+
"scale" : "2x"
12+
},
13+
{
14+
"filename" : "Event@3x.png",
15+
"idiom" : "universal",
16+
"scale" : "3x"
17+
}
18+
],
19+
"info" : {
20+
"author" : "xcode",
21+
"version" : 1
22+
}
23+
}
4.3 KB
Loading
8.64 KB
Loading
11.3 KB
Loading

PennMobile/Dining/Controllers/DiningViewController.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,7 @@ class DiningViewController: GenericTableViewController {
3434
override func viewWillAppear(_ animated: Bool) {
3535
super.viewWillAppear(animated)
3636
fetchDiningHours()
37-
38-
if UserDefaults.standard.hasDiningPlan() {
39-
if viewModel.balance == nil {
40-
fetchBalance()
41-
} else {
42-
updateBalanceIfNeeded()
43-
}
44-
} else {
45-
viewModel.balance = DiningBalance(diningDollars: 0, visits: 0, guestVisits: 0, lastUpdated: Date())
46-
}
37+
fetchBalance()
4738

4839
if viewModel.venues[.dining]?.isEmpty ?? true {
4940
viewModel.refresh()

PennMobile/Dining/Networking + Cache/DiningAPI.swift

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ class DiningAPI: Requestable {
1313

1414
static let instance = DiningAPI()
1515

16-
let diningUrl = "https://api.pennlabs.org/dining/venues"
17-
let diningMenuUrl = "https://api.pennlabs.org/dining/daily_menu/"
18-
let diningPrefs = "https://api.pennlabs.org/dining/preferences"
19-
let diningBalanceUrl = "https://api.pennlabs.org/dining/balance"
20-
let diningInsightsUrl = "https://studentlife.pennlabs.org/dining/"
16+
let diningUrl = "https://pennmobile.org/api/dining/venues/"
17+
let diningMenuUrl = "https://pennmobile.org/api/dining/daily_menu/"
18+
// TODO: Broken API, need to fetch locally
19+
let diningInsightsUrl = "https://pennmobile.org/api/dining/"
2120

2221
func fetchDiningHours(_ completion: @escaping (_ result: Result<DiningAPIResponse, NetworkingError>) -> Void) {
2322
getRequestData(url: diningUrl) { (data, error, statusCode) in
@@ -30,7 +29,7 @@ class DiningAPI: Requestable {
3029
}
3130

3231
guard let data = data else { return completion(.failure(.other)) }
33-
32+
3433
if let diningAPIResponse = try? JSONDecoder().decode(DiningAPIResponse.self, from: data) {
3534
self.saveToCache(diningAPIResponse.document.venues)
3635
return completion(.success(diningAPIResponse))
@@ -64,7 +63,6 @@ class DiningAPI: Requestable {
6463

6564
func fetchDiningInsights(_ completion: @escaping (_ result: Result<DiningInsightsAPIResponse, NetworkingError>) -> Void ) {
6665
OAuth2NetworkManager.instance.getAccessToken { (token) in
67-
print("token:" + token!.value)
6866
guard let token = token else {
6967
// TODO: - Add network error handling for OAuth2
7068
completion(.failure(.noInternet))
@@ -97,7 +95,6 @@ class DiningAPI: Requestable {
9795
}
9896
task.resume()
9997
}
100-
10198
}
10299

103100
func fetchDetailPageHTML(for venue: DiningVenue, _ completion: @escaping (_ html: String?) -> Void) {
@@ -112,36 +109,10 @@ class DiningAPI: Requestable {
112109
// MARK: - Dining Balance API
113110
extension DiningAPI {
114111
func fetchDiningBalance(_ completion: @escaping (_ diningBalance: DiningBalance?) -> Void) {
115-
OAuth2NetworkManager.instance.getAccessToken { (token) in
116-
guard let token = token else {
117-
completion(nil)
118-
return
119-
}
120-
121-
let url = URL(string: self.diningBalanceUrl)!
122-
let request = URLRequest(url: url, accessToken: token)
123-
124-
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
125-
if let httpResponse = response as? HTTPURLResponse, let data = data, httpResponse.statusCode == 200 {
126-
let json = JSON(data)
127-
let balance = json["balance"]
128-
if let diningDollars = balance["dining_dollars"].float,
129-
let swipes = balance["swipes"].int,
130-
let guestSwipes = balance["guest_swipes"].int,
131-
let timestamp = balance["timestamp"].string {
132-
133-
let formatter = DateFormatter()
134-
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
135-
if let lastUpdated = formatter.date(from: timestamp){
136-
let balance = DiningBalance(diningDollars: diningDollars, visits: swipes, guestVisits: guestSwipes, lastUpdated: lastUpdated)
137-
completion(balance)
138-
return
139-
}
140-
}
141-
}
142-
completion(nil)
143-
}
144-
task.resume()
112+
if Account.isLoggedIn {
113+
CampusExpressNetworkManager.instance.getDiningBalance(completion)
114+
} else {
115+
completion(DiningBalance(diningDollars: 0, visits: 0, guestVisits: 0, lastUpdated: Date()))
145116
}
146117
}
147118
}

PennMobile/Events/EventsAPI.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// EventsAPI.swift
3+
// PennMobile
4+
//
5+
// Created by Samantha Su on 10/1/21.
6+
// Copyright © 2021 PennLabs. All rights reserved.
7+
//
8+
9+
import SwiftyJSON
10+
import Foundation
11+
12+
class EventsAPI: Requestable {
13+
static let instance = EventsAPI()
14+
15+
let eventsUrl = "https://penntoday.upenn.edu/events-feed?_format=json"
16+
17+
func fetchEvents(_ completion: @escaping (_ result: Result<[PennEvents], NetworkingError>) -> Void) {
18+
getRequestData(url: eventsUrl) { (data, error, statusCode) in
19+
if statusCode == nil {
20+
return completion(.failure(.noInternet))
21+
}
22+
23+
if statusCode != 200 {
24+
return completion(.failure(.serverError))
25+
}
26+
27+
guard let data = data else { return completion(.failure(.other)) }
28+
29+
let decoder = JSONDecoder()
30+
31+
let formatter = DateFormatter()
32+
formatter.locale = Locale(identifier: "en_US_POSIX")
33+
formatter.timeZone = TimeZone(abbreviation: "EST")
34+
formatter.dateFormat = "MM/dd/yyyy"
35+
36+
decoder.dateDecodingStrategy = .formatted(formatter)
37+
38+
if let events = try? decoder.decode([PennEvents].self, from: data){
39+
self.saveToCache(events)
40+
completion(.success(events))
41+
} else {
42+
completion(.failure(.serverError))
43+
}
44+
}
45+
}
46+
47+
// MARK: - Cache Methods
48+
func saveToCache(_ response: [PennEvents]) {
49+
Storage.store(response, to: .caches, as: PennEvents.directory)
50+
}
51+
}

PennMobile/Events/PennEvents.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// PennEvents.swift
3+
// PennMobile
4+
//
5+
// Created by Samantha Su on 10/1/21.
6+
// Copyright © 2021 PennLabs. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct PennEvents: Codable {
12+
static let directory = "events.json"
13+
14+
let id: String
15+
let title: String
16+
let body: String
17+
let image: String
18+
let location: StringOrBool
19+
let category: String
20+
let path: String
21+
let start: Date?
22+
let end: Date?
23+
let starttime: String
24+
let endtime: String
25+
let allday: String
26+
var media_image: String
27+
let shortdate: String
28+
29+
var isAllDay: Bool {
30+
return allday == "All day"
31+
}
32+
}
33+
34+
struct StringOrBool: Codable{
35+
var value: String?
36+
init(from decoder: Decoder) throws {
37+
if let string = try? String(from: decoder) {
38+
value = string
39+
return
40+
}
41+
value = nil
42+
}
43+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//
2+
// EventsTableViewCell.swift
3+
// PennMobile
4+
//
5+
// Created by Samantha Su on 10/1/21.
6+
// Copyright © 2021 PennLabs. All rights reserved.
7+
//
8+
9+
import UIKit
10+
import Kingfisher
11+
import SwiftSoup
12+
13+
class PennEventsTableViewCell: UITableViewCell {
14+
15+
lazy var imageExistsConstraint = eventImageView.widthAnchor.constraint(equalToConstant:120)
16+
lazy var imageMissingConstraint = eventImageView.widthAnchor.constraint(equalToConstant:0)
17+
var isExpanded = false
18+
19+
var pennEvent: PennEvents? {
20+
didSet {
21+
guard let event = pennEvent else {return}
22+
if event.media_image == "" {
23+
imageExistsConstraint.isActive = false
24+
imageMissingConstraint.isActive = true
25+
} else {
26+
let imageString = "https://penntoday.upenn.edu" + (event.media_image.slice(from: "<img src=\"", to: "\n") ?? "").trimmingCharacters(in: .whitespaces)
27+
eventImageView.kf.setImage(with: URL(string: imageString))
28+
imageExistsConstraint.isActive = true
29+
imageMissingConstraint.isActive = false
30+
}
31+
titleLabel.text = event.shortdate + ": " + event.title
32+
if let doc: Document = try? SwiftSoup.parse(event.body), let text = try? doc.text() {
33+
bodyLabel.text = text
34+
}
35+
}
36+
}
37+
38+
// MARK: - Views
39+
40+
let containerView:UIView = {
41+
let view = UIView()
42+
view.translatesAutoresizingMaskIntoConstraints = false
43+
view.clipsToBounds = true // this will make sure its children do not go out of the boundary
44+
return view
45+
}()
46+
47+
let eventImageView:UIImageView = {
48+
let img = UIImageView()
49+
img.contentMode = .scaleAspectFill // image will never be strecthed vertially or horizontally
50+
img.translatesAutoresizingMaskIntoConstraints = false // enable autolayout
51+
img.layer.cornerRadius = 7
52+
img.clipsToBounds = true
53+
return img
54+
}()
55+
56+
let titleLabel:UILabel = {
57+
var view = UILabel()
58+
view.backgroundColor = .clear
59+
view.numberOfLines = 0
60+
61+
view.textColor = UIColor(red: 0.122, green: 0.122, blue: 0.122, alpha: 1)
62+
view.font = UIFont(name: "SFProText-Regular", size: 30)
63+
view.translatesAutoresizingMaskIntoConstraints = false
64+
return view
65+
}()
66+
67+
let bodyLabel:UILabel = {
68+
var view = UILabel()
69+
view.backgroundColor = .clear
70+
view.font = UIFont(name: "Helvetica", size: 12)
71+
view.textColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
72+
view.numberOfLines = 3
73+
view.translatesAutoresizingMaskIntoConstraints = false
74+
return view
75+
}()
76+
77+
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
78+
super.init(style: style, reuseIdentifier: reuseIdentifier)
79+
80+
containerView.addSubview(titleLabel)
81+
containerView.addSubview(bodyLabel)
82+
self.contentView.addSubview(containerView)
83+
self.contentView.addSubview(eventImageView)
84+
85+
containerView.leadingAnchor.constraint(equalTo:self.contentView.leadingAnchor, constant:10).isActive = true
86+
containerView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
87+
containerView.bottomAnchor.constraint(equalTo:self.contentView.bottomAnchor, constant:-10).isActive = true
88+
89+
eventImageView.centerYAnchor.constraint(equalTo:self.contentView.centerYAnchor).isActive = true
90+
eventImageView.leadingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:10).isActive = true
91+
eventImageView.trailingAnchor.constraint(equalTo:self.contentView.trailingAnchor, constant:-10).isActive = true
92+
eventImageView.heightAnchor.constraint(equalToConstant:80).isActive = true
93+
94+
titleLabel.topAnchor.constraint(equalTo:containerView.topAnchor, constant:5).isActive = true
95+
titleLabel.trailingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:-10).isActive = true
96+
titleLabel.leadingAnchor.constraint(equalTo:containerView.leadingAnchor, constant:10).isActive = true
97+
98+
bodyLabel.topAnchor.constraint(equalTo:titleLabel.bottomAnchor, constant:0).isActive = true
99+
bodyLabel.bottomAnchor.constraint(equalTo:containerView.bottomAnchor, constant:-5).isActive = true
100+
bodyLabel.trailingAnchor.constraint(equalTo:containerView.trailingAnchor, constant:-10).isActive = true
101+
bodyLabel.leadingAnchor.constraint(equalTo:containerView.leadingAnchor, constant:10).isActive = true
102+
}
103+
104+
required init?(coder aDecoder: NSCoder) {
105+
super.init(coder: aDecoder)
106+
}
107+
108+
}

0 commit comments

Comments
 (0)