Skip to content

Commit 93d7aeb

Browse files
committed
Add Developers aka Contributors View
1 parent d44184d commit 93d7aeb

File tree

6 files changed

+153
-1
lines changed

6 files changed

+153
-1
lines changed

OPass.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
D3A00F2327D837D00097A6AB /* OneSignalNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D3A00F1C27D837D00097A6AB /* OneSignalNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
5959
D3A00F2A27D838920097A6AB /* OneSignal in Frameworks */ = {isa = PBXBuildFile; productRef = D3A00F2927D838920097A6AB /* OneSignal */; };
6060
D3A94D03281E62530053FEFA /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A94D02281E62530053FEFA /* ErrorView.swift */; };
61+
D3B0B20528BBC4400001B311 /* DevelopersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3B0B20428BBC4400001B311 /* DevelopersView.swift */; };
6162
D3C0E21A27D51B460095169F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3C0E21927D51B460095169F /* NetworkExtension.framework */; };
6263
D3C0E21C27D51C060095169F /* WifiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C0E21B27D51C060095169F /* WifiView.swift */; };
6364
D3C6194827CF864B00643D97 /* ScheduleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C6194727CF864B00643D97 /* ScheduleModel.swift */; };
@@ -165,6 +166,7 @@
165166
D3A00F2B27D839B10097A6AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
166167
D3A00F2C27D840C10097A6AB /* OneSignalNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OneSignalNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
167168
D3A94D02281E62530053FEFA /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
169+
D3B0B20428BBC4400001B311 /* DevelopersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevelopersView.swift; sourceTree = "<group>"; };
168170
D3C0E21727D51B460095169F /* OPass.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OPass.entitlements; sourceTree = "<group>"; };
169171
D3C0E21927D51B460095169F /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
170172
D3C0E21B27D51C060095169F /* WifiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiView.swift; sourceTree = "<group>"; };
@@ -271,6 +273,7 @@
271273
children = (
272274
D3C6194C27CF9A7400643D97 /* SettingsView.swift */,
273275
D3EFF36E28A12824004CD8DF /* AppearanceView.swift */,
276+
D3B0B20428BBC4400001B311 /* DevelopersView.swift */,
274277
);
275278
path = Settings;
276279
sourceTree = "<group>";
@@ -687,6 +690,7 @@
687690
4F3A10DC2815201E00CCE5A9 /* ImagePicker.swift in Sources */,
688691
4F5CA37D27FD58D600DEAB39 /* Color+Extension.swift in Sources */,
689692
D318CE44280918C400CEA1AA /* TruncableText.swift in Sources */,
693+
D3B0B20528BBC4400001B311 /* DevelopersView.swift in Sources */,
690694
D3652A792869AF72002AC419 /* NSLineBreakMode+TruncationMode.swift in Sources */,
691695
D3652A782869AF72002AC419 /* TextSizeViewModel.swift in Sources */,
692696
D3E0123327D3667C0040621F /* ScenarioStatusModel.swift in Sources */,

OPass/OPassApp.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ struct OPassApp: App {
2929
ContentView(url: $url)
3030
.onOpenURL { url in
3131
//It seems that both universal link and custom schemed url from firebase are received via onOpenURL, so we must try parse it in both ways.
32-
3332
if DynamicLinks.dynamicLinks().handleUniversalLink(url, completion: { dynamicLink, _ in
3433
if let url = dynamicLink?.url {
3534
UIApplication.currentUIWindow()?.rootViewController?.dismiss(animated: true)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//
2+
// DevelopersView.swift
3+
// OPass
4+
//
5+
// Created by 張智堯 on 2022/8/28.
6+
// 2022 OPass.
7+
//
8+
9+
import SwiftUI
10+
11+
struct DevelopersView: View {
12+
13+
@Environment(\.colorScheme) private var colorScheme
14+
@State private var contributors: [ContributorsModel]? = nil
15+
@State private var error = false
16+
17+
var body: some View {
18+
Group {
19+
if !error {
20+
if let contributors = contributors {
21+
Form {
22+
ForEach(contributors, id: \.id) { contributor in
23+
Button {
24+
if let url = URL(string: contributor.html_url) {
25+
Constants.OpenInAppSafari(forURL: url, style: colorScheme)
26+
}
27+
} label: {
28+
HStack {
29+
AsyncImage(url: URL(string: contributor.avatar_url), transaction: Transaction(animation: .spring())) { phase in
30+
switch phase {
31+
case .success(let image):
32+
image
33+
.resizable().scaledToFit()
34+
default:
35+
Image(systemName: "person.crop.circle.fill")
36+
.resizable().scaledToFit()
37+
.foregroundColor(.gray)
38+
}
39+
}
40+
.clipShape(Circle())
41+
.frame(width: 35)
42+
43+
VStack(alignment: .leading) {
44+
HStack {
45+
if let name = contributor.name {
46+
Text(name)
47+
.foregroundColor(colorScheme == .dark ? .white : .black)
48+
}
49+
Text(contributor.id)
50+
.foregroundColor(.gray)
51+
}
52+
53+
Text("\(contributor.contributions) contribution\(contributor.contributions > 1 ? "s" : "")")
54+
.foregroundColor(.gray)
55+
}
56+
Spacer()
57+
Image("external-link")
58+
.renderingMode(.template)
59+
.resizable()
60+
.scaledToFit()
61+
.foregroundColor(.gray)
62+
.frame(width: UIScreen.main.bounds.width * 0.045)
63+
}
64+
}
65+
}
66+
}
67+
} else {
68+
ProgressView("LOADING")
69+
.task {
70+
do { self.contributors = try await GetContributorsData() }
71+
catch { self.error = true }
72+
}
73+
}
74+
} else {
75+
ErrorWithRetryView {
76+
self.error = false
77+
}
78+
}
79+
}
80+
.navigationTitle("Developers")
81+
.navigationBarTitleDisplayMode(.inline)
82+
.toolbar {
83+
ToolbarItem(placement: .navigationBarTrailing) {
84+
Button {
85+
Constants.OpenInAppSafari(forURL: URL(string: "https://github.com/CCIP-App/CCIP-iOS/graphs/contributors")!, style: colorScheme)
86+
} label: { Image(systemName: "chart.bar.xaxis") }
87+
}
88+
}
89+
}
90+
}
91+
92+
#if DEBUG
93+
struct DevelopersView_Previews: PreviewProvider {
94+
static var previews: some View {
95+
NavigationView {
96+
DevelopersView()
97+
.navigationTitle("Developers")
98+
.navigationBarTitleDisplayMode(.inline)
99+
}
100+
}
101+
}
102+
#endif
103+
104+
// MARK: GitHub REST API
105+
private extension DevelopersView {
106+
func GetContributorsData() async throws -> [ContributorsModel] {
107+
var result: [ContributorsModel] = []
108+
let decoder = JSONDecoder()
109+
let (contributorsData, _) = try await URLSession.shared.data(from: URL(string: "https://api.github.com/repos/CCIP-App/CCIP-iOS/contributors")!)
110+
let contributors = try decoder.decode([RawContributorsModel].self, from: contributorsData)
111+
for contributor in contributors {
112+
let userData = try? await URLSession.shared.data(from: URL(string: "https://api.github.com/users/\(contributor.login)")!)
113+
let user = try? decoder.decode(UserModel.self, from: userData?.0 ?? Data())
114+
result.append(.init(
115+
id: contributor.login,
116+
name: user?.name,
117+
avatar_url: contributor.avatar_url,
118+
html_url: contributor.html_url,
119+
contributions: contributor.contributions
120+
))
121+
}
122+
return result
123+
}
124+
125+
struct ContributorsModel: Hashable, Codable {
126+
var id: String
127+
var name: String?
128+
var avatar_url: String
129+
var html_url: String
130+
var contributions: Int
131+
}
132+
133+
struct RawContributorsModel: Hashable, Codable {
134+
var login: String
135+
var avatar_url: String
136+
var html_url: String
137+
var contributions: Int
138+
}
139+
140+
struct UserModel: Hashable, Codable {
141+
var name: String?
142+
}
143+
}

OPass/Views/Tabs/Settings/SettingsView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ private struct AboutSection: View {
155155
.frame(width: UIScreen.main.bounds.width * 0.045)
156156
}
157157
}
158+
159+
NavigationLink("Developers") {
160+
DevelopersView()
161+
}
158162
}
159163
}
160164
}

Supporting Files/OPass/en.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"ABOUT" = "ABOUT";
9292
"OfficialWebsite" = "Official Website";
9393
"PrivacyPolicy" = "Privacy Policy";
94+
"Developers" = "Developers";
9495
"Version" = "Version";
9596
"ADVANCED" = "ADVANCED";
9697
"AdvancedOption" = "Advanced Option";

Supporting Files/OPass/zh-Hant.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"ABOUT" = "關於";
9292
"OfficialWebsite" = "官方網站";
9393
"PrivacyPolicy" = "隱私權政策";
94+
"Developers" = "開發人員";
9495
"ADVANCED" = "進階";
9596
"AdvancedOption" = "進階選項";
9697
"ClearCacheData" = "清除快取資料";

0 commit comments

Comments
 (0)