[✨] 채팅(Base) 잡는 중: WebSocket 기반으로 구현 중
|
@ -14,6 +14,7 @@
|
||||||
A78774722CF586AF002FE2EE /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = A78774712CF586AF002FE2EE /* Alamofire */; };
|
A78774722CF586AF002FE2EE /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = A78774712CF586AF002FE2EE /* Alamofire */; };
|
||||||
A7A518CF2CF555E200822D0D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = A7A518CE2CF555E200822D0D /* README.md */; };
|
A7A518CF2CF555E200822D0D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = A7A518CE2CF555E200822D0D /* README.md */; };
|
||||||
A7A518D12CF5588500822D0D /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = A7A518D02CF5588500822D0D /* .gitignore */; };
|
A7A518D12CF5588500822D0D /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = A7A518D02CF5588500822D0D /* .gitignore */; };
|
||||||
|
FB0119D32D62EEF000C1FA82 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = FB0119D22D62EEF000C1FA82 /* Starscream */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
A73892252D526A9D00659A62 /* FirebaseCrashlytics in Frameworks */,
|
A73892252D526A9D00659A62 /* FirebaseCrashlytics in Frameworks */,
|
||||||
A771FFF22CFB70D100367DA6 /* KakaoSDK in Frameworks */,
|
A771FFF22CFB70D100367DA6 /* KakaoSDK in Frameworks */,
|
||||||
A73892212D526A9D00659A62 /* FirebaseAnalytics in Frameworks */,
|
A73892212D526A9D00659A62 /* FirebaseAnalytics in Frameworks */,
|
||||||
|
FB0119D32D62EEF000C1FA82 /* Starscream in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -102,6 +104,7 @@
|
||||||
A73892202D526A9D00659A62 /* FirebaseAnalytics */,
|
A73892202D526A9D00659A62 /* FirebaseAnalytics */,
|
||||||
A73892222D526A9D00659A62 /* FirebaseAppCheck */,
|
A73892222D526A9D00659A62 /* FirebaseAppCheck */,
|
||||||
A73892242D526A9D00659A62 /* FirebaseCrashlytics */,
|
A73892242D526A9D00659A62 /* FirebaseCrashlytics */,
|
||||||
|
FB0119D22D62EEF000C1FA82 /* Starscream */,
|
||||||
);
|
);
|
||||||
productName = AcaMate;
|
productName = AcaMate;
|
||||||
productReference = A7A518BB2CF5558B00822D0D /* AcaMate.app */;
|
productReference = A7A518BB2CF5558B00822D0D /* AcaMate.app */;
|
||||||
|
@ -135,6 +138,7 @@
|
||||||
A78774702CF586AF002FE2EE /* XCRemoteSwiftPackageReference "Alamofire" */,
|
A78774702CF586AF002FE2EE /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||||
A771FFF02CFB70D100367DA6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */,
|
A771FFF02CFB70D100367DA6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */,
|
||||||
A738921F2D526A9D00659A62 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
|
A738921F2D526A9D00659A62 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
|
||||||
|
FB0119D12D62EEF000C1FA82 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = A7A518BC2CF5558B00822D0D /* Products */;
|
productRefGroup = A7A518BC2CF5558B00822D0D /* Products */;
|
||||||
|
@ -420,6 +424,14 @@
|
||||||
minimumVersion = 5.10.2;
|
minimumVersion = 5.10.2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
FB0119D12D62EEF000C1FA82 /* XCRemoteSwiftPackageReference "Starscream" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/daltoniam/Starscream";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 4.0.8;
|
||||||
|
};
|
||||||
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
@ -448,6 +460,11 @@
|
||||||
package = A78774702CF586AF002FE2EE /* XCRemoteSwiftPackageReference "Alamofire" */;
|
package = A78774702CF586AF002FE2EE /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
productName = Alamofire;
|
productName = Alamofire;
|
||||||
};
|
};
|
||||||
|
FB0119D22D62EEF000C1FA82 /* Starscream */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = FB0119D12D62EEF000C1FA82 /* XCRemoteSwiftPackageReference "Starscream" */;
|
||||||
|
productName = Starscream;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = A7A518B32CF5558B00822D0D /* Project object */;
|
rootObject = A7A518B32CF5558B00822D0D /* Project object */;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"originHash" : "3b609245b8d633048f6670834279f82d0601cc0879a2d8c9c86fa0dd25734ea3",
|
"originHash" : "2aab34be4ec6f8de8e42f37bee06d0f181ea2ab2db742d0c279bace3d5a0bcfb",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "abseil-cpp-binary",
|
"identity" : "abseil-cpp-binary",
|
||||||
|
@ -127,6 +127,15 @@
|
||||||
"version" : "2.4.0"
|
"version" : "2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "starscream",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/daltoniam/Starscream",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "c6bfd1af48efcc9a9ad203665db12375ba6b145a",
|
||||||
|
"version" : "4.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-protobuf",
|
"identity" : "swift-protobuf",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|
|
@ -7,13 +7,18 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
// MARK: - ACAMATE
|
// MARK: - ACAMATE
|
||||||
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
|
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
|
||||||
//#if DEV && LOCAL
|
#if LOCAL
|
||||||
//public let API_URL: String = "http://localhost:5144"
|
public let API_URL: String = "http://localhost:5144"
|
||||||
|
public let WS_URL: String = "ws://localhost:5144"
|
||||||
|
//public let WS_URL: String = "ws://169.254.53.148:5144"
|
||||||
//#else
|
//#else
|
||||||
#if DEV
|
#elseif DEV
|
||||||
public let API_URL: String = "https://devacamate.ipstein.myds.me"
|
public let API_URL: String = "https://devacamate.ipstein.myds.me"
|
||||||
|
public let WS_URL: String = "wss://devacamate.ipstein.myds.me"
|
||||||
#else
|
#else
|
||||||
public let API_URL: String = "https://acamate.ipstein.myds.me"
|
public let API_URL: String = "https://acamate.ipstein.myds.me"
|
||||||
|
public let WS_URL: String = "wss://acamate.ipstein.myds.me"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,26 +12,30 @@ import Combine
|
||||||
/// 이거 위에 다른 뷰들이 위젯 느낌으로 계속 갈아 끼워지는거
|
/// 이거 위에 다른 뷰들이 위젯 느낌으로 계속 갈아 끼워지는거
|
||||||
struct NavigationView: View {
|
struct NavigationView: View {
|
||||||
@EnvironmentObject var appVM: AppViewModel
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
@State private var naviState : NaviState = .init(act: .NONE, path: .Intro)
|
|
||||||
@State private var history: [PathName] = [.Intro]
|
@State private var history: [PathName] = [.Intro]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
switch naviState.path {
|
switch appVM.naviState.path {
|
||||||
case .NONE:
|
case .NONE:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
case .Intro:
|
case .Intro:
|
||||||
IntroView(naviState: $naviState)
|
// IntroView(naviState: $naviState)
|
||||||
|
IntroView()
|
||||||
case .Login :
|
case .Login :
|
||||||
LoginView(naviState: $naviState)
|
// LoginView(naviState: $naviState)
|
||||||
|
LoginView()
|
||||||
case .Main:
|
case .Main:
|
||||||
MainView(naviState: $naviState)
|
// MainView(naviState: $naviState)
|
||||||
|
MainView()
|
||||||
|
case .ChatRoom(let id):
|
||||||
|
ChattingRoomView(roomID: id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: naviState) { old, new in
|
.onChange(of: appVM.naviState) { old, new in
|
||||||
switch new.act {
|
switch new.act {
|
||||||
case .NONE:
|
case .NONE:
|
||||||
break
|
break
|
||||||
|
@ -54,6 +58,7 @@ struct NavigationView: View {
|
||||||
.setAlert()
|
.setAlert()
|
||||||
.setNetwork()
|
.setNetwork()
|
||||||
.loadingView(isLoading: $appVM.isLoading)
|
.loadingView(isLoading: $appVM.isLoading)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 경로에 한 단계 추가
|
/// 경로에 한 단계 추가
|
||||||
|
@ -64,7 +69,8 @@ struct NavigationView: View {
|
||||||
/// 가장 가까운 경로 삭제
|
/// 가장 가까운 경로 삭제
|
||||||
private func popHistory() {
|
private func popHistory() {
|
||||||
history.removeLast()
|
history.removeLast()
|
||||||
naviState.set(act: .NONE, path: history.last ?? .NONE)
|
appVM.naviState.set(act: .NONE, path: history.last ?? .NONE)
|
||||||
|
// naviState.set(act: .NONE, path: history.last ?? .NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 경로 기록 전체 삭제
|
/// 경로 기록 전체 삭제
|
||||||
|
@ -76,17 +82,20 @@ struct NavigationView: View {
|
||||||
/// 경로의 최상단 지우고, 새로 이동하는 경로로 설정
|
/// 경로의 최상단 지우고, 새로 이동하는 경로로 설정
|
||||||
private func moveHistory(path: PathName) {
|
private func moveHistory(path: PathName) {
|
||||||
if path == .NONE {
|
if path == .NONE {
|
||||||
naviState.set(act: .RESET, path: history.first ?? .Main)
|
appVM.naviState.set(act: .RESET, path: history.first ?? .Main)
|
||||||
|
// naviState.set(act: .RESET, path: history.first ?? .Main)
|
||||||
}
|
}
|
||||||
if history.contains(path) {
|
if history.contains(path) {
|
||||||
let remove = history.count - history.firstIndex(of: path)! - 1
|
let remove = history.count - history.firstIndex(of: path)! - 1
|
||||||
history.removeLast(remove)
|
history.removeLast(remove)
|
||||||
if remove > 0 {
|
if remove > 0 {
|
||||||
naviState.set(act: .NONE, path: path)
|
appVM.naviState.set(act: .NONE, path: path)
|
||||||
|
// naviState.set(act: .NONE, path: path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
naviState.set(act: .RESET, path: path)
|
appVM.naviState.set(act: .RESET, path: path)
|
||||||
|
// naviState.set(act: .RESET, path: path)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showHistory() {
|
private func showHistory() {
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct IntroView: View {
|
||||||
@EnvironmentObject var appVM: AppViewModel
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
|
|
||||||
@State var cancellables: Set<AnyCancellable> = []
|
@State var cancellables: Set<AnyCancellable> = []
|
||||||
@Binding var naviState : NaviState
|
// @Binding var naviState : NaviState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
|
@ -40,7 +40,7 @@ struct IntroView: View {
|
||||||
.onAppear {
|
.onAppear {
|
||||||
printLog("IntroView_onAppear")
|
printLog("IntroView_onAppear")
|
||||||
#if LOCAL
|
#if LOCAL
|
||||||
naviState.set(act: .RESET, path: .Login)
|
appVM.naviState.set(act: .RESET, path: .Login)
|
||||||
#else
|
#else
|
||||||
subscribeAlertAction()
|
subscribeAlertAction()
|
||||||
loadVersion()
|
loadVersion()
|
||||||
|
@ -71,7 +71,8 @@ struct IntroView: View {
|
||||||
appVM.showAlert.toggle()
|
appVM.showAlert.toggle()
|
||||||
} else {
|
} else {
|
||||||
// 정상 동작 넘어감
|
// 정상 동작 넘어감
|
||||||
naviState.set(act: .RESET, path: .Login)
|
appVM.naviState.set(act: .RESET, path: .Login)
|
||||||
|
// naviState.set(act: .RESET, path: .Login)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ struct IntroView: View {
|
||||||
exit(1)
|
exit(1)
|
||||||
//MARK: - TODO (앱스토어 이동 로직 넣을 것)
|
//MARK: - TODO (앱스토어 이동 로직 넣을 것)
|
||||||
} else {
|
} else {
|
||||||
naviState.set(act: .RESET, path: .Login)
|
appVM.naviState.set(act: .RESET, path: .Login)
|
||||||
}
|
}
|
||||||
}.store(in: &cancellables)
|
}.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct LoginView: View {
|
||||||
@EnvironmentObject var appVM: AppViewModel
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
@StateObject private var loginVM = LoginViewModel()
|
@StateObject private var loginVM = LoginViewModel()
|
||||||
@State var cancellables: Set<AnyCancellable> = []
|
@State var cancellables: Set<AnyCancellable> = []
|
||||||
@Binding var naviState : NaviState
|
// @Binding var naviState : NaviState
|
||||||
|
|
||||||
@State var selectIdLogin: Bool = false
|
@State var selectIdLogin: Bool = false
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ struct LoginView: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
// MARK: - TODO, 애플 계정 로그인 구현
|
// MARK: - TODO, 애플 계정 로그인 구현
|
||||||
naviState.set(act: .MOVE, path: .Main)
|
appVM.naviState.set(act: .MOVE, path: .Main)
|
||||||
} label: {
|
} label: {
|
||||||
makeButton(image: Image(.Logo.appleIcon), color: Color(.Text.black), "애플 계정으로 시작하기")
|
makeButton(image: Image(.Logo.appleIcon), color: Color(.Text.black), "애플 계정으로 시작하기")
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct HomeView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||||
VStack(spacing: 24) {
|
LazyVStack(spacing: 24) {
|
||||||
|
|
||||||
TopProfileView(myType: myType)
|
TopProfileView(myType: myType)
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ struct ChatListView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChatCellView: View {
|
struct ChatCellView: View {
|
||||||
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
|
|
||||||
var summaryChat: SummaryChat
|
var summaryChat: SummaryChat
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -41,7 +43,6 @@ struct ChatCellView: View {
|
||||||
.font(.nps(size: 8))
|
.font(.nps(size: 8))
|
||||||
.foregroundStyle(Color(.Text.detail))
|
.foregroundStyle(Color(.Text.detail))
|
||||||
}
|
}
|
||||||
// .frame(height: 24)
|
|
||||||
}
|
}
|
||||||
Spacer(minLength: 1)
|
Spacer(minLength: 1)
|
||||||
|
|
||||||
|
@ -84,6 +85,7 @@ struct ChatCellView: View {
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
printLog("채팅 내부 셀 클릭")
|
printLog("채팅 내부 셀 클릭")
|
||||||
// MARK: TO-DO
|
// MARK: TO-DO
|
||||||
|
appVM.naviState.set(act: .ADD, path: .ChatRoom(id: summaryChat.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// ChattingRoomView.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/17/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ChattingRoomView: View {
|
||||||
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
|
@StateObject private var topVM = TopViewModel()
|
||||||
|
|
||||||
|
|
||||||
|
// @StateObject private var chatVM = ChatViewModel()
|
||||||
|
|
||||||
|
@StateObject private var btnVM = ButtonViewModel()
|
||||||
|
@State var sendBtnID = UUID()
|
||||||
|
@State var addSomeBtnID = UUID()
|
||||||
|
@State var callCameraBtnID = UUID()
|
||||||
|
@State var addPhotoBtnID = UUID()
|
||||||
|
|
||||||
|
@State private var scrollOffset: CGPoint = .zero
|
||||||
|
|
||||||
|
@StateObject private var socketManager = WebSocketManager()
|
||||||
|
@State private var message = ""
|
||||||
|
|
||||||
|
let roomID: String
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .center, spacing: 0) {
|
||||||
|
TopView(topVM: topVM)
|
||||||
|
Text("Hello, World! \(roomID)")
|
||||||
|
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||||
|
|
||||||
|
}.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|
||||||
|
TextField("SEND MESSAGE", text: $message)
|
||||||
|
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||||
|
.padding()
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
Button {
|
||||||
|
socketManager.connect()
|
||||||
|
} label: {
|
||||||
|
Text("CONNECT")
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
socketManager.sendMessage(message)
|
||||||
|
message = ""
|
||||||
|
} label: {
|
||||||
|
Text("SEND")
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
socketManager.disconnect()
|
||||||
|
} label: {
|
||||||
|
Text("DISCONNECT")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List(socketManager.receivedMessage, id: \.self) { msg in
|
||||||
|
Text(msg)
|
||||||
|
.foregroundStyle(Color(.Other.cell))
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullDrawView(Color(.Other.cell))
|
||||||
|
}
|
||||||
|
|
||||||
|
.onAppear {
|
||||||
|
topVM.titleName = "클래스 명"
|
||||||
|
topVM.setLeftBtn(Image(.Icon.back), size: CGPoint(x:40, y:40)) {
|
||||||
|
appVM.naviState.set(act: .POP, path: .ChatRoom(id: roomID))
|
||||||
|
}
|
||||||
|
topVM.setRightBtn(Image(.Icon.setting), size: CGPoint(x: 40, y: 40)) {
|
||||||
|
// MARK: TO-DO
|
||||||
|
// 채팅방 설정 열기
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,14 +34,14 @@ struct ChattingView: View {
|
||||||
@State var chatMenu: chatType = .Class
|
@State var chatMenu: chatType = .Class
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
TopView(topVM: topVM)
|
TopView(topVM: topVM)
|
||||||
if myType == .ETC || myType == .Employee {
|
if myType == .ETC || myType == .Employee {
|
||||||
EmptyBoxView(title: "이용하실 수 없는 기능입니다.")
|
EmptyBoxView(title: "이용하실 수 없는 기능입니다.")
|
||||||
.padding(24)
|
.padding(24)
|
||||||
Spacer(minLength: 1)
|
Spacer(minLength: 1)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if myType == .Teacher || myType == .Admin {
|
if myType == .Teacher || myType == .Admin {
|
||||||
if myType == .Admin {
|
if myType == .Admin {
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -55,7 +55,6 @@ struct ChattingView: View {
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 0, trailing: 24))
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
SelectChatMenu(chatMenu: $chatMenu, tag: .Class, image: Image(.Icon.group), title: "클래스")
|
SelectChatMenu(chatMenu: $chatMenu, tag: .Class, image: Image(.Icon.group), title: "클래스")
|
||||||
Spacer(minLength: 1)
|
Spacer(minLength: 1)
|
||||||
|
@ -67,35 +66,53 @@ struct ChattingView: View {
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||||
}
|
}
|
||||||
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||||
VStack(spacing: 24) {
|
LazyVStack(spacing: 24) {
|
||||||
Group {
|
if myType == .Student || myType == .Parent {
|
||||||
if myType != .Parent {
|
Group {
|
||||||
DashBoardView(image: Image(.Icon.group), title: "클래스") {
|
DashBoardView(image: Image(.Icon.group), title: "클래스") {
|
||||||
ChatListView(chatList: classList)
|
ChatListView(chatList: classList)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if myType != .Teacher || myType != .Admin {
|
|
||||||
DashBoardView(image: Image(.Icon.talk), title: "선생님과 1:1") {
|
DashBoardView(image: Image(.Icon.talk), title: "선생님과 1:1") {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if myType == .Teacher {
|
|
||||||
DashBoardView(image: Image(.Icon.talk), title: "부모님과 1:1") {
|
DashBoardView(image: Image(.Icon.talk), title: "부모님과 1:1") {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.background {
|
||||||
.background {
|
RoundedRectangle(cornerRadius: 8)
|
||||||
RoundedRectangle(cornerRadius: 8)
|
.foregroundStyle(Color(.Other.cell))
|
||||||
.foregroundStyle(Color(.Other.cell))
|
}
|
||||||
|
} else {
|
||||||
|
Group {
|
||||||
|
switch chatMenu {
|
||||||
|
case .Class:
|
||||||
|
DashBoardView(image: Image(.Icon.group), title: "클래스") {
|
||||||
|
ChatListView(chatList: classList)
|
||||||
|
}
|
||||||
|
case .Student:
|
||||||
|
DashBoardView(image: Image(.Icon.talk), title: "학생과 1:1") {
|
||||||
|
|
||||||
|
}
|
||||||
|
case .Parent:
|
||||||
|
DashBoardView(image: Image(.Icon.talk), title: "부모님과 1:1") {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background {
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.foregroundStyle(Color(.Other.cell))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(
|
.padding(EdgeInsets(
|
||||||
top: (myType == .Student || myType == .Parent) ? 24 : 12, leading: 24, bottom: 24, trailing: 24))
|
top: (myType == .Student || myType == .Parent) ? 24 : 12, leading: 24, bottom: 24, trailing: 24))
|
||||||
}
|
}
|
||||||
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
// MARK: TO-DO
|
// MARK: TO-DO
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct MainView: View {
|
||||||
@EnvironmentObject var appVM: AppViewModel
|
@EnvironmentObject var appVM: AppViewModel
|
||||||
@EnvironmentObject var alertController: AlertController
|
@EnvironmentObject var alertController: AlertController
|
||||||
@State var cancellables: Set<AnyCancellable> = []
|
@State var cancellables: Set<AnyCancellable> = []
|
||||||
@Binding var naviState : NaviState
|
// @Binding var naviState : NaviState
|
||||||
|
|
||||||
@State private var myType: UserType = .Admin
|
@State private var myType: UserType = .Admin
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
enum chatType {
|
||||||
|
case Class
|
||||||
|
case Student
|
||||||
|
case Parent
|
||||||
|
}
|
||||||
|
|
||||||
struct SummaryChat {
|
struct SummaryChat {
|
||||||
var id: String
|
var id: String
|
||||||
|
|
||||||
|
@ -19,11 +25,12 @@ struct SummaryChat {
|
||||||
var notiState: Bool
|
var notiState: Bool
|
||||||
|
|
||||||
var groupNum: Int
|
var groupNum: Int
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum chatType {
|
struct ChatMesage {
|
||||||
case Class
|
var roomID: String
|
||||||
case Student
|
var chatID: String
|
||||||
case Parent
|
var senderID: String
|
||||||
|
var message: String
|
||||||
|
var sendTime: String
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ struct NaviState: Equatable {
|
||||||
var act: NaviAction
|
var act: NaviAction
|
||||||
var path: PathName
|
var path: PathName
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static func == (lhs: NaviState, rhs: NaviState) -> Bool {
|
static func == (lhs: NaviState, rhs: NaviState) -> Bool {
|
||||||
return lhs.act == rhs.act && lhs.path == rhs.path
|
return lhs.act == rhs.act && lhs.path == rhs.path
|
||||||
}
|
}
|
||||||
|
@ -45,7 +43,7 @@ enum PathName: Hashable {
|
||||||
case Intro
|
case Intro
|
||||||
case Login
|
case Login
|
||||||
case Main
|
case Main
|
||||||
|
case ChatRoom(id: String)
|
||||||
case NONE
|
case NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ class AppViewModel: ObservableObject {
|
||||||
@Published var isLoading: Bool = false
|
@Published var isLoading: Bool = false
|
||||||
@Published var showAlert: Bool = false
|
@Published var showAlert: Bool = false
|
||||||
@Published var menuName: MenuName = .Home
|
@Published var menuName: MenuName = .Home
|
||||||
|
@Published var naviState: NaviState = .init(act: .NONE, path: .Intro)
|
||||||
|
|
||||||
var alertData: AlertData = .init(body: "")
|
var alertData: AlertData = .init(body: "")
|
||||||
|
|
||||||
|
|
14
AcaMate/3. ViewModel/ChatViewModel.swift
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// ChatViewModel.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/17/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
class ChatViewModel: ObservableObject {
|
||||||
|
@Published var messages: [ChatMesage] = []
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Sean Kim on 11/26/24.
|
// Created by Sean Kim on 11/26/24.
|
||||||
//
|
//
|
||||||
|
// 이건 컨트롤러일까 매니저일까?
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
79
AcaMate/5. Manager/WebSocketManager.swift
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// WebSocketManager.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/17/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
import Starscream
|
||||||
|
|
||||||
|
// SignalR은 메세지의 끝이 \u{1E} 해당 유니코드 문자를 원함
|
||||||
|
class WebSocketManager: ObservableObject ,WebSocketDelegate {
|
||||||
|
|
||||||
|
@Published var socket: WebSocket?
|
||||||
|
@Published var receivedMessage: [String] = []
|
||||||
|
|
||||||
|
init() {
|
||||||
|
guard let url = URL(string: "\(WS_URL)/chatHub?transport=webSockets") else { return }
|
||||||
|
// guard let url = URL(string: "\(API_URL)/chatHub?transport=websockets3") else { return }
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.timeoutInterval = 5
|
||||||
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
request.setValue("Upgrade", forHTTPHeaderField: "Connection")
|
||||||
|
|
||||||
|
socket = WebSocket(request: request)
|
||||||
|
socket?.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect() {
|
||||||
|
socket?.connect()
|
||||||
|
printLog("TRY CONNECT SOCKET")
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||||
|
let handShakeMsg = """
|
||||||
|
{"protocol":"json","version":1}\u{1E}
|
||||||
|
"""
|
||||||
|
self.socket?.write(string: handShakeMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func disconnect() {
|
||||||
|
socket?.disconnect()
|
||||||
|
printLog("ClOSE SOCKET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMessage(_ message: String) {
|
||||||
|
let json = """
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"target":"SendMessage",
|
||||||
|
"arguments":["iOS", "\(message)"]
|
||||||
|
}\u{1E}
|
||||||
|
"""
|
||||||
|
socket?.write(string: json)
|
||||||
|
printLog("SEND THE MESSAGE: \(message)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func didReceive(event: Starscream.WebSocketEvent, client: any Starscream.WebSocketClient) {
|
||||||
|
switch event {
|
||||||
|
|
||||||
|
case .connected(let header):
|
||||||
|
printLog("CONNECTED : \(header)")
|
||||||
|
case .disconnected(let reason, let code):
|
||||||
|
printLog("DISCONNECTED: [\(code)] - \(reason)")
|
||||||
|
case .text(let text):
|
||||||
|
receivedMessage.append(text)
|
||||||
|
printLog("SERVER SAID: \(text)")
|
||||||
|
case .binary(let binary):
|
||||||
|
printLog("BINARY?: \(binary)")
|
||||||
|
case .error(let error):
|
||||||
|
printLog("ERROR!: \(error)")
|
||||||
|
case .cancelled:
|
||||||
|
printLog("SOCKET CONNECTED CANCELLED!!!")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
Before Width: | Height: | Size: 779 B After Width: | Height: | Size: 779 B |
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 630 B |
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
Before Width: | Height: | Size: 636 B After Width: | Height: | Size: 636 B |
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 543 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |