diff --git a/Podfile b/Podfile
index 63c99b6..d0a506d 100644
--- a/Podfile
+++ b/Podfile
@@ -13,19 +13,16 @@ target 'TiquiTaca_iOS' do
pod 'ExytePopupView', '= 1.1.4'
# Firebase
- pod 'Firebase/Analytics', '= 8.15.0'
- pod 'Firebase/Messaging', '= 8.15.0'
+ pod 'FirebaseAnalytics', '= 9.1.0'
+ pod 'FirebaseCrashlytics', '= 9.1.0'
+ pod 'FirebaseMessaging', '= 9.1.0'
+ pod 'FirebasePerformance', '= 9.2.0'
# DB
pod 'RealmSwift', '= 10.25.1'
- # Security
- pod 'KeychainAccess', '= 4.2.2'
- pod 'CryptoSwift', '= 1.4.3'
-
# Util
pod 'SwiftLint', '= 0.47.0'
- pod 'R.swift', '= 6.1.0'
pod 'LicensePlist', '= 3.22.0'
# Socket
diff --git a/README.md b/README.md
index 3c5a3ed..e9fc2ed 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,12 @@
실시간으로 궁금한 장소에 대해 정보를 공유하는 만남의 광장, 티키타카⚡️
+
+
---
![github_img2](https://user-images.githubusercontent.com/13329304/176240116-90cccb5f-e0b4-4437-94d2-523e6ac2150b.png)
@@ -35,34 +41,41 @@
![github_img7](https://user-images.githubusercontent.com/13329304/176240464-0d1abadc-5563-4358-8244-18bb7277491f.png)
-⚡️ 같이 티키타카할 준비가 됐다면 망설이지 말고 놀러오세요:)⚡️
-
-
+
+ ⚡️ 같이 티키타카할 준비가 됐다면 망설이지 말고 놀러오세요:)⚡️
+
+
---
## **🛠 Tech Stack**
### Project
-- SwiftUI
-- Combine
-- TCA(The Composable Architecture)
+|Based|
+|:---|
+|SwiftUI|
+|Combine|
+|TCA (The Composable Architecture)|
+
+![github_img2](https://user-images.githubusercontent.com/13329304/176240116-90cccb5f-e0b4-4437-94d2-523e6ac2150b.png)
-
### Dependency
|Core|
|:---|
|[ComposableArchitecture](https://github.com/pointfreeco/swift-composable-architecture)|
|[ComposableCoreLocation](https://github.com/pointfreeco/composable-core-location)|
- |[Firebase/Messaging](https://github.com/firebase/firebase-ios-sdk)|
- |[Firebase/Analytics](https://github.com/firebase/firebase-ios-sdk)|
+ |[Firebase](https://github.com/firebase/firebase-ios-sdk)|
|[Socket.IO-Client-Swift](https://github.com/socketio/socket.io-client-swift)|
|Security|
|:---|
|[KeyChainAccess](https://github.com/kishikawakatsumi/KeychainAccess)|
+ |DB|
+ |:---|
+ |[RealmSwift](https://github.com/realm/realm-swift)|
+
|UI|
|:---|
|[Map](https://github.com/pauljohanneskraft/Map)|
diff --git a/TiquiTaca_iOS.xcodeproj/project.pbxproj b/TiquiTaca_iOS.xcodeproj/project.pbxproj
index 1447e31..4ff6a87 100644
--- a/TiquiTaca_iOS.xcodeproj/project.pbxproj
+++ b/TiquiTaca_iOS.xcodeproj/project.pbxproj
@@ -128,7 +128,6 @@
F2F1EDA828258E0E00AC4FDD /* AppService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F1EDA728258E0E00AC4FDD /* AppService.swift */; };
F2F1EDAF2829335A00AC4FDD /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F1EDAE2829335A00AC4FDD /* AppView.swift */; };
F2F1EDB12829336200AC4FDD /* AppCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2F1EDB02829336200AC4FDD /* AppCore.swift */; };
- F2FA365C2808897C00A24789 /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FA365B2808897C00A24789 /* R.generated.swift */; };
F2FDA216280BBDAC00B2ED75 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F2FDA215280BBDAC00B2ED75 /* LaunchScreen.storyboard */; };
F2FDA21A280BBF8300B2ED75 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FDA219280BBF8300B2ED75 /* AppDelegate.swift */; };
F2FDA228280C0EE300B2ED75 /* TermsOfServiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FDA227280C0EE300B2ED75 /* TermsOfServiceView.swift */; };
@@ -341,7 +340,6 @@
F2F1EDAE2829335A00AC4FDD /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; sourceTree = ""; };
F2F1EDB02829336200AC4FDD /* AppCore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCore.swift; sourceTree = ""; };
F2F1FFBE28049D7E00F3A8E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = TiquiTaca_iOS/SupportingFile/Info.plist; sourceTree = SOURCE_ROOT; };
- F2FA365B2808897C00A24789 /* R.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = R.generated.swift; sourceTree = SOURCE_ROOT; };
F2FDA215280BBDAC00B2ED75 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
F2FDA219280BBF8300B2ED75 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
F2FDA227280C0EE300B2ED75 /* TermsOfServiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfServiceView.swift; sourceTree = ""; };
@@ -550,7 +548,6 @@
F2F1FFBE28049D7E00F3A8E4 /* Info.plist */,
F2F1ED6E2817ABE900AC4FDD /* GoogleService-Info.plist */,
F24B1EEE28086CA0003D4E1D /* .swiftlint.yml */,
- F2FA365B2808897C00A24789 /* R.generated.swift */,
);
path = SupportingFile;
sourceTree = "";
@@ -1034,7 +1031,6 @@
buildPhases = (
BD3CF7E3E7E022E0969F7A18 /* [CP] Check Pods Manifest.lock */,
F2F1FFBF2804A2AA00F3A8E4 /* Run Swiftlint */,
- F24B1EF12808728F003D4E1D /* R.swift */,
F2C02C6527F8824A004C4235 /* Sources */,
F2C02C6627F8824A004C4235 /* Frameworks */,
F2C02C6727F8824A004C4235 /* Resources */,
@@ -1196,26 +1192,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TiquiTaca_iOS/Pods-TiquiTaca_iOS-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- F24B1EF12808728F003D4E1D /* R.swift */ = {
- isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- );
- name = R.swift;
- outputFileListPaths = (
- );
- outputPaths = (
- $SRCROOT/R.generated.swift,
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"$PODS_ROOT/R.swift/rswift\" generate \"$SRCROOT/R.generated.swift\"\n";
- };
F2F1FFBF2804A2AA00F3A8E4 /* Run Swiftlint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1335,7 +1311,6 @@
F96CDE772824EF9B002DEB90 /* MyInfoCore.swift in Sources */,
F25C79B8282CB5F2009C60EE /* CheckNicknameEntity.swift in Sources */,
F21276462811C6D500FB568D /* TOSFieldListViewCore.swift in Sources */,
- F2FA365C2808897C00A24789 /* R.generated.swift in Sources */,
F22184FD284C9EED005CE6C3 /* ChatRoomListSortType.swift in Sources */,
8601F65C281309B10085F692 /* SplashView.swift in Sources */,
F25C79BA282D7524009C60EE /* String+Extensions.swift in Sources */,
@@ -1548,7 +1523,7 @@
CODE_SIGN_ENTITLEMENTS = TiquiTaca_iOS/TiquiTaca_iOSDebug.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_ASSET_PATHS = "\"TiquiTaca_iOS/Resource/Preview Content\"";
DEVELOPMENT_TEAM = 75FNRKTJ9S;
ENABLE_PREVIEWS = YES;
@@ -1564,7 +1539,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0;
+ MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.tiquitaca.TiquiTaca-iOS";
PRODUCT_NAME = TiquiTaca;
PROVISIONING_PROFILE_SPECIFIER = dev_tiquiTaca;
@@ -1583,7 +1558,7 @@
CODE_SIGN_ENTITLEMENTS = TiquiTaca_iOS/TiquiTaca_iOS.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution: Minseok Kang (75FNRKTJ9S)";
CODE_SIGN_STYLE = Manual;
- CURRENT_PROJECT_VERSION = 19;
+ CURRENT_PROJECT_VERSION = 24;
DEVELOPMENT_ASSET_PATHS = "\"TiquiTaca_iOS/Resource/Preview Content\"";
DEVELOPMENT_TEAM = 75FNRKTJ9S;
ENABLE_PREVIEWS = YES;
@@ -1599,7 +1574,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.0;
+ MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.tiquitaca.TiquiTaca-iOS";
PRODUCT_NAME = TiquiTaca;
PROVISIONING_PROFILE_SPECIFIER = appStore_tiquiTaca;
diff --git a/TiquiTaca_iOS/AppDelegate.swift b/TiquiTaca_iOS/AppDelegate.swift
index 00d4db7..7c9bda4 100644
--- a/TiquiTaca_iOS/AppDelegate.swift
+++ b/TiquiTaca_iOS/AppDelegate.swift
@@ -7,8 +7,12 @@
//
import UIKit
-import Firebase
import UserNotifications
+import FirebaseCore
+import FirebaseAnalytics
+import FirebaseMessaging
+import FirebasePerformance
+import FirebaseCrashlytics
@main
final class AppDelegate: NSObject, UIApplicationDelegate {
@@ -16,9 +20,8 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
- UIApplication.shared.applicationIconBadgeNumber = 0
-
FirebaseApp.configure()
+ UIApplication.shared.applicationIconBadgeNumber = 0
Messaging.messaging().delegate = self
UNUserNotificationCenter.current().delegate = self
@@ -65,7 +68,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
completionHandler([.sound, .banner, .list])
}
-
+
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
Messaging.messaging().appDidReceiveMessage(userInfo)
diff --git a/TiquiTaca_iOS/SupportingFile/Info.plist b/TiquiTaca_iOS/SupportingFile/Info.plist
index 7ab5da3..22f99e8 100644
--- a/TiquiTaca_iOS/SupportingFile/Info.plist
+++ b/TiquiTaca_iOS/SupportingFile/Info.plist
@@ -47,11 +47,11 @@
NSLocationAlwaysAndWhenInUseUsageDescription
- 회원님의 위치정보가 지도에 표시됩니다. 위치 정보는 근처 채팅방 정보를 불러올때 그리고 채팅시 장소 반경 내부에 있는지 표기할 때 활용됩니다. 다른사람들에게 해당 반경 내부에 있는지에 대한 정보가 노출 될 수 있습니다. 언제든지 해당 위치 정보 공유를 중지할 수 있습니다.
+ 위치 서비스를 켜면 내 주변 채팅가능 장소를 볼 수 있습니다.
NSLocationAlwaysUsageDescription
- 회원님의 위치정보가 지도에 표시됩니다. 위치 정보는 근처 채팅방 정보를 불러올때 그리고 채팅시 장소 반경 내부에 있는지 표기할 때 활용됩니다. 다른사람들에게 해당 반경 내부에 있는지에 대한 정보가 노출 될 수 있습니다. 언제든지 해당 위치 정보 공유를 중지할 수 있습니다.
+ 위치 서비스를 켜면 내 주변 채팅가능 장소를 볼 수 있습니다.
NSLocationWhenInUseUsageDescription
- 회원님의 위치정보가 지도에 표시됩니다. 위치 정보는 근처 채팅방 정보를 불러올때 그리고 채팅시 장소 반경 내부에 있는지 표기할 때 활용됩니다. 다른사람들에게 해당 반경 내부에 있는지에 대한 정보가 노출 될 수 있습니다. 언제든지 해당 위치 정보 공유를 중지할 수 있습니다.
+ 위치 서비스를 켜면 내 주변 채팅가능 장소를 볼 수 있습니다.
UIAppFonts
Pretendard-Bold.otf
diff --git a/TiquiTaca_iOS/Views/Chat/ChatCore.swift b/TiquiTaca_iOS/Views/Chat/ChatCore.swift
index 7b92b3e..e64ce2d 100644
--- a/TiquiTaca_iOS/Views/Chat/ChatCore.swift
+++ b/TiquiTaca_iOS/Views/Chat/ChatCore.swift
@@ -14,19 +14,21 @@ import SwiftUI
struct ChatState: Equatable {
enum Route {
case chatDetail
+ case none
}
- var route: Route?
- var isFirstLoad = true
- var currentTab: RoomListType = .like
+ var route: Route?
- var unReadChatCount: Int = 0
+ var enteredRoom: RoomInfoEntity.Response?
var lastChatLog: ChatLogEntity.Response?
-
var lastLoadTime: String = Date.current(type: .HHmm)
- var enteredRoom: RoomInfoEntity.Response?
+ var unReadChatCount: Int = 0
+
+ var currentTab: RoomListType = .like
var likeRoomList: [RoomInfoEntity.Response] = []
var popularRoomList: [RoomInfoEntity.Response] = []
+
+ var showRoomEnterPopup: Bool = false
var moveToChatDetail: Bool = false
var willEnterRoom: RoomInfoEntity.Response?
@@ -34,30 +36,31 @@ struct ChatState: Equatable {
}
enum ChatAction: Equatable {
-
case onAppear
+ // MARK: Request Action
case fetchEnteredRoomInfo
case fetchLikeRoomList
case fetchPopularRoomList
-
+ // MARK: Socket Action
case socketConnected(String)
case socketDisconnected(String)
case socketResponse(SocketBannerService.Action)
-
+ // MARK: Response Action
case responsePopularRoomList(Result<[RoomInfoEntity.Response]?, HTTPError>)
case responseLikeRoomList(Result<[RoomInfoEntity.Response]?, HTTPError>)
case responseEnteredRoom(Result)
case responseRoomFavorite(Result)
-
+ // MARK: User Action
case tabChange(RoomListType)
case removeFavoriteRoom(RoomInfoEntity.Response)
case willEnterRoom(RoomInfoEntity.Response)
case refresh
-
-
- case chatDetailAction(ChatDetailAction)
+ // MARK: Router Action
case setRoute(ChatState.Route?)
+ case setShowRoomEnterPopup(Bool)
case setMoveToChatDetail(Bool)
+ // MARK: Child Action
+ case chatDetailAction(ChatDetailAction)
}
struct ChatEnvironment {
@@ -96,10 +99,7 @@ let chatCore = Reducer<
> { state, action, environment in
switch action {
case .onAppear:
- guard state.isFirstLoad else { return .none }
-
state.lastLoadTime = Date.current(type: .HHmm)
- state.isFirstLoad = true
return .merge(
Effect(value: .fetchEnteredRoomInfo)
.eraseToEffect(),
@@ -108,7 +108,7 @@ let chatCore = Reducer<
Effect(value: .fetchPopularRoomList)
.eraseToEffect()
)
- // MARK: Requeset
+ // MARK: Requeset
case .fetchEnteredRoomInfo:
return environment.appService.roomService
.getEnteredRoom()
@@ -133,7 +133,7 @@ let chatCore = Reducer<
.receive(on: environment.mainQueue)
.catchToEffect()
.map(ChatAction.responseRoomFavorite)
- // MARK: Response
+ // MARK: Response
case let .responseRoomFavorite(.success(res)):
return Effect(value: .fetchLikeRoomList)
.eraseToEffect()
@@ -188,7 +188,7 @@ let chatCore = Reducer<
.responsePopularRoomList(.failure),
.responseRoomFavorite(.failure):
return .none
- // MARK: View Action
+ // MARK: View Action
case .tabChange(let type):
guard state.currentTab != type else { return .none }
state.currentTab = type
@@ -200,10 +200,16 @@ let chatCore = Reducer<
return .none
case let .setRoute(route):
state.route = route
+ if route == .chatDetail {
+ state.moveToChatDetail = true
+ }
+ return .none
+ case let .setShowRoomEnterPopup(isPresented):
+ state.showRoomEnterPopup = isPresented
return .none
case let .setMoveToChatDetail(isMoveToChatDetail):
+ state.route = isMoveToChatDetail ? .chatDetail : ChatState.Route.none
state.moveToChatDetail = isMoveToChatDetail
- state.route = isMoveToChatDetail ? .chatDetail : nil
return .none
case .refresh:
state.lastLoadTime = Date.current(type: .HHmm)
@@ -215,6 +221,5 @@ let chatCore = Reducer<
)
case .chatDetailAction:
return .none
-
}
}
diff --git a/TiquiTaca_iOS/Views/Chat/ChatView.swift b/TiquiTaca_iOS/Views/Chat/ChatView.swift
index cd52abe..5283c7a 100644
--- a/TiquiTaca_iOS/Views/Chat/ChatView.swift
+++ b/TiquiTaca_iOS/Views/Chat/ChatView.swift
@@ -11,94 +11,97 @@ import ComposableArchitecture
import TTDesignSystemModule
struct ChatView: View {
- @State private var showPopup: Bool = false
var store: Store
- @StateObject private var viewStore: ViewStore
+ @ObservedObject private var viewStore: ViewStore
struct ViewState: Equatable {
- let currentTab: RoomListType
- let lastLoadTime: String
+ let enteredRoom: RoomInfoEntity.Response?
+ let lastChatLog: ChatLogEntity.Response?
let unReadChatCount: Int
- let enteredRoom: RoomInfoEntity.Response?
+ let currentTab: RoomListType
+ let lastLoadTime: String
let likeRoomList: [RoomInfoEntity.Response]
let popularRoomList: [RoomInfoEntity.Response]
+
+ let showRoomEnterPopup: Bool
let moveToChatDetail: Bool
+ let route: ChatState.Route?
init(state: ChatState) {
- currentTab = state.currentTab
- lastLoadTime = state.lastLoadTime
+ enteredRoom = state.enteredRoom
+ lastChatLog = state.lastChatLog
unReadChatCount = state.unReadChatCount
- enteredRoom = state.enteredRoom
+ currentTab = state.currentTab
+ lastLoadTime = state.lastLoadTime
likeRoomList = state.likeRoomList
popularRoomList = state.popularRoomList
+
+ showRoomEnterPopup = state.showRoomEnterPopup
moveToChatDetail = state.moveToChatDetail
+ route = state.route
}
}
init(store: Store) {
self.store = store
- self._viewStore = StateObject(wrappedValue: ViewStore(store.scope(state: ViewState.init)))
+ self.viewStore = ViewStore(store.scope(state: ViewState.init))
}
var body: some View {
VStack(spacing: 0) {
VStack(spacing: .spacingM) {
- Text("채팅방")
- .font(.heading1)
- .foregroundColor(.white)
- .padding(.horizontal, .spacingXL)
- .padding(.top, .spacingXL)
- .hLeading()
-
- EnteredRoomView(
- store: store,
- moveToChatDetail: viewStore.binding(
- get: \.moveToChatDetail,
- send: ChatAction.setMoveToChatDetail
- )
- )
-
- TabKindView(
- currentTab: viewStore.binding(
- get: \.currentTab,
- send: ChatAction.tabChange
- ),
- currentTime: viewStore.lastLoadTime
- )
+ title
+ enteredRoomView
+ tabKindView
}
- .background(Color.black800)
+ .background(Color.black800)
- RoomListView(
- store: store,
- type: viewStore.currentTab,
- showPopup: $showPopup,
- moveToChatDetail: viewStore.binding(
- get: \.moveToChatDetail,
- send: ChatAction.setMoveToChatDetail
- )
- )
- .background(.white)
+ RoomListView(store: store)
+ .background(.white)
NavigationLink(
- destination: ChatDetailView(
- store: store.scope(
- state: \.chatDetailState,
- action: ChatAction.chatDetailAction),
- shouldPopToRootView: viewStore.binding(
- get: \.moveToChatDetail,
- send: ChatAction.setMoveToChatDetail
- )
+ tag: ChatState.Route.chatDetail,
+ selection: viewStore.binding(
+ get: \.route,
+ send: ChatAction.setRoute
),
- isActive: viewStore.binding(
- get: \.moveToChatDetail,
- send: ChatAction.setMoveToChatDetail
- )
- ) { EmptyView() }
+ destination: {
+ ChatDetailView(
+ store: store.scope(
+ state: \.chatDetailState,
+ action: ChatAction.chatDetailAction
+ ),
+ shouldPopToRootView: viewStore.binding (
+ get: \.moveToChatDetail,
+ send: ChatAction.setMoveToChatDetail
+ )
+ )
+ },
+ label: EmptyView.init
+ )
.isDetailLink(false)
.frame(height: 0)
.hidden()
+// NavigationLink(
+// destination: ChatDetailView(
+// store: store.scope(
+// state: \.chatDetailState,
+// action: ChatAction.chatDetailAction),
+// shouldPopToRootView: viewStore.binding(
+// get: \.moveToChatDetail,
+// send: ChatAction.setMoveToChatDetail
+// )
+// ),
+// isActive: viewStore.binding(
+// get: \.moveToChatDetail,
+// send: ChatAction.setMoveToChatDetail
+// )
+// ) { EmptyView() }
+// .isDetailLink(false)
+// .frame(height: 0)
+// .hidden()
}
.listStyle(.plain)
.navigationTitle("채팅방")
@@ -108,34 +111,21 @@ struct ChatView: View {
}
}
-// MARK: Current Chat View
-private struct EnteredRoomView: View {
- typealias State = ChatState
- typealias Action = ChatAction
-
- private let store: Store
- @Binding private var moveToChatDetail: Bool
- @ObservedObject private var viewStore: ViewStore
-
- struct ViewState: Equatable {
- let enteredRoom: RoomInfoEntity.Response?
- let lastChatLog: ChatLogEntity.Response?
- let unReadChatCount: Int
-
- init(state: State) {
- enteredRoom = state.enteredRoom
- lastChatLog = state.lastChatLog
- unReadChatCount = state.unReadChatCount
- }
+// MARK: Title View
+extension ChatView {
+ var title: some View {
+ Text("채팅방")
+ .font(.heading1)
+ .foregroundColor(.white)
+ .padding(.horizontal, .spacingXL)
+ .padding(.top, .spacingXL)
+ .hLeading()
}
-
- init(store: Store, moveToChatDetail: Binding) {
- self.store = store
- viewStore = ViewStore(store.scope(state: ViewState.init))
- self._moveToChatDetail = moveToChatDetail
- }
-
- var body: some View {
+}
+
+// MARK: Entered Chat View
+extension ChatView {
+ var enteredRoomView: some View {
VStack {
VStack {
if viewStore.enteredRoom == nil {
@@ -182,30 +172,30 @@ private struct EnteredRoomView: View {
HStack {
Text(
viewStore.lastChatLog == nil ?
- "아직 아무도 채팅을 치지 않았어요" :
+ "아직 아무도 채팅을 치지 않았어요" :
(viewStore.lastChatLog?.getMessage() ?? "")
)
- .foregroundColor(.white800)
- .font(.body7)
- .lineLimit(1)
- .hLeading()
+ .foregroundColor(.white800)
+ .font(.body7)
+ .lineLimit(1)
+ .hLeading()
VStack {
Text(
viewStore.unReadChatCount >= 100 ?
- "+99" :
+ "+99" :
"\(viewStore.unReadChatCount)"
)
- .foregroundColor(viewStore.unReadChatCount == 0 ? Color.black600 : .black800)
- .font(.body4)
- .padding([.leading, .trailing], 6)
- .padding([.top, .bottom], 2)
+ .foregroundColor(viewStore.unReadChatCount == 0 ? Color.black600 : .black800)
+ .font(.body4)
+ .padding([.leading, .trailing], 6)
+ .padding([.top, .bottom], 2)
}
.background(viewStore.unReadChatCount == 0 ? Color.black600 : Color.green900)
.cornerRadius(11)
}
- .hLeading()
+ .hLeading()
}
- .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16))
+ .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16))
}
}
.background(Color.black600)
@@ -215,52 +205,30 @@ private struct EnteredRoomView: View {
.onTapGesture {
guard let room = viewStore.enteredRoom else { return }
viewStore.send(.willEnterRoom(room))
- moveToChatDetail = true
+ viewStore.send(.setRoute(.chatDetail))
}
}
}
}
-
// MARK: Tab Kind View
-private struct TabKindView: View {
- @Binding var currentTab: RoomListType
- let currentTime: String
-
- private struct TabButton: ButtonStyle {
- @Environment(\.isEnabled) var isEnabled
-
- public init() { }
- public func makeBody(configuration: Configuration) -> some View {
- return configuration.label
- .frame(height: 40)
- .font(.subtitle3)
- .foregroundColor(isEnabled ? .black100 : .green500)
- .padding([.leading, .trailing], 14)
- .overlay(
- Rectangle()
- .frame(height: isEnabled ? 0 : 2)
- .foregroundColor(Color.green500),
- alignment: .bottom)
- }
- }
-
- var body: some View {
+extension ChatView {
+ var tabKindView: some View {
HStack(spacing: 0) {
Spacer().frame(width: 10)
Button(
- action: { currentTab = .like },
+ action: { viewStore.send(.tabChange(.like)) },
label: { Text("즐겨찾기") }
)
.buttonStyle(TabButton())
- .disabled(currentTab == .like)
+ .disabled(viewStore.currentTab == .like)
Button(
- action: { currentTab = .popular },
+ action: { viewStore.send(.tabChange(.popular)) },
label: { Text("인기채팅방") }
)
.buttonStyle(TabButton())
- .disabled(currentTab == .popular)
- Text(currentTime + " 기준")
+ .disabled(viewStore.currentTab == .popular)
+ Text(viewStore.lastLoadTime + " 기준")
.foregroundColor(.white800)
.font(.system(size: 13, weight: .semibold, design: .default))
.padding(.trailing, 24)
@@ -270,60 +238,77 @@ private struct TabKindView: View {
}
}
+private struct TabButton: ButtonStyle {
+ @Environment(\.isEnabled) var isEnabled
+ public init() { }
+ public func makeBody(configuration: Configuration) -> some View {
+ return configuration.label
+ .frame(height: 40)
+ .font(.subtitle3)
+ .foregroundColor(isEnabled ? .black100 : .green500)
+ .padding([.leading, .trailing], 14)
+ .overlay(
+ Rectangle()
+ .frame(height: isEnabled ? 0 : 2)
+ .foregroundColor(Color.green500),
+ alignment: .bottom
+ )
+ }
+}
+
+// MARK: RoomListView
private struct RoomListView: View {
- typealias Action = ChatAction
+ typealias CAction = ChatAction
+ typealias CState = ChatState
- private let store: Store
- let roomType: RoomListType
- @Binding private var showPopup: Bool
- @Binding private var moveToChatDetail: Bool
- @ObservedObject private var viewStore: ViewStore
- @State var selectedRoomUserCount: Int = 0
+ private let maxUserCount = 300
+ private let store: Store
+ @ObservedObject private var viewStore: ViewStore
struct ViewState: Equatable {
+ let roomType: RoomListType
let likeRoomList: [RoomInfoEntity.Response]
let popularRoomList: [RoomInfoEntity.Response]
let enteredRoom: RoomInfoEntity.Response?
- init(state: ChatState) {
+ let willEnterRoom: RoomInfoEntity.Response?
+ let showRoomEnterPopup: Bool
+
+ init(state: CState) {
+ roomType = state.currentTab
likeRoomList = state.likeRoomList
popularRoomList = state.popularRoomList
enteredRoom = state.enteredRoom
+
+ willEnterRoom = state.willEnterRoom
+ showRoomEnterPopup = state.showRoomEnterPopup
}
}
- init(
- store: Store,
- type: RoomListType,
- showPopup: Binding,
- moveToChatDetail: Binding
- ) {
+ init(store: Store) {
self.store = store
self.viewStore = ViewStore(store.scope(state: ViewState.init))
- self.roomType = type
- self._showPopup = showPopup
- self._moveToChatDetail = moveToChatDetail
}
var body: some View {
List {
- if (roomType == .like && viewStore.likeRoomList.isEmpty) ||
- (roomType == .popular && viewStore.popularRoomList.isEmpty) {
- NoDataView(noDataType: roomType)
+ if (viewStore.roomType == .like && viewStore.likeRoomList.isEmpty) ||
+ (viewStore.roomType == .popular && viewStore.popularRoomList.isEmpty) {
+ NoDataView(noDataType: viewStore.roomType)
.padding(.top, .spacingXXXL * 2)
.background(.white)
} else {
ForEach(
- (roomType == .like ?
+ (viewStore.roomType == .like ?
viewStore.likeRoomList :
viewStore.popularRoomList
).enumerated().map({ $0 }), id: \.element.id
) { index, room in
- RoomListCell(ranking: index + 1, info: room, type: roomType)
+ RoomListCell(ranking: index + 1, info: room, type: viewStore.roomType)
.background(.white)
.swipeActions(edge: .trailing, allowsFullSwipe: false, content: {
- if roomType == .like {
+ if viewStore.roomType == .like {
Button(
action: {
ViewStore(store).send(.removeFavoriteRoom(room))
@@ -335,12 +320,11 @@ private struct RoomListView: View {
.onTapGesture(perform: {
if viewStore.enteredRoom == nil || room.id == viewStore.enteredRoom?.id {
viewStore.send(.willEnterRoom(room))
- moveToChatDetail = true
+ viewStore.send(.setRoute(.chatDetail))
} else {
- selectedRoomUserCount = room.userCount ?? 0
UIView.setAnimationsEnabled(false)
viewStore.send(.willEnterRoom(room))
- showPopup = true
+ viewStore.send(.setShowRoomEnterPopup(true))
}
})
.listRowSeparator(.hidden)
@@ -348,13 +332,19 @@ private struct RoomListView: View {
}
}
}
- .fullScreenCover(isPresented: $showPopup) {
- AlertView(
- isPopupPresent: $showPopup,
- moveToChatDetailState: $moveToChatDetail,
- roomUserCount: selectedRoomUserCount
+ .fullScreenCover(
+ isPresented: viewStore.binding(
+ get: \.showRoomEnterPopup,
+ send: ChatAction.setShowRoomEnterPopup
)
- .background(BackgroundTransparentView())
+ ) {
+ if viewStore.willEnterRoom?.userCount ?? 0 >= maxUserCount {
+ AlertView(store: store)
+ .overCapacity
+ } else {
+ AlertView(store: store)
+ .existEnteredRoom
+ }
}
.refreshable {
viewStore.send(.refresh)
@@ -364,21 +354,28 @@ private struct RoomListView: View {
// MARK: Alert
private struct AlertView: View {
- @Binding var isPopupPresent: Bool
- @Binding var moveToChatDetailState: Bool
- let roomUserCount: Int
- var maxUserCount = 300
- // 채팅방 인원 풀 -> 경고만
- // 채팅방 교체할 것인지 -> 경고 후 참가
- var body: some View {
- if roomUserCount < maxUserCount {
- existEnteredRoom
- .padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24))
- } else {
- overCapacity
+ private let store: Store
+ @ObservedObject var viewStore: ViewStore
+
+ struct ViewState: Equatable {
+ let showRoomEnterPopup: Bool
+ let route: ChatState.Route?
+
+ init(state: ChatState) {
+ showRoomEnterPopup = state.showRoomEnterPopup
+ route = state.route
}
}
+ init(store: Store) {
+ self.store = store
+ self.viewStore = ViewStore(store.scope(state: ViewState.init))
+ }
+
+ var body: some View {
+ existEnteredRoom
+ }
+
var existEnteredRoom: some View {
TTPopupView.init(
popUpCase: .oneLineTwoButton,
@@ -387,19 +384,21 @@ private struct AlertView: View {
leftButtonName: "취소",
rightButtonName: "참여하기",
confirm: {
- isPopupPresent = false
+ viewStore.send(.setShowRoomEnterPopup(false))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
- UIView.setAnimationsEnabled(true)
- moveToChatDetailState = true
+ UIView.setAnimationsEnabled(true)
+ viewStore.send(.setRoute(.chatDetail))
}
},
cancel: {
- isPopupPresent = false
+ viewStore.send(.setShowRoomEnterPopup(false))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
UIView.setAnimationsEnabled(true)
}
}
)
+ .padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24))
+ .background(BackgroundTransparentView())
}
var overCapacity: some View {
@@ -408,19 +407,15 @@ private struct AlertView: View {
title: "해당 채팅방은 인원이 가득 찼어요",
subtitle: "최대 인원수 300명이 차서 입장이 불가능해요",
leftButtonName: "닫기",
- confirm: {
- isPopupPresent = false
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
- UIView.setAnimationsEnabled(true)
- }
- },
cancel: {
- isPopupPresent = false
+ viewStore.send(.setShowRoomEnterPopup(false))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
UIView.setAnimationsEnabled(true)
}
}
)
+ .padding(EdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 24))
+ .background(BackgroundTransparentView())
}
}
diff --git a/TiquiTaca_iOS/Views/MainMap/MainMapCore.swift b/TiquiTaca_iOS/Views/MainMap/MainMapCore.swift
index 71b5791..1ba1e86 100644
--- a/TiquiTaca_iOS/Views/MainMap/MainMapCore.swift
+++ b/TiquiTaca_iOS/Views/MainMap/MainMapCore.swift
@@ -142,17 +142,17 @@ private let mainMapCore = Reducer<
case .onLoad:
state.isFirstLoad = true
-
- if environment.locationManager.authorizationStatus() == .notDetermined {
- state.showLocationPopup = true
- return .none
- }
+// if environment.locationManager.authorizationStatus() == .notDetermined {
+// state.showLocationPopup = true
+// return .none
+// }
return .init(value: .currentLocationButtonTapped)
case .currentLocationButtonTapped:
// 첫 위치 권한 설정, onLoad, 현위치 버튼
- if environment.locationManager.authorizationStatus() != .notDetermined && !environment.locationManager.locationServicesEnabled() {
- return .init(value: .showLocationAlert)
- }
+ guard environment.locationManager.locationServicesEnabled() else { return .init(value: .showLocationAlert) }
+// if environment.locationManager.authorizationStatus() != .notDetermined && !environment.locationManager.locationServicesEnabled() {
+// return .init(value: .showLocationAlert)
+// }
switch environment.locationManager.authorizationStatus() {
case .restricted, .denied:
diff --git a/TiquiTaca_iOS/Views/MyPage/MyPageView.swift b/TiquiTaca_iOS/Views/MyPage/MyPageView.swift
index 686c5ff..dbbff14 100644
--- a/TiquiTaca_iOS/Views/MyPage/MyPageView.swift
+++ b/TiquiTaca_iOS/Views/MyPage/MyPageView.swift
@@ -199,7 +199,7 @@ private struct AlertView: View {
.foregroundColor(.white700)
.frame(height: 34)
- Image("LinerRectangle")
+ Image("LinearRectangle")
.overlay {
VStack {
Text("내가 받은 번개 갯수")
diff --git a/TiquiTaca_iOS/Views/OtherProfile/OtherProfileView.swift b/TiquiTaca_iOS/Views/OtherProfile/OtherProfileView.swift
index 3429df8..96cf208 100644
--- a/TiquiTaca_iOS/Views/OtherProfile/OtherProfileView.swift
+++ b/TiquiTaca_iOS/Views/OtherProfile/OtherProfileView.swift
@@ -204,8 +204,8 @@ struct OtherProfileView: View {
}
HStack(alignment: .center, spacing: 4) {
- if viewStore.userInfo?.canUseFeature == true {
- Image("bxLoudspeaker3")
+ if viewStore.userInfo?.canUseFeature == true && viewStore.userInfo?.level != 0 {
+ Image("ratingLv\(viewStore.userInfo?.level ?? 1)")
.resizable()
.frame(width: 32, height: 32)
}