-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
472e9c3
commit f4cad1e
Showing
59 changed files
with
7,902 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// | ||
// StateManager.swift | ||
// mocacong | ||
// | ||
// Created by Suji Lee on 2023/07/01. | ||
// | ||
|
||
import SwiftUI | ||
import Combine | ||
|
||
class StateManager: ObservableObject { | ||
|
||
static let shared = StateManager() | ||
|
||
@Published private var _isLoggedIn: Bool = false | ||
@Published private var _isAgreed: Bool = false | ||
@Published private var _tokenExpired: Bool = false | ||
@Published private var _serverDown: Bool = false | ||
@Published private var _userReportCount: Int = 0 | ||
|
||
var isLoggedIn: Bool { | ||
get { | ||
return self._isLoggedIn | ||
} | ||
set { | ||
self._isLoggedIn = newValue | ||
} | ||
} | ||
var isAgreed: Bool { | ||
get { | ||
return self._isAgreed | ||
} | ||
set { | ||
self._isAgreed = newValue | ||
} | ||
} | ||
var tokenExpired: Bool { | ||
get { | ||
return self._tokenExpired | ||
} | ||
set { | ||
self._tokenExpired = newValue | ||
} | ||
} | ||
var serverDown: Bool { | ||
get { | ||
return self._serverDown | ||
} | ||
set { | ||
self._serverDown = newValue | ||
} | ||
} | ||
var userReportCount: Int { | ||
get { | ||
return self._userReportCount | ||
} | ||
set { | ||
self._userReportCount = newValue | ||
} | ||
} | ||
|
||
init() {} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// | ||
// TokenManager.swift | ||
// mocacong | ||
// | ||
// Created by Suji Lee on 2023/06/28. | ||
// | ||
|
||
import Foundation | ||
import Combine | ||
import CryptoKit | ||
import SwiftKeychainWrapper | ||
|
||
class TokenManager: ObservableObject { | ||
|
||
var cancellables = Set<AnyCancellable>() | ||
|
||
static let shared = TokenManager() | ||
private let keychain = KeychainWrapper.standard | ||
private let key = SymmetricKey(size: .bits256) | ||
|
||
private init() {} | ||
|
||
func isAccessTokenPresent() -> Bool { | ||
if let _ = keychain.string(forKey: "access_token") { | ||
// 데이터가 있을 경우 | ||
return true | ||
} else { | ||
// 데이터가 nil일 경우 | ||
return false | ||
} | ||
} | ||
|
||
func saveToken(token: String) { | ||
keychain.set(token, forKey: "access_token") | ||
} | ||
|
||
func getToken() -> String? { | ||
return keychain.string(forKey: "access_token") | ||
} | ||
|
||
func saveRefreshToken(token: String) { | ||
keychain.set(token, forKey: "refresh_token") | ||
} | ||
|
||
func getRefreshToken() -> String? { | ||
return keychain.string(forKey: "refresh_token") | ||
|
||
} | ||
|
||
func logoutUser() { | ||
let accessRemoved = KeychainWrapper.standard.removeObject(forKey: "access_token") | ||
let refreshRemoved = KeychainWrapper.standard.removeObject(forKey: "refresh_token") | ||
if accessRemoved && refreshRemoved { | ||
print("All tokens removed successfully.") | ||
} else { | ||
print("Failed to remove some tokens.") | ||
} | ||
StateManager.shared.isLoggedIn = false | ||
} | ||
|
||
func deleteUserAccount() { | ||
logoutUser() | ||
} | ||
|
||
func refreshAccessToken(accessInfo: AccessInfo) { | ||
guard let url = URL(string: "\(requestURL)/login/reissue") else { | ||
fatalError("Invalid Url") | ||
} | ||
|
||
var request = URLRequest(url: url) | ||
request.httpMethod = "POST" | ||
do { | ||
let jsonData = try JSONEncoder().encode(accessInfo) | ||
request.httpBody = jsonData | ||
request.addValue("application/json", forHTTPHeaderField: "Content-Type") | ||
} catch { | ||
|
||
} | ||
|
||
URLSession.shared.dataTaskPublisher(for: request) | ||
.receive(on: DispatchQueue.main) | ||
.tryMap { data, response -> Data in | ||
guard let httpResponse = response as? HTTPURLResponse else { | ||
throw URLError(.badServerResponse) | ||
} | ||
switch httpResponse.statusCode { | ||
case 200: | ||
print("토큰 리프레시 통신 200") | ||
case 401: | ||
StateManager.shared.tokenExpired = true | ||
print("토큰 리프레시 통신 401") | ||
case 500 : | ||
print("토큰 리프레시 서버 에러 500") | ||
StateManager.shared.serverDown = true | ||
default: | ||
print("토큰 리프레시 상태코드: \(httpResponse.statusCode)") | ||
} | ||
return data | ||
} | ||
.decode(type: AccessInfo.self, decoder: JSONDecoder()) | ||
.sink { completion in | ||
switch completion { | ||
case .failure(let error): | ||
if let urlError = error as? URLError { | ||
switch urlError.code { | ||
case .notConnectedToInternet, .networkConnectionLost, .cannotConnectToHost: | ||
StateManager.shared.serverDown = true // Handle connection-related errors | ||
default: | ||
break // Handle other errors if needed | ||
} | ||
} | ||
print("Error: \(error)") | ||
case .finished: | ||
break | ||
} | ||
} receiveValue: { data in | ||
print("토큰 리프레시 반환 데이터 : ", data) | ||
if let userReportCount = data.userReportCount { | ||
StateManager.shared.userReportCount = userReportCount | ||
} | ||
if let accessToken = data.accessToken { | ||
TokenManager.shared.saveToken(token: accessToken) | ||
} | ||
} | ||
.store(in: &self.cancellables) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// | ||
// Filter.swift | ||
// mocacong | ||
// | ||
// Created by Suji Lee on 2023/06/19. | ||
// | ||
|
||
import Foundation | ||
|
||
struct Filter: Codable, Hashable { | ||
var mapIds: [String]? | ||
var userReportCount: Int? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// | ||
// Place.swift | ||
// mocacong | ||
// | ||
// Created by Suji Lee on 2023/04/11. | ||
// | ||
|
||
import Foundation | ||
import MapKit | ||
|
||
final class PlaceAnnotation: NSObject, MKAnnotation { | ||
let title: String? | ||
let coordinate: CLLocationCoordinate2D | ||
|
||
init(place: Place) { | ||
self.title = place.placeName | ||
self.coordinate = CLLocationCoordinate2D(latitude: Double(place.y)!, longitude: Double(place.x)!) | ||
} | ||
} | ||
|
||
struct KakaoResponse: Codable, Hashable { | ||
var meta: Meta? | ||
var documents: Array<Place>? | ||
} | ||
|
||
struct CafePlace: Codable, Hashable, Identifiable { | ||
var id: String | ||
var addressName: String | ||
var phone: String | ||
var placeName: String | ||
var roadAddressName: String | ||
var x: String | ||
var y: String | ||
var solo: Bool = false | ||
var group: Bool = false | ||
var favorite: Bool = false | ||
} | ||
|
||
struct Place: Codable, Hashable, Identifiable { | ||
var addressName: String = "" | ||
var categoryGroupCode: String = "" | ||
var categoryGroup_name: String = "" | ||
var category_name: String = "" | ||
var distance: String = "" | ||
var id: String = "" | ||
var phone: String = "" | ||
var placeName: String = "" | ||
var placeUrl: String = "" | ||
var roadAddressName: String = "" | ||
var x: String = "" | ||
var y: String = "" | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case addressName = "address_name" | ||
case categoryGroupCode = "category_group_code" | ||
case categoryGroup_name = "category_group_name" | ||
case category_name = "category_name" | ||
case distance = "distance" | ||
case id = "id" | ||
case phone = "phone" | ||
case placeName = "place_name" | ||
case placeUrl = "place_url" | ||
case roadAddressName = "road_address_name" | ||
case x = "x" | ||
case y = "y" | ||
} | ||
} | ||
|
||
struct Meta: Codable, Hashable { | ||
var isEnd: Bool? | ||
var pageableCount: Int? | ||
var totalCount: Int? | ||
var sameName: SameName? | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case isEnd = "is_end" | ||
case pageableCount = "pageable_count" | ||
case totalCount = "total_count" | ||
case sameName = "same_name" | ||
} | ||
|
||
struct SameName: Codable, Hashable { | ||
var region: [String]? | ||
var keyword: String? | ||
var selectedRegion: String? | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case region = "region" | ||
case keyword = "keyword" | ||
case selectedRegion = "selected_region" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// | ||
// CustomAnnotation.swift | ||
// mocacong | ||
// | ||
// Created by Suji Lee on 2023/05/14. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct CafeAnnotation: View { | ||
|
||
@ObservedObject var memberVM: MemberViewModel | ||
@ObservedObject var cafeVM: CafeViewModel | ||
@ObservedObject var mapVM: MapViewModel | ||
@StateObject var previewVM: PreviewViewModel = PreviewViewModel() | ||
@ObservedObject var myVM: MyViewModel | ||
@Binding var currentMode: CurrentMode | ||
@State var targetPlace: CafePlace | ||
@State var cafeToPost: Cafe = Cafe() | ||
@State var showModal: Bool = false | ||
@Binding var profileImageData: Data? | ||
|
||
@State var showCafePage: Bool = false | ||
|
||
var body: some View { | ||
VStack(spacing: 0) { | ||
NavigationLink(destination: CafePageView(memberVM: memberVM, cafeVM: cafeVM, myVM: myVM), isActive: $showCafePage) | ||
{ | ||
Button(action: { | ||
cafeToPost.name = targetPlace.placeName | ||
cafeToPost.mapId = targetPlace.id | ||
cafeToPost.phoneNumber = targetPlace.phone | ||
cafeToPost.roadAddress = targetPlace.roadAddressName | ||
|
||
cafeVM.cafeData = cafeToPost | ||
cafeVM.cafeMapId = targetPlace.id | ||
|
||
postCafe() | ||
|
||
if let token = TokenManager.shared.getToken() { | ||
previewVM.fetchCafePreview(accessToken: token, mapId: targetPlace.id) | ||
} | ||
}, label: { | ||
if targetPlace.favorite { | ||
AnnotationMark(type: "FavoriteAnnotation") | ||
} else if targetPlace.solo || targetPlace.group { | ||
AnnotationMark(type: "MocafeAnnotation") | ||
} else { | ||
if targetPlace.id == "388741564" { | ||
AnnotationMark(type: "MocafeAnnotation") | ||
} else { | ||
AnnotationMark(type: "CafeAnnotation") | ||
} | ||
} | ||
}) | ||
} | ||
Text(targetPlace.placeName) | ||
.font(.system(size: 12, weight: .medium)) | ||
.foregroundColor(.black) | ||
} | ||
.sheet(isPresented: $showModal) { | ||
CafePreviewModal(memberVM: memberVM, cafeVM: cafeVM, previewVM: previewVM, showCafePage: $showCafePage) | ||
.presentationDetents([.large, .fraction(0.385)]) | ||
} | ||
.onAppear { | ||
if currentMode == .search { | ||
print("타겟 장소 : ", targetPlace) | ||
} | ||
} | ||
} | ||
|
||
@ViewBuilder | ||
func AnnotationMark(type: String) -> some View { | ||
Image(type) | ||
.resizable() | ||
.scaledToFit() | ||
.frame(width: type == "MocafeAnnotation" ? 26 : 23, height: type == "MocafeAnnotation" ? 26 : 23) | ||
} | ||
|
||
func postCafe() { | ||
cafeVM.postNewCafe(cafeToPost: cafeToPost) | ||
.sink(receiveCompletion: { completion in | ||
switch completion { | ||
case .failure(let error): | ||
print("카페 등록 비동기 error : \(error)") | ||
case .finished: | ||
print("카페 등록 비동기 성공") | ||
break | ||
} | ||
}, receiveValue: { data in | ||
showModal = true | ||
}) | ||
.store(in: &mapVM.cancellables) | ||
} | ||
} |
Oops, something went wrong.