Merge pull request #8 from wulkanowy/feature/account-manager

Account manager
This commit is contained in:
Tomasz F 2021-03-26 23:42:13 +01:00 committed by GitHub
commit b99c4efeed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1621 additions and 340 deletions

View file

@ -28,6 +28,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SdkTests"
BuildableName = "SdkTests"
BlueprintName = "SdkTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

View file

@ -13,15 +13,16 @@ let package = Package(
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
.package(url: "https://github.com/krzyzanowskim/OpenSSL", from: "1.1.180"),
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON", from: "5.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Sdk",
dependencies: ["KeychainAccess", "OpenSSL"]),
dependencies: ["KeychainAccess", "OpenSSL", "SwiftyJSON"]),
.testTarget(
name: "SdkTests",
dependencies: ["Sdk"]),

View file

@ -18,6 +18,7 @@ public extension Sdk {
case noPrivateKey
case noSignatureValues
case urlError
case deviceExist
case wrongToken
case wrongSymbol

View file

@ -9,35 +9,25 @@ import Foundation
import KeychainAccess
@available (iOS 14, macOS 11, watchOS 7, tvOS 14, *)
func getSignatures(request: URLRequest, certificate: X509) -> String {
func getSignatures(request: URLRequest, fingerprint: String, privateKeyString: Data) -> String {
guard let urlString = request.url?.absoluteString else {
return "\(Sdk.APIError.urlError)"
}
// Get private key
guard let privateKeyRawData = certificate.getPrivateKeyData(format: .DER),
let privateKeyString = String(data: privateKeyRawData, encoding: .utf8)?
.split(separator: "\n")
.dropFirst()
.dropLast()
.joined()
.data(using: .utf8) else {
return "\(Sdk.APIError.noPrivateKey)"
}
// Create SecKey
// Create SecKey
let attributes = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
]
guard let privateKeyData = Data(base64Encoded: privateKeyString),
let secKey = SecKeyCreateWithData(privateKeyData as NSData, attributes as NSDictionary, nil) else {
return "\(Sdk.APIError.noPrivateKey)"
}
guard let privateKeyData = Data(base64Encoded: privateKeyString),
let secKey = SecKeyCreateWithData(privateKeyData as NSData, attributes as NSDictionary, nil) else {
return "\(Sdk.APIError.noPrivateKey)"
}
// Get fingerprint
guard let signatureValues = Sdk.Signer.getSignatureValues(body: request.httpBody, url: urlString, privateKey: secKey, fingerprint: certificate.getCertificateFingerprint().lowercased()) else {
return "\(Sdk.APIError.noPrivateKey)"
guard let signatureValues = Sdk.Signer.getSignatureValues(body: request.httpBody, url: urlString, privateKey: secKey, fingerprint: fingerprint) else {
return "noFingerprint"
}
// Headers

View file

@ -7,15 +7,17 @@
import Foundation
import KeychainAccess
import Combine
import os
import KeychainAccess
import SwiftyJSON
import SwiftUI
@available (iOS 14, macOS 11, watchOS 7, tvOS 14, *)
public class Sdk {
static private let libraryVersion: String = "0.0.1"
private let loggerSubsystem: String = "com.wulkanowy-ios.Sdk"
private let loggerSubsystem: String = "io.wulkanowy-ios.Sdk"
private var cancellables: Set<AnyCancellable> = []
var firebaseToken: String!
@ -140,9 +142,91 @@ public class Sdk {
let parsedError = self.parseResponse(response) {
completionHandler(parsedError)
} else {
self.getStudents(symbol: symbol, deviceModel: deviceModel)
completionHandler(nil)
do {
let keychain = Keychain()
let allAccountsCheck: String! = keychain["allAccounts"] ?? "[]"
let receiveValueJSON = try! JSON(data: data)
//parsing allAccounts to array
let data = Data(allAccountsCheck.utf8)
do {
var ids = try JSONSerialization.jsonObject(with: data) as! [Int]
if(ids == [])
{
ids = [0]
} else {
ids.append(ids.last! + 1)
}
keychain["allAccounts"] = "\(ids)"
} catch {
print(error)
}
// Get private key
guard let privateKeyRawData = self.certificate.getPrivateKeyData(format: .DER),
let privateKeyString = String(data: privateKeyRawData, encoding: .utf8)?
.split(separator: "\n")
.dropFirst()
.dropLast()
.joined()
.data(using: .utf8) else {
return
}
let privateKeyStringString = String(decoding: privateKeyString, as: UTF8.self)
let fingerprint = self.certificate.getCertificateFingerprint().lowercased()
let saveAccount = """
{
"actualStudent": "0",
"customUsername": "",
"privateKeyString": "\(privateKeyStringString)",
"fingerprint": "\(fingerprint)",
"deviceModel": "\(deviceModel)",
"account": {
"UserName": "\(receiveValueJSON["Envelope"]["UserName"])",
"RestURL": "\(receiveValueJSON["Envelope"]["RestURL"])",
"UserLogin": "\(receiveValueJSON["Envelope"]["UserLogin"])",
"LoginId": "\(receiveValueJSON["Envelope"]["LoginId"])"
}
}
"""
let ids = keychain["allAccounts"]
let dataIds: Data = Data(ids!.utf8)
let idsArray = try JSONSerialization.jsonObject(with: dataIds) as! [Int]
let id = idsArray.last
keychain["\(id!)"] = "\(saveAccount)"
keychain["actualStudentId"] = "\(id!)"
keychain["actualAccountEmail"] = "\(receiveValueJSON["Envelope"]["UserName"])"
let endpointURL: String = "\(receiveValueJSON["Envelope"]["RestURL"])api/mobile/register/hebe"
let apiResponseRequest = apiRequest(endpointURL: endpointURL, id: "\(id!)")
let session = URLSession.shared
session.dataTask(with: apiResponseRequest) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
let responseBody = String(data: data, encoding: String.Encoding.utf8)
keychain["actualStudentHebe"] = "\(responseBody!)"
} else {
// Handle unexpected error
}
}.resume()
} catch {
print(error)
}
}
completionHandler(nil)
})
.store(in: &cancellables)
}
@ -190,6 +274,8 @@ public class Sdk {
let keychain = Keychain()
keychain[string: "keyFingerprint"] = keyFingerprint
//Boże, proszę, dlaczego to nie działa, błagam
// Body
let body: [String: Encodable?] = [
"AppName": "DzienniczekPlus 2.0",
@ -211,7 +297,7 @@ public class Sdk {
"Timestamp": now.millisecondsSince1970,
"TimestampFormatted": "\(timestampFormatted) GMT"
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
request.allHTTPHeaderFields = [
@ -225,50 +311,6 @@ public class Sdk {
return URLSession.shared.dataTaskPublisher(for: signedRequest)
}
private func getStudents(symbol: String, deviceModel: String) {
let url = "\(self.endpointURL!)/\(symbol)/api/mobile/register/hebe"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let now = Date()
var vDate: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
return "\(dateFormatter.string(from: now)) GMT"
}
let signatures = getSignatures(request: request, certificate: certificate)
request.setValue("\(signatures)", forHTTPHeaderField: "Signature")
request.allHTTPHeaderFields = [
"User-Agent": "wulkanowy/1 CFNetwork/1220.1 Darwin/20.1.0",
"vOS": "iOS",
"vDeviceModel": deviceModel,
"vAPI": "1",
"vDate": vDate,
"vCanonicalUrl": "api%2fmobile%2fregister%2fhebe"
]
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
print(String(data: data, encoding: String.Encoding.utf8) as Any)
} else {
// Handle unexpected error
}
}
task.resume()
}
// MARK: - Helper functions
/// Parses the response
@ -287,6 +329,7 @@ public class Sdk {
case 200: return APIError.wrongToken
case -1: return APIError.wrongSymbol //Ya, Vulcan returns -1 code
case 203: return APIError.wrongPin
case 205: return APIError.deviceExist
default: return nil
}
}

View file

@ -0,0 +1,54 @@
//
// apiRequest.swift
//
//
// Created by Tomasz on 02/03/2021.
//
import Foundation
import SwiftUI
import SwiftyJSON
import KeychainAccess
@available (iOS 14, macOS 11, watchOS 7, tvOS 14, *)
public func apiRequest(endpointURL: String, id: String) -> URLRequest {
var request = URLRequest(url: URL(string: endpointURL)!)
request.httpMethod = "GET"
let keychain = Keychain()
let account = keychain[id] ?? "[]"
let data = Data(account.utf8)
let accountJSON = try! JSON(data: data)
let fingerprint: String = "\(accountJSON["fingerprint"])"
let privateKeyStringString: String = "\(accountJSON["privateKeyString"])"
let privateKeyString: Data = Data(privateKeyStringString.utf8)
let deviceModel: String = "\(accountJSON["deviceModel"])"
let now = Date()
var vDate: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
return "\(dateFormatter.string(from: now)) GMT"
}
let signatures = getSignatures(request: request, fingerprint: fingerprint, privateKeyString: privateKeyString)
request.setValue("\(signatures)", forHTTPHeaderField: "Signature")
request.allHTTPHeaderFields = [
"User-Agent": "wulkanowy/1 CFNetwork/1220.1 Darwin/20.1.0",
"vOS": "iOS",
"vDeviceModel": deviceModel,
"vAPI": "1",
"vDate": vDate,
"vCanonicalUrl": "api%2fmobile%2fregister%2fhebe"
]
return request
}

View file

@ -0,0 +1,45 @@
//
// File.swift
//
//
// Created by Tomasz on 12/03/2021.
//
import Foundation
import KeychainAccess
import SwiftyJSON
import SwiftUI
@available (iOS 14, macOS 11, watchOS 7, tvOS 14, *)
public func getHebe(id: String, account: JSON) -> JSON {
let RestURL = "\(account["account"]["RestURL"])api/mobile/register/hebe"
let keychain = Keychain()
let hebeRequest = apiRequest(endpointURL: "\(RestURL)", id: id)
let session = URLSession.shared
var hebe: String = ""
session.dataTask(with: hebeRequest) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
let responseBody = String(data: data, encoding: String.Encoding.utf8)
hebe = "\(responseBody!)"
} else {
// Handle unexpected error
}
}.resume()
var hebeJSON: JSON?
while true {
if(hebe != ""){
let data: Data = Data(hebe.utf8)
hebeJSON = try! JSON(data: data)
break
} else {
continue
}
}
return hebeJSON!
}

View file

@ -6,7 +6,47 @@
//
import Foundation
import KeychainAccess
import SwiftyJSON
@available (iOS 14, macOS 11, watchOS 7, tvOS 14, *)
public func getLuckyNumber() -> Int {
let keychain = Keychain()
let actualStudentId = "\(keychain["actualStudentId"] ?? "0")"
let actualAccount = keychain[actualStudentId]
let dataAccount: Data = Data(actualAccount!.utf8)
let actualAccountJSON = try! JSON(data: dataAccount)
let studentId = Int("\(actualAccountJSON["actualStudent"])")
let actualStudentHebe = keychain["actualStudentHebe"]
let dataHebe: Data = Data(actualStudentHebe!.utf8)
let actualStudentHebeJSON = try! JSON(data: dataHebe)
let actualStudent = actualStudentHebeJSON["Envelope"][studentId!]
let Id = "\(actualStudent["ConstituentUnit"]["Id"])"
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let day = formatter.string(from: date)
let RestURL = "\(actualStudent["Unit"]["RestURL"])/mobile/school/lucky?constituentId=\(Id)&day=\(day)"
let apiResponseRequest = apiRequest(endpointURL: RestURL, id: actualStudentId)
let session = URLSession.shared
session.dataTask(with: apiResponseRequest) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
let responseBody = String(data: data, encoding: String.Encoding.utf8)
print(responseBody!)
} else {
// Handle unexpected error
}
}.resume()
return 7
}

View file

@ -7,28 +7,34 @@
objects = {
/* Begin PBXBuildFile section */
5C0B2AFD25F78A6A009EABEC /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 5C0B2AFC25F78A6A009EABEC /* KeychainAccess */; };
5C0ED1E3260D2704000DE4C6 /* PullToRefresh.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0ED1E2260D2704000DE4C6 /* PullToRefresh.swift */; };
5C1794B425E8FDFB007AD91A /* messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1794B325E8FDFB007AD91A /* messages.swift */; };
5C1794B825E8FE08007AD91A /* notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1794B725E8FE08007AD91A /* notes.swift */; };
5C1794BC25E8FE19007AD91A /* settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1794BB25E8FE19007AD91A /* settings.swift */; };
5C1794C025E8FE27007AD91A /* about.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1794BF25E8FE27007AD91A /* about.swift */; };
5C1794CD25E90DBD007AD91A /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 5C1794CC25E90DBD007AD91A /* KeychainAccess */; };
5C1CFA7A25EA32AE0047286F /* ghImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1CFA7925EA32AE0047286F /* ghImage.swift */; };
5C2D331025E64F3C000253AC /* grades.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2D330F25E64F3C000253AC /* grades.swift */; };
5C2D331425E650EC000253AC /* exams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2D331325E650EC000253AC /* exams.swift */; };
5C2D331825E651C4000253AC /* homework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2D331725E651C4000253AC /* homework.swift */; };
5C2D331C25E651FB000253AC /* more.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2D331B25E651FB000253AC /* more.swift */; };
5C2D331425E650EC000253AC /* calendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2D331325E650EC000253AC /* calendar.swift */; };
5C2D9A3225F75A760053CB9F /* Sdk in Frameworks */ = {isa = PBXBuildFile; productRef = 5C2D9A3125F75A760053CB9F /* Sdk */; };
5C42E2A225EE656E0048DDCD /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 5C42E2A125EE656E0048DDCD /* SwiftyJSON */; };
5C478F3525DC742100ABEFB7 /* VulcanStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C478F3425DC742100ABEFB7 /* VulcanStore.swift */; };
5C5162D825F0DCDE00EF0777 /* more.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5162D725F0DCDE00EF0777 /* more.swift */; };
5C5162DC25F0DD9200EF0777 /* attendance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5162DB25F0DD9200EF0777 /* attendance.swift */; };
5C84E04625FAC5440088FD47 /* personalInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C84E04525FAC5440088FD47 /* personalInfo.swift */; };
5C84E04A25FAC5520088FD47 /* residenceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C84E04925FAC5520088FD47 /* residenceInfo.swift */; };
5C84E04E25FAC5670088FD47 /* contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C84E04D25FAC5670088FD47 /* contact.swift */; };
5C84E05225FAC5700088FD47 /* family.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C84E05125FAC5700088FD47 /* family.swift */; };
5C89C8F525EA6AA4000B5816 /* licenses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C89C8F425EA6AA4000B5816 /* licenses.swift */; };
5C89C90625EA7996000B5816 /* SwiftUIEKtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 5C89C90525EA7996000B5816 /* SwiftUIEKtensions */; };
5C9B6F4925D6C08D00C3F5F5 /* Sdk in Frameworks */ = {isa = PBXBuildFile; productRef = 5C9B6F4825D6C08D00C3F5F5 /* Sdk */; };
5CB39737260D4BE200349B0D /* wulkanowyTests in Resources */ = {isa = PBXBuildFile; fileRef = 5CB39735260D4BE200349B0D /* wulkanowyTests */; };
5CB39738260D4BE200349B0D /* LinuxMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB39736260D4BE200349B0D /* LinuxMain.swift */; };
5CB9907225EE4A3200AA405C /* CustomButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9907125EE4A3200AA405C /* CustomButtonView.swift */; };
5CB9907625EE4A3B00AA405C /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9907525EE4A3B00AA405C /* OnboardingView.swift */; };
5CC2EAA525E516F100B6183E /* dashboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC2EAA425E516F100B6183E /* dashboard.swift */; };
5CC2EAAE25E526B500B6183E /* navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC2EAAD25E526B500B6183E /* navigation.swift */; };
5CC80E9825F2CAA900F6DE60 /* accountCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC80E9725F2CAA900F6DE60 /* accountCard.swift */; };
5CCAE31625DA4CDD00D87580 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 5CCAE31525DA4CDD00D87580 /* OpenSSL */; };
5CCB276325EF9C4A00482F4A /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB276225EF9C4A00482F4A /* OnboardingView.swift */; };
5CEA516B25D540B900DB45BD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CEA516D25D540B900DB45BD /* Localizable.strings */; };
5CF095AD25F053B10068F2C6 /* accountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF095AC25F053B10068F2C6 /* accountManager.swift */; };
5CF6695F25EEE2FD00AC0A86 /* chooseStudent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF6695E25EEE2FD00AC0A86 /* chooseStudent.swift */; };
F4C6D9082544E17400F8903A /* wulkanowyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C6D9072544E17400F8903A /* wulkanowyApp.swift */; };
F4C6D90A2544E17400F8903A /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C6D9092544E17400F8903A /* LoginView.swift */; };
F4C6D90C2544E17500F8903A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C6D90B2544E17500F8903A /* Assets.xcassets */; };
@ -77,26 +83,31 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
5C0ED1E2260D2704000DE4C6 /* PullToRefresh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PullToRefresh.swift; sourceTree = "<group>"; };
5C1794B325E8FDFB007AD91A /* messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = messages.swift; sourceTree = "<group>"; };
5C1794B725E8FE08007AD91A /* notes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = notes.swift; sourceTree = "<group>"; };
5C1794BB25E8FE19007AD91A /* settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = settings.swift; sourceTree = "<group>"; };
5C1794BF25E8FE27007AD91A /* about.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = about.swift; sourceTree = "<group>"; };
5C1CFA7925EA32AE0047286F /* ghImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ghImage.swift; sourceTree = "<group>"; };
5C2D330F25E64F3C000253AC /* grades.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = grades.swift; sourceTree = "<group>"; };
5C2D331325E650EC000253AC /* exams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = exams.swift; sourceTree = "<group>"; };
5C2D331725E651C4000253AC /* homework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = homework.swift; sourceTree = "<group>"; };
5C2D331B25E651FB000253AC /* more.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = more.swift; sourceTree = "<group>"; };
5C2D331325E650EC000253AC /* calendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = calendar.swift; sourceTree = "<group>"; };
5C478F3425DC742100ABEFB7 /* VulcanStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VulcanStore.swift; sourceTree = "<group>"; };
5C5162D725F0DCDE00EF0777 /* more.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = more.swift; sourceTree = "<group>"; };
5C5162DB25F0DD9200EF0777 /* attendance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = attendance.swift; sourceTree = "<group>"; };
5C84E04525FAC5440088FD47 /* personalInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = personalInfo.swift; sourceTree = "<group>"; };
5C84E04925FAC5520088FD47 /* residenceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = residenceInfo.swift; sourceTree = "<group>"; };
5C84E04D25FAC5670088FD47 /* contact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = contact.swift; sourceTree = "<group>"; };
5C84E05125FAC5700088FD47 /* family.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = family.swift; sourceTree = "<group>"; };
5C89C8F425EA6AA4000B5816 /* licenses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = licenses.swift; sourceTree = "<group>"; };
5C9B6E4925D6ADFB00C3F5F5 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
5C9B6F4525D6C06D00C3F5F5 /* Sdk */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Sdk; sourceTree = "<group>"; };
5CB39735260D4BE200349B0D /* wulkanowyTests */ = {isa = PBXFileReference; lastKnownFileType = folder; name = wulkanowyTests; path = "../../../../../Library/Mobile Documents/com~apple~CloudDocs/.Trash/Tests/wulkanowyTests"; sourceTree = "<group>"; };
5CB39736260D4BE200349B0D /* LinuxMain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LinuxMain.swift; path = "../../../../../Library/Mobile Documents/com~apple~CloudDocs/.Trash/Tests/LinuxMain.swift"; sourceTree = "<group>"; };
5CB9907125EE4A3200AA405C /* CustomButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CustomButtonView.swift; path = ../../../../../../Shared/CustomButtonView.swift; sourceTree = "<group>"; };
5CB9907525EE4A3B00AA405C /* OnboardingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OnboardingView.swift; path = ../../../../../../Onboarding/OnboardingView.swift; sourceTree = "<group>"; };
5CC2EAA425E516F100B6183E /* dashboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dashboard.swift; sourceTree = "<group>"; };
5CC2EAAD25E526B500B6183E /* navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = navigation.swift; sourceTree = "<group>"; };
5CC80E9725F2CAA900F6DE60 /* accountCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = accountCard.swift; sourceTree = "<group>"; };
5CCB276225EF9C4A00482F4A /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
5CEA516C25D540B900DB45BD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
5CF095AC25F053B10068F2C6 /* accountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = accountManager.swift; sourceTree = "<group>"; };
5CF6695E25EEE2FD00AC0A86 /* chooseStudent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = chooseStudent.swift; sourceTree = "<group>"; };
5CF81BD725D9D44400B12C4C /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/Localizable.strings"; sourceTree = "<group>"; };
F4C6D9042544E17400F8903A /* wulkanowy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wulkanowy.app; sourceTree = BUILT_PRODUCTS_DIR; };
F4C6D9072544E17400F8903A /* wulkanowyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = wulkanowyApp.swift; sourceTree = "<group>"; };
@ -116,10 +127,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C9B6F4925D6C08D00C3F5F5 /* Sdk in Frameworks */,
5CCAE31625DA4CDD00D87580 /* OpenSSL in Frameworks */,
5C1794CD25E90DBD007AD91A /* KeychainAccess in Frameworks */,
5C2D9A3225F75A760053CB9F /* Sdk in Frameworks */,
5C42E2A225EE656E0048DDCD /* SwiftyJSON in Frameworks */,
5C89C90625EA7996000B5816 /* SwiftUIEKtensions in Frameworks */,
5C0B2AFD25F78A6A009EABEC /* KeychainAccess in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -151,12 +163,13 @@
5C1B848925E1B6910074F29D /* Views */ = {
isa = PBXGroup;
children = (
5CB9907025EE4A2400AA405C /* Shared */,
5CC80E9625F2CA8A00F6DE60 /* AccountManager */,
5CB9906F25EE4A1C00AA405C /* Onboarding */,
5CC2EAAC25E5269E00B6183E /* Navigation */,
5CC2EAA325E516DD00B6183E /* Content */,
5C1B849F25E1B7A30074F29D /* Login */,
5C1CFA7925EA32AE0047286F /* ghImage.swift */,
5C0ED1E2260D2704000DE4C6 /* PullToRefresh.swift */,
);
path = Views;
sourceTree = "<group>";
@ -172,6 +185,7 @@
5C1B849F25E1B7A30074F29D /* Login */ = {
isa = PBXGroup;
children = (
5CF6695E25EEE2FD00AC0A86 /* chooseStudent.swift */,
F4C6D9092544E17400F8903A /* LoginView.swift */,
);
path = Login;
@ -188,32 +202,24 @@
5CB9906F25EE4A1C00AA405C /* Onboarding */ = {
isa = PBXGroup;
children = (
5CB9907525EE4A3B00AA405C /* OnboardingView.swift */,
5CCB276225EF9C4A00482F4A /* OnboardingView.swift */,
);
path = Onboarding;
sourceTree = "<group>";
};
5CB9907025EE4A2400AA405C /* Shared */ = {
isa = PBXGroup;
children = (
5CB9907125EE4A3200AA405C /* CustomButtonView.swift */,
);
path = Shared;
sourceTree = "<group>";
};
5CC2EAA325E516DD00B6183E /* Content */ = {
isa = PBXGroup;
children = (
5CC2EAA425E516F100B6183E /* dashboard.swift */,
5C2D330F25E64F3C000253AC /* grades.swift */,
5C2D331325E650EC000253AC /* exams.swift */,
5C2D331725E651C4000253AC /* homework.swift */,
5C2D331B25E651FB000253AC /* more.swift */,
5C2D331325E650EC000253AC /* calendar.swift */,
5C1794B325E8FDFB007AD91A /* messages.swift */,
5C1794B725E8FE08007AD91A /* notes.swift */,
5C1794BB25E8FE19007AD91A /* settings.swift */,
5C1794BF25E8FE27007AD91A /* about.swift */,
5C89C8F425EA6AA4000B5816 /* licenses.swift */,
5C5162D725F0DCDE00EF0777 /* more.swift */,
5C5162DB25F0DD9200EF0777 /* attendance.swift */,
);
path = Content;
sourceTree = "<group>";
@ -226,6 +232,19 @@
path = Navigation;
sourceTree = "<group>";
};
5CC80E9625F2CA8A00F6DE60 /* AccountManager */ = {
isa = PBXGroup;
children = (
5CF095AC25F053B10068F2C6 /* accountManager.swift */,
5CC80E9725F2CAA900F6DE60 /* accountCard.swift */,
5C84E04525FAC5440088FD47 /* personalInfo.swift */,
5C84E04925FAC5520088FD47 /* residenceInfo.swift */,
5C84E04D25FAC5670088FD47 /* contact.swift */,
5C84E05125FAC5700088FD47 /* family.swift */,
);
path = AccountManager;
sourceTree = "<group>";
};
F4C6D8FB2544E17300F8903A = {
isa = PBXGroup;
children = (
@ -308,10 +327,11 @@
);
name = wulkanowy;
packageProductDependencies = (
5C9B6F4825D6C08D00C3F5F5 /* Sdk */,
5CCAE31525DA4CDD00D87580 /* OpenSSL */,
5C1794CC25E90DBD007AD91A /* KeychainAccess */,
5C89C90525EA7996000B5816 /* SwiftUIEKtensions */,
5C42E2A125EE656E0048DDCD /* SwiftyJSON */,
5C2D9A3125F75A760053CB9F /* Sdk */,
5C0B2AFC25F78A6A009EABEC /* KeychainAccess */,
);
productName = wulkanowy;
productReference = F4C6D9042544E17400F8903A /* wulkanowy.app */;
@ -387,8 +407,9 @@
mainGroup = F4C6D8FB2544E17300F8903A;
packageReferences = (
5CCAE31025DA4CCA00D87580 /* XCRemoteSwiftPackageReference "OpenSSL" */,
5C1794CB25E90DBD007AD91A /* XCRemoteSwiftPackageReference "KeychainAccess" */,
5C89C90425EA7996000B5816 /* XCRemoteSwiftPackageReference "SwiftUIEKtensions" */,
5C42E2A025EE656E0048DDCD /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
5C0B2AFB25F78A6A009EABEC /* XCRemoteSwiftPackageReference "KeychainAccess" */,
);
productRefGroup = F4C6D9052544E17400F8903A /* Products */;
projectDirPath = "";
@ -434,23 +455,30 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5C2D331425E650EC000253AC /* exams.swift in Sources */,
5C2D331425E650EC000253AC /* calendar.swift in Sources */,
5C478F3525DC742100ABEFB7 /* VulcanStore.swift in Sources */,
5C1CFA7A25EA32AE0047286F /* ghImage.swift in Sources */,
5CB9907225EE4A3200AA405C /* CustomButtonView.swift in Sources */,
5CCB276325EF9C4A00482F4A /* OnboardingView.swift in Sources */,
5C1794BC25E8FE19007AD91A /* settings.swift in Sources */,
5C2D331025E64F3C000253AC /* grades.swift in Sources */,
5C2D331C25E651FB000253AC /* more.swift in Sources */,
5CF6695F25EEE2FD00AC0A86 /* chooseStudent.swift in Sources */,
5C1794C025E8FE27007AD91A /* about.swift in Sources */,
F4C6D90A2544E17400F8903A /* LoginView.swift in Sources */,
5C89C8F525EA6AA4000B5816 /* licenses.swift in Sources */,
5C84E04A25FAC5520088FD47 /* residenceInfo.swift in Sources */,
5C84E05225FAC5700088FD47 /* family.swift in Sources */,
5CC2EAAE25E526B500B6183E /* navigation.swift in Sources */,
5C2D331825E651C4000253AC /* homework.swift in Sources */,
5C0ED1E3260D2704000DE4C6 /* PullToRefresh.swift in Sources */,
5C5162D825F0DCDE00EF0777 /* more.swift in Sources */,
5CF095AD25F053B10068F2C6 /* accountManager.swift in Sources */,
5C84E04E25FAC5670088FD47 /* contact.swift in Sources */,
5C84E04625FAC5440088FD47 /* personalInfo.swift in Sources */,
5C1794B425E8FDFB007AD91A /* messages.swift in Sources */,
5CC80E9825F2CAA900F6DE60 /* accountCard.swift in Sources */,
5CC2EAA525E516F100B6183E /* dashboard.swift in Sources */,
5C1794B825E8FE08007AD91A /* notes.swift in Sources */,
5CB9907625EE4A3B00AA405C /* OnboardingView.swift in Sources */,
F4C6D9082544E17400F8903A /* wulkanowyApp.swift in Sources */,
5C5162DC25F0DD9200EF0777 /* attendance.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -536,6 +564,10 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SRCROOT)",
"$(inherited)",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -598,6 +630,10 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SRCROOT)",
"$(inherited)",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -633,6 +669,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "io.wulkanowy.wulkanowy-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -655,6 +692,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "io.wulkanowy.wulkanowy-ios";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -782,12 +820,20 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
5C1794CB25E90DBD007AD91A /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
5C0B2AFB25F78A6A009EABEC /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 4.2.1;
minimumVersion = 4.2.2;
};
};
5C42E2A025EE656E0048DDCD /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 5.0.0;
};
};
5C89C90425EA7996000B5816 /* XCRemoteSwiftPackageReference "SwiftUIEKtensions" */ = {
@ -809,20 +855,25 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
5C1794CC25E90DBD007AD91A /* KeychainAccess */ = {
5C0B2AFC25F78A6A009EABEC /* KeychainAccess */ = {
isa = XCSwiftPackageProductDependency;
package = 5C1794CB25E90DBD007AD91A /* XCRemoteSwiftPackageReference "KeychainAccess" */;
package = 5C0B2AFB25F78A6A009EABEC /* XCRemoteSwiftPackageReference "KeychainAccess" */;
productName = KeychainAccess;
};
5C2D9A3125F75A760053CB9F /* Sdk */ = {
isa = XCSwiftPackageProductDependency;
productName = Sdk;
};
5C42E2A125EE656E0048DDCD /* SwiftyJSON */ = {
isa = XCSwiftPackageProductDependency;
package = 5C42E2A025EE656E0048DDCD /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
productName = SwiftyJSON;
};
5C89C90525EA7996000B5816 /* SwiftUIEKtensions */ = {
isa = XCSwiftPackageProductDependency;
package = 5C89C90425EA7996000B5816 /* XCRemoteSwiftPackageReference "SwiftUIEKtensions" */;
productName = SwiftUIEKtensions;
};
5C9B6F4825D6C08D00C3F5F5 /* Sdk */ = {
isa = XCSwiftPackageProductDependency;
productName = Sdk;
};
5CCAE31525DA4CDD00D87580 /* OpenSSL */ = {
isa = XCSwiftPackageProductDependency;
package = 5CCAE31025DA4CCA00D87580 /* XCRemoteSwiftPackageReference "OpenSSL" */;

View file

@ -28,6 +28,26 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F4C6D9142544E17500F8903A"
BuildableName = "wulkanowyTests.xctest"
BlueprintName = "wulkanowyTests"
ReferencedContainer = "container:wulkanowy.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F4C6D91F2544E17500F8903A"
BuildableName = "wulkanowyUITests.xctest"
BlueprintName = "wulkanowyUITests"
ReferencedContainer = "container:wulkanowy.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

View file

@ -8,12 +8,48 @@
import SwiftUI
import Sdk
import Combine
import SwiftyJSON
import KeychainAccess
@main
struct wulkanowyApp: App {
@AppStorage("isLogged") private var isLogged: Bool = false
@AppStorage("needsAppOnboarding") private var needsAppOnboarding: Bool = true
init() {
if(isLogged == true) {
let keychain = Keychain()
let actualStudentId = "\(keychain["actualStudentId"] ?? "0")"
let accountString = keychain[actualStudentId]
let data: Data = Data(accountString!.utf8)
let account = try! JSON(data: data)
let endpointURL: String = "\(account["account"]["RestURL"])api/mobile/register/hebe"
let apiResponseRequest = apiRequest(endpointURL: endpointURL, id: actualStudentId)
let session = URLSession.shared
session.dataTask(with: apiResponseRequest) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
let responseBody = String(data: data, encoding: String.Encoding.utf8)
keychain["actualStudentHebe"] = "\(responseBody!)"
} else {
// Handle unexpected error
}
}.resume()
}
}
var body: some Scene {
WindowGroup {
NavigationBarView()
if(needsAppOnboarding == true) {
OnboardingView()
} else {
NavigationBarView()
}
}
}
}

View file

@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2E",
"green" : "0x2E",
"red" : "0xD2"
"blue" : "0.180",
"green" : "0.180",
"red" : "0.824"
}
},
"idiom" : "universal"

View file

@ -5,9 +5,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2E",
"green" : "0x2E",
"red" : "0xD2"
"blue" : "0.180",
"green" : "0.180",
"red" : "0.824"
}
},
"idiom" : "universal"
@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2E",
"green" : "0x2E",
"red" : "0xD2"
"blue" : "0.180",
"green" : "0.180",
"red" : "0.824"
}
},
"idiom" : "universal"

View file

@ -1,9 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"compression-type" : "automatic"
}
}

View file

@ -6,6 +6,19 @@
*/
//ONBOARDING
"onboardingTitle" = "Welcome to Wulkanowy!";
"notificationsOnboarding" = "Notifications";
"messagesOnboarding" = "Messages";
"fast" = "Fast";
"notificationsOnboardingContent" = "Forget about saving exams in calendar";
"messagesOnboardingContent" = "Simple contact with your favorite teachers";
"fastContent" = "Fast as Sonic";
"continueButton" = "Let's go!";
//LOGIN SCREEN
"loginTitle" = "Log In";
"token" = "Token";
@ -18,12 +31,18 @@
"wrongPin" = "Wrong pin";
"invalidData" = "Wrong token, symbol or pin";
"success" = "Success";
"accountRegistered" = "Account registered";
"accountRegisteredContent" = "The account has already registered. Please log in to another account.";
//CHOOSE ACCOUNT
"registerButton" = "Register";
"selectStudent" = "Select student";
//NAVIGATION
"dashboardButton" = "Dashboard";
"gradesButton" = "Grades";
"examsButton" = "Exams";
"homeworkButton" = "Homework";
"calendarButton" = "Calendar";
"attendanceButton" = "Attendance";
"moreButton" = "More";
//MORE
@ -45,3 +64,7 @@
//LICENCES
"noLicence" = "No licence";
//ACCOUNT MANAGER
"addAccount" = "Add account";
"chooseAccount" = "Choose account";

View file

@ -6,6 +6,19 @@
*/
//ONBOARDING
"onboardingTitle" = "Witaj w Wulkanowym!";
"notificationsOnboarding" = "Powiadomienia";
"messagesOnboarding" = "Wiadomości";
"fast" = "Prędkość";
"notificationsOnboardingContent" = "Zapomnij o zapisywaniu sprawdzianów w kalendarzyku";
"messagesOnboardingContent" = "Prosty kontakt z twoimi ulubionymi nauczycielami";
"fastContent" = "Szybki, niczym Sonic";
"continueButton" = "Zaczynajmy!";
//LOGIN SCREEN
"loginTitle" = "Logowanie";
"token" = "Token";
@ -18,12 +31,18 @@
"wrongPin" = "Zły pin";
"invalidData" = "Zły token, symbol lub pin";
"success" = "Sukces";
"accountRegistered" = "Konto zarejestrowane";
"accountRegisteredContent" = "Konto zostało już zarejestrowane. Proszę zalogowaź się na inne konto.";
//CHOOSE ACCOUNT
"registerButton" = "Zarejestruj";
"selectStudent" = "Wybierz ucznia";
//NAVIGATION
"dashboardButton" = "Start";
"gradesButton" = "Oceny";
"examsButton" = "Sprawdziany";
"homeworkButton" = "Zadania";
"calendarButton" = "Kalendarz";
"attendanceButton" = "Frekwencja";
"moreButton" = "Więcej";
//MORE
@ -45,3 +64,7 @@
//LICENCES
"noLicence" = "Brak licencji";
//ACCOUNT MANAGER
"addAccount" = "Dodaj konto";
"chooseAccount" = "Wybierz konto";

View file

@ -0,0 +1,227 @@
//
// accountCard.swift
// wulkanowy
//
// Created by Tomasz on 05/03/2021.
//
import SwiftUI
import KeychainAccess
import SwiftyJSON
import Sdk
struct AccountCardView: View {
@Environment(\.presentationMode) var presentation
@State private var account: JSON = []
@State private var username: String = ""
@State private var showAlert = false
@State private var hebe: JSON = []
@State private var displayStudents: Array<String> = Array()
@State private var selectedStudent: String = ""
@State private var studentSchool = ""
let id: String
let keychain = Keychain()
private func saveNewUsername(newUsername: String) {
var accountJSON = self.account
accountJSON["customUsername"].stringValue = "\(newUsername)"
keychain[self.id] = "\(accountJSON)"
getAccount()
getUsername()
}
private func alert() {
let alert = UIAlertController(title: "Zmiana nazwy", message: "Tu wpisz swoją nową nazwę konta", preferredStyle: .alert)
alert.addTextField() { textField in
textField.placeholder = username
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in })
alert.addAction(UIAlertAction(title: "Save", style: .default) { _ in let textField = alert.textFields![0]
let newUsername = "\(textField.text ?? "\(username)")"
saveNewUsername(newUsername: newUsername)})
showAlert(alert: alert)
}
func showAlert(alert: UIAlertController) {
if let controller = topMostViewController() {
controller.present(alert, animated: true)
}
}
private func keyWindow() -> UIWindow? {
return UIApplication.shared.connectedScenes
.filter {$0.activationState == .foregroundActive}
.compactMap {$0 as? UIWindowScene}
.first?.windows.filter {$0.isKeyWindow}.first
}
private func topMostViewController() -> UIViewController? {
guard let rootController = keyWindow()?.rootViewController else {
return nil
}
return topMostViewController(for: rootController)
}
private func topMostViewController(for controller: UIViewController) -> UIViewController {
if let presentedController = controller.presentedViewController {
return topMostViewController(for: presentedController)
} else if let navigationController = controller as? UINavigationController {
guard let topController = navigationController.topViewController else {
return navigationController
}
return topMostViewController(for: topController)
} else if let tabController = controller as? UITabBarController {
guard let topController = tabController.selectedViewController else {
return tabController
}
return topMostViewController(for: topController)
}
return controller
}
init() {
self.id = keychain["editAccountId"] ?? "0"
}
private func getActualStudentName() -> String {
let account = keychain[self.id] ?? "0"
let data: Data = Data(account.utf8)
let accountJSON = try! JSON(data: data)
let actualStudentId = Int("\(accountJSON["actualStudent"])") ?? 0
return "\(hebe["Envelope"][actualStudentId]["Login"]["DisplayName"])"
}
private func getStudents() -> Array<String> {
var i: Int = 0
var displayStudentsFunc: Array<String> = Array()
while true {
if (String(describing: hebe["Envelope"][i]) == "null") {
break
}
else {
displayStudentsFunc.append(String(describing: hebe["Envelope"][i]["Login"]["DisplayName"]))
i += 1
}
}
return displayStudentsFunc
}
private func getAccount() {
let account = "\(keychain[self.id] ?? "{}")"
let data: Data = Data(account.utf8)
self.account = try! JSON(data: data)
}
private func getUsername() {
if("\(account["customUsername"])".isEmpty) {
username = "\(account["account"]["UserName"])"
} else {
username = "\(account["customUsername"])"
}
}
private func getSchoolName() -> String {
let actualStudent = Int("\(account["actualStudent"])") ?? 0
return "\(hebe["Envelope"][actualStudent]["Unit"]["DisplayName"])"
}
private func done() {
presentation.wrappedValue.dismiss()
}
var body: some View {
NavigationView {
VStack {
HStack {
Spacer()
let pencilSymbol = Image(systemName: "pencil")
Button("\(pencilSymbol)") { alert() }
.font(.system(size: 25))
.padding()
.multilineTextAlignment(.trailing)
}
Spacer()
Image(systemName: "person.circle")
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 92)
.foregroundColor(.accentColor)
.padding(.bottom)
Text("\(username)")
.font(.title)
Spacer()
Text(self.studentSchool)
.fontWeight(.light)
Form {
Section(header: Text("Aktualny uczeń")) {
HStack {
Text("\(selectedStudent)")
Spacer()
Picker(selection: $selectedStudent, label: Image(systemName:"pencil")) {
ForEach(displayStudents, id: \.self) { student in
Text(student)
}
}.pickerStyle(MenuPickerStyle())
}
}
Section {
NavigationLink(destination: PersonalInfoView()) {
Label("Dane osobowe", systemImage: "person")
.accessibility(label: Text("Dane osobowe"))
}
NavigationLink(destination: ResidenceInfoView()) {
Label("Dane adresowe", systemImage: "house")
.accessibility(label: Text("Dane adresowe"))
}
NavigationLink(destination: ContactView()) {
Label("Kontakt", systemImage: "phone")
.accessibility(label: Text("Kontakt"))
}
NavigationLink(destination: FamilyView()) {
Label("Rodzina", systemImage: "person.2")
.accessibility(label: Text("Rodzina"))
}
}
}
Spacer()
Button("Done") { done() }
.font(.headline)
.multilineTextAlignment(.center)
.padding()
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(12)
}.padding()
.onAppear {
getAccount()
getUsername()
self.hebe = getHebe(id: self.id, account: self.account)
self.displayStudents = getStudents()
self.studentSchool = getSchoolName()
self.selectedStudent = getActualStudentName()
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
struct AccountCardView_Previews: PreviewProvider {
static var previews: some View {
Group {
AccountCardView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,198 @@
//
// accountManager.swift
// wulkanowy
//
// Created by Tomasz on 04/03/2021.
//
import SwiftUI
import KeychainAccess
import SwiftyJSON
import Sdk
struct AccountManagerView: View {
@State private var showLoginModal = false
@State private var showEditAccountModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
@State private var accounts: [String] = [""]
@State private var actualId: String = "0"
@State private var showingDeleteAlert = false
@State private var deletingAccount = "0"
private func getStudentsNames() -> [String] {
//getting all accounts
let keychain = Keychain()
let allAccounts: String! = keychain["allAccounts"] ?? "[]"
//parsing allAccounts to array
var allAccountsArray: [String] = []
print(allAccountsArray)
if(allAccounts != "[]"){
let data = Data(allAccounts.utf8)
do {
let ids = try JSONSerialization.jsonObject(with: data) as! [Int]
for id in ids {
allAccountsArray.append("\(id)")
}
} catch {
print(error)
}
}
return allAccountsArray
}
private func addAccount() {
self.showLoginModal = true
}
private func openEditAccount(id: String) {
let keychain = Keychain()
keychain["editAccountId"] = id
self.showEditAccountModal = true
}
private func getJsonFromString(body: String) -> JSON {
let data = Data(body.utf8)
let json = try! JSON(data: data)
return json
}
private func setActualAccount(id: String) {
let keychain = Keychain()
let accountData = keychain["\(id)"] ?? "{}"
let data: Data = Data(accountData.utf8)
let accountJSON = try! JSON(data: data)
let RestURL = "\(accountJSON["account"]["RestURL"])api/mobile/register/hebe"
keychain["actualAccountId"] = "\(id)"
self.actualId = "\(id)"
keychain["actualAccountEmail"] = "\(accountJSON["account"]["UserName"])"
let apiResponseRequest = apiRequest(endpointURL: "\(RestURL)", id: id)
let session = URLSession.shared
session.dataTask(with: apiResponseRequest) { (data, response, error) in
if let error = error {
// Handle HTTP request error
print(error)
} else if let data = data {
// Handle HTTP request response
let responseBody = String(data: data, encoding: String.Encoding.utf8)
keychain["actualStudentHebe"] = "\(responseBody!)"
} else {
// Handle unexpected error
}
}.resume()
accounts = getStudentsNames()
}
private func deleteAccount() {
let keychain = Keychain()
var ids = getStudentsNames()
let id: Set<String> = [deletingAccount]
ids.removeAll(where: { id.contains($0) })
if(ids == []) {
isLogged = false
}
keychain["allAccounts"] = "\(ids)"
keychain["\(deletingAccount)"] = nil
}
private func getUsername(student: JSON) -> String {
if("\(student["customUsername"])".isEmpty) {
return "\(student["account"]["UserName"])"
} else {
return "\(student["customUsername"])"
}
}
var body: some View {
if(isLogged == true) {
VStack {
Form {
Section(header: Text("chooseAccount")
.font(.title)) {
ForEach(accounts, id: \.self) { student in
let keychain = Keychain()
HStack {
let studentString = "\(keychain[student] ?? "{}")"
let data: Data = Data(studentString.utf8)
let studentJSON = try! JSON(data: data)
let studentEmail = getUsername(student: studentJSON)
Button("\(studentEmail)") { setActualAccount(id: student) }
.foregroundColor(Color("customControlColor"))
.aspectRatio(contentMode: .fit)
if("\(actualId)" == "\(student)") {
Image(systemName: "checkmark.circle")
.foregroundColor(.green)
}
Spacer()
let cardImage = Image(uiImage: UIImage(systemName: "ellipsis")!)
.renderingMode(.template)
Button("\(cardImage)") { openEditAccount(id: student) }
.foregroundColor(Color("customControlColor"))
.padding(.horizontal)
.sheet(isPresented: $showEditAccountModal, onDismiss: {
}) {
AccountCardView()
}
let trashImage = Image(uiImage: UIImage(systemName: "trash")!)
.renderingMode(.template)
Button("\(trashImage)") {
showingDeleteAlert = true
deletingAccount = student
}
}.buttonStyle(BorderlessButtonStyle())
}
}
}
}.onAppear {
let keychain = Keychain()
self.accounts = getStudentsNames()
self.actualId = "\(keychain["actualAccountId"] ?? "0")"
}
.alert(isPresented: $showingDeleteAlert) {
Alert(title: Text("Do you want to delete this account?"),
message: Text("You cannot undo this action"),
primaryButton: .destructive(Text("Delete"), action: deleteAccount),
secondaryButton: .cancel(Text("Cancel")))
}
} else {
Spacer()
Text("No accounts added")
}
Spacer()
Button("addAccount") {addAccount()}
.font(.headline)
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(12)
.buttonStyle(BorderlessButtonStyle())
.padding()
.sheet(isPresented: $showLoginModal, onDismiss: {
}) {
LoginView()
}
}
}
struct AccountManagerView_Previews: PreviewProvider {
static var previews: some View {
Group {
AccountManagerView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,28 @@
//
// contact.swift
// wulkanowy
//
// Created by Tomasz on 11/03/2021.
//
import SwiftUI
import KeychainAccess
import Sdk
struct ContactView: View {
var body: some View {
Text("Here is contact view (in my imagination)")
}
}
struct ContactView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContactView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,29 @@
//
// family.swift
// wulkanowy
//
// Created by Tomasz on 11/03/2021.
//
import SwiftUI
import KeychainAccess
import Sdk
struct FamilyView: View {
var body: some View {
Text("Here is family info (in my imagination)")
}
}
struct FamilyView_Previews: PreviewProvider {
static var previews: some View {
Group {
FamilyView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,29 @@
//
// personalInfo.swift
// wulkanowy
//
// Created by Tomasz on 11/03/2021.
//
import SwiftUI
import KeychainAccess
import Sdk
import SwiftyJSON
struct PersonalInfoView: View {
var body: some View {
Text("Here is personal info (in my imagination)")
}
}
struct PersonalInfoView_Previews: PreviewProvider {
static var previews: some View {
Group {
PersonalInfoView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,28 @@
//
// residenceInfo.swift
// wulkanowy
//
// Created by Tomasz on 11/03/2021.
//
import SwiftUI
import KeychainAccess
import Sdk
struct ResidenceInfoView: View {
var body: some View {
Text("Here is residence details (in my imagination)")
}
}
struct ResidenceInfoView_Previews: PreviewProvider {
static var previews: some View {
Group {
ResidenceInfoView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,47 @@
//
// attendance.swift
// wulkanowy
//
// Created by Tomasz on 04/03/2021.
//
import SwiftUI
struct AttendanceView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
var body: some View {
NavigationView {
if(isLogged == false){
VStack {
Text("You are not logged in")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
Text("Here is attendance (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}
}
struct AttendanceView_Previews: PreviewProvider {
static var previews: some View {
Group {
AttendanceView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,47 @@
//
// attendance.swift
// wulkanowy
//
// Created by Tomasz on 24/02/2021.
//
import SwiftUI
import KeychainAccess
import Sdk
struct CalendarView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
var body: some View {
if(isLogged == false){
VStack {
Text("You are not logged in")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
Text("Here is calendar (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}
struct CalendarView_Previews: PreviewProvider {
static var previews: some View {
Group {
CalendarView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -10,23 +10,36 @@ import KeychainAccess
import Sdk
struct DashboardView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
init() {
let keychain = Keychain()
let key = keychain["privateKey"]
let luckyNumber = getLuckyNumber()
print(luckyNumber)
if(isLogged == true){
let luckyNumber = getLuckyNumber()
print(luckyNumber)
}
}
var body: some View {
NavigationView {
if(isLogged == false){
VStack {
Text("You are not logged in (dashboard)")
NavigationLink(destination: LoginView()) {
Text("Log in")
}
Text("You are not logged in")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
Text("Here is dashboard (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}

View file

@ -1,32 +0,0 @@
//
// attendance.swift
// wulkanowy
//
// Created by Tomasz on 24/02/2021.
//
import SwiftUI
struct ExamsView: View {
var body: some View {
NavigationView {
VStack {
Text("You are not logged in (exams)")
NavigationLink(destination: LoginView()) {
Text("Log in")
}
}.padding()
}
}
}
struct ExamsView_Previews: PreviewProvider {
static var previews: some View {
Group {
ExamsView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -6,17 +6,32 @@
//
import SwiftUI
import KeychainAccess
import Sdk
struct GradesView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
var body: some View {
NavigationView {
VStack {
Text("You are not logged in (grades)")
NavigationLink(destination: LoginView()) {
Text("Log in")
}
}.padding()
}
if(isLogged == false){
VStack {
Text("You are not logged in")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
Text("Here is grades (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}

View file

@ -1,32 +0,0 @@
//
// homework.swift
// wulkanowy
//
// Created by Tomasz on 24/02/2021.
//
import SwiftUI
struct HomeworksView: View {
var body: some View {
NavigationView {
VStack {
Text("You are not logged in (homeworks)")
NavigationLink(destination: LoginView()) {
Text("Log in")
}
}.padding()
}
}
}
struct HomeworksView_Previews: PreviewProvider {
static var previews: some View {
Group {
HomeworksView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -9,12 +9,39 @@ import SwiftUI
struct LicensesView: View {
let KeychainAccessLicense: String = "The MIT License (MIT)\nCopyright (c) 2014 kishikawa katsumi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
let SwiftyJSONLicense: String = """
The MIT License (MIT)
Copyright (c) 2017 Ruoyu Fu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
let KeychainAccessURL: URL? = URL(string: "https://github.com/kishikawakatsumi/KeychainAccess")!
let OpenSSLURL: URL? = URL(string: "https://github.com/krzyzanowskim/OpenSSL")!
let SwiftUIEKtensionsURL: URL? = URL(string: "https://github.com/EnesKaraosman/SwiftUIEKtensions")!
let SwiftyJSONURL: URL? = URL(string: "https://github.com/SwiftyJSON/SwiftyJSON")!
let OpenSSLLicense: String = """
LICENSE ISSUES
==============
@ -177,6 +204,17 @@ struct LicensesView: View {
}
}
.padding(.vertical)
// SwiftyJSON
DisclosureGroup("SwiftyJSON") {
Text(SwiftyJSONLicense)
.font(.system(.body, design: .monospaced))
.onTapGesture {
guard let url = SwiftyJSONURL else { return }
UIApplication.shared.open(url)
}
}
.padding(.vertical)
}
.listStyle(InsetGroupedListStyle())
.navigationTitle(Text("Libraries"))

View file

@ -8,15 +8,28 @@
import SwiftUI
struct MessagesView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
var body: some View {
NavigationView {
VStack {
Text("You are not logged in (messages)")
NavigationLink(destination: LoginView()) {
Text("Log in")
}
}.padding()
}
if(isLogged == false){
VStack {
Text("You are not logged in")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
Text("Here is messages (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}

View file

@ -4,7 +4,6 @@
//
// Created by Tomasz on 24/02/2021.
//
import SwiftUI
struct MoreView: View {
@ -12,9 +11,9 @@ struct MoreView: View {
NavigationView {
Form {
Section {
NavigationLink(destination: MessagesView()) {
Label("messagesButton", systemImage: "envelope")
.accessibility(label: Text("messagesButton"))
NavigationLink(destination: AttendanceView()) {
Label("attendanceButton", systemImage: "chart.bar.doc.horizontal")
.accessibility(label: Text("attendanceButton"))
}
NavigationLink(destination: NotesView()) {
Label("notesButton", systemImage: "graduationcap")

View file

@ -8,15 +8,30 @@
import SwiftUI
struct NotesView: View {
@State private var showModal = false
@AppStorage("isLogged") private var isLogged: Bool = false
var body: some View {
NavigationView {
VStack {
Text("You are not logged in (notes)")
NavigationLink(destination: LoginView()) {
Text("Log in")
if(isLogged == false){
VStack {
Text("You are not logged in (notes)")
Button("Log in") {self.showModal = true}
.sheet(isPresented: $showModal, onDismiss: {
print(self.showModal)
}) {
LoginView()
}
}.padding()
} else {
ScrollView {
PullToRefresh(coordinateSpaceName: "pullToRefresh") {
print("Refreshing..")
}
}.padding()
}
Text("Here is notes (in my imagination)")
}.coordinateSpace(name: "pullToRefresh")
}
}
}
}

View file

@ -5,17 +5,24 @@
// Created by Mikołaj on 25/10/2020.
//
import SwiftUI
import KeychainAccess
import SwiftyJSON
enum AvailableEndpoints: String, CaseIterable {
case vulcan = "Vulcan"
case fakelog = "Fakelog"
}
open class Navigation: ObservableObject {
let window: UIWindow
public init(window: UIWindow) {
self.window = window
}
}
struct LoginView: View {
@StateObject var vulcan: VulcanStore = VulcanStore.shared
@State private var token: String = ""
@ -27,10 +34,12 @@ struct LoginView: View {
@State private var buttonValue = String(format: NSLocalizedString("loginButton", comment: "loginButton"))
@State private var loginStatus: String = ""
@State private var willMoveToNextScreen = false
@State private var success = false
@State private var showingAlert = false
let cellHeight: CGFloat = 55
let cornerRadius: CGFloat = 12
let cellBackground: Color = Color(UIColor.systemGray6).opacity(0.5)
let cellBackground: Color = Color(UIColor.systemGray5).opacity(0.5)
let nullColor: Color = Color.accentColor.opacity(0.4)
@ -49,16 +58,19 @@ struct LoginView: View {
case "wrongPin":
buttonValue = String(format: NSLocalizedString("\(error)", comment: "loginButton"))
case "deviceExist":
showingAlert.toggle()
success = false
default:
buttonValue = String(format: NSLocalizedString("invalidData", comment: "loginButton"))
}
} else {
print("success")
success = true
}
}
}
}
@ -102,85 +114,95 @@ struct LoginView: View {
}
var body: some View {
VStack {
Image("wulkanowy")
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 92)
.foregroundColor(.accentColor)
.padding(.bottom)
Text("loginTitle")
.font(.largeTitle)
.fontWeight(.semibold)
Spacer()
TextField("token", text: $token)
.autocapitalization(.none)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "token"), lineWidth: 2)
)
TextField("symbol", text: $symbol)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "symbol"), lineWidth: 2)
)
TextField("pin", text: $pin)
.keyboardType(.numberPad)
.autocapitalization(.none)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "pin"), lineWidth: 2)
)
TextField("deviceName", text: $deviceModel)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "deviceName"), lineWidth: 2)
)
Spacer()
Button(buttonValue) {login()}
.font(.headline)
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(cornerRadius)
if(success == true) {
ChooseStudentView()
}
else
{
VStack {
Spacer()
Image("wulkanowy")
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 92)
.foregroundColor(.accentColor)
.padding(.bottom)
Text("loginTitle")
.font(.largeTitle)
.fontWeight(.semibold)
Spacer()
TextField("token", text: $token)
.autocapitalization(.none)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "token"), lineWidth: 2)
)
TextField("symbol", text: $symbol)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "symbol"), lineWidth: 2)
)
TextField("pin", text: $pin)
.keyboardType(.numberPad)
.autocapitalization(.none)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "pin"), lineWidth: 2)
)
TextField("deviceName", text: $deviceModel)
.autocapitalization(.none)
.disableAutocorrection(true)
.font(Font.body.weight(Font.Weight.medium))
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.background(cellBackground)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(setColor(input: "deviceName"), lineWidth: 2)
)
Spacer()
Button(buttonValue) {login()}
.font(.headline)
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: cellHeight)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(cornerRadius)
}.padding()
.alert(isPresented: $showingAlert) {
Alert(title: Text("accountRegistered"), message: Text("accountRegisteredContent"), dismissButton: .default(Text("OK")))}
Spacer()
}
.padding()
Spacer()
}
}

View file

@ -0,0 +1,108 @@
//
// chooseStudent.swift
// wulkanowy
//
// Created by Tomasz on 02/03/2021.
//
import Foundation
import SwiftUI
import KeychainAccess
import SwiftyJSON
struct ChooseStudentView: View {
@Environment(\.presentationMode) var presentation
@AppStorage("isLogged") private var isLogged: Bool = false
let keychain = Keychain()
var displayStudents: Array<String> = Array()
@State private var selectedStudent: String = ""
init() {
var responseBody = keychain["actualStudentHebe"]
while responseBody == nil {
responseBody = keychain["actualStudentHebe"]
}
let data = Data(responseBody!.utf8)
let json = try! JSON(data: data)
selectedStudent = "\(json["Envelope"][0]["Login"]["DisplayName"])"
var i: Int = 0
while true {
if (String(describing: json["Envelope"][i]) == "null") {
break
}
else {
displayStudents.append(String(describing: json["Envelope"][i]["Login"]["DisplayName"]))
i += 1
}
}
}
private func saveStudent() {
let responseBody = keychain["actualStudentHebe"]
let data = Data(responseBody!.utf8)
let json = try! JSON(data: data)
var i: Int = 0
if(selectedStudent == "") {
selectedStudent = "\(json["Envelope"][i]["Login"]["DisplayName"])"
}
while true {
let student = "\(json["Envelope"][i]["Login"]["DisplayName"])"
if(student == selectedStudent) {
//saving student
let id = "\(keychain["actualStudentId"] ?? "0")"
let account = keychain[id]
let data: Data = Data(account!.utf8)
var accountJSON = try! JSON(data: data)
accountJSON["actualStudent"] = JSON(i)
print(accountJSON)
keychain[id] = "\(accountJSON)"
break
}
i += 1
}
isLogged = true
presentation.wrappedValue.dismiss()
}
var body: some View {
VStack {
Spacer()
Text("selectStudent")
.font(.title)
.padding(.top)
Picker(selection: $selectedStudent, label: Text("selectStudent")) {
ForEach(displayStudents, id: \.self) { student in
Text(student)
}
}
Spacer()
Button("registerButton") {saveStudent()}
.font(.headline)
.multilineTextAlignment(.center)
.padding(.horizontal)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(12)
}.padding()
}
}
struct ChooseStudentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ChooseStudentView()
}
.preferredColorScheme(.dark)
}
}

View file

@ -5,33 +5,59 @@
// Created by Tomasz on 23/02/2021.
//
//
// navigation.swift
// wulkanowy
//
// Created by Tomasz on 23/02/2021.
//
import SwiftUI
struct NavigationBarView: View {
var body: some View {
TabView() {
DashboardView()
.tabItem {
NavigationView {
DashboardView()
.navigationBarItems(trailing: NavigationLink(destination: AccountManagerView()) {
Image(systemName: "person.circle")
})
}
.tabItem {
Label("dashboardButton", systemImage: "rectangle.on.rectangle")
.accessibility(label: Text("dashboardButton"))
}
GradesView()
.tabItem {
NavigationView {
GradesView()
.navigationBarItems(trailing: NavigationLink(destination: AccountManagerView()) {
Image(systemName: "person.circle")
})
}
.tabItem {
Label("gradesButton", systemImage: "rosette")
.accessibility(label: Text("gradesButton"))
}
ExamsView()
.tabItem {
Label("examsButton", systemImage: "calendar")
.accessibility(label: Text("examsButton"))
NavigationView {
CalendarView()
.navigationBarItems(trailing: NavigationLink(destination: AccountManagerView()) {
Image(systemName: "person.circle")
})
}
.tabItem {
Label("calendarButton", systemImage: "calendar")
.accessibility(label: Text("calendarButton"))
}
HomeworksView()
.tabItem {
Label("homeworkButton", systemImage: "note.text")
.accessibility(label: Text("homeworkButton"))
NavigationView {
MessagesView()
.navigationBarItems(trailing: NavigationLink(destination: AccountManagerView()) {
Image(systemName: "person.circle")
})
}
.tabItem {
Label("messagesButton", systemImage: "envelope")
.accessibility(label: Text("messagesButton"))
}
MoreView()
@ -52,4 +78,3 @@ struct NavigationBarView_Previews: PreviewProvider {
.preferredColorScheme(.dark)
}
}

View file

@ -0,0 +1,93 @@
//
// OnboardingView.swift
// wulkanowy
//
// Created by Karol Zientek on 12/02/2021.
//
import SwiftUI
fileprivate struct InformationDetailView: View {
var title: LocalizedStringKey = ""
var subtitle: LocalizedStringKey = ""
var imageName: String = ""
var body: some View {
HStack(alignment: .center) {
Image(systemName: imageName)
.font(.system(size: 50))
.font(.largeTitle)
.frame(width: 50)
.foregroundColor(.accentColor)
.padding()
.accessibility(hidden: true)
VStack(alignment: .leading) {
Text(title)
.font(.headline)
.foregroundColor(.primary)
.accessibility(addTraits: .isHeader)
.lineLimit(2)
Text(subtitle)
.font(.body)
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
.padding(.top)
}
}
struct OnboardingView: View {
@AppStorage("needsAppOnboarding") private var needsAppOnboarding: Bool = true
var body: some View {
VStack() {
Spacer()
Image("wulkanowy")
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 92)
.foregroundColor(.accentColor)
.padding(.bottom)
Text("onboardingTitle")
.font(.title)
.fontWeight(.semibold)
.padding()
.multilineTextAlignment(.center)
.frame(height: 100)
Spacer()
HStack {
Spacer()
VStack(alignment: .leading) {
InformationDetailView(title: "notificationsOnboarding", subtitle: "notificationsOnboardingContent", imageName: "bell")
InformationDetailView(title: "messagesOnboarding", subtitle: "messagesOnboardingContent", imageName: "envelope")
InformationDetailView(title: "fast", subtitle: "fastContent", imageName: "hare")
}.multilineTextAlignment(.leading)
Spacer()
}
Spacer()
Spacer()
Button("continueButton") { needsAppOnboarding = false }
.font(.headline)
.multilineTextAlignment(.center)
.padding()
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.accentColor.opacity(0.1))
.cornerRadius(12)
}.padding()
}
}
struct WulkanowyCardView_Previews: PreviewProvider {
static var previews: some View {
Group {
OnboardingView().previewLayout(.fixed(width: 320, height: 640))
}
}
}

View file

@ -0,0 +1,43 @@
//
// PullToRefresh.swift
// wulkanowy
//
// Created by Tomasz on 25/03/2021.
//
import Foundation
import SwiftUI
struct PullToRefresh: View {
var coordinateSpaceName: String
var onRefresh: ()->Void
@State var needRefresh: Bool = false
var body: some View {
GeometryReader { geo in
if (geo.frame(in: .named(coordinateSpaceName)).midY > 50) {
Spacer()
.onAppear {
needRefresh = true
}
} else if (geo.frame(in: .named(coordinateSpaceName)).maxY < 10) {
Spacer()
.onAppear {
if needRefresh {
needRefresh = false
onRefresh()
}
}
}
HStack {
Spacer()
if needRefresh {
ProgressView()
}
Spacer()
}
}.padding(.top, -50)
}
}