Commit 9aeb57c1 authored by shigemi miura's avatar shigemi miura

チャット機能

parent 40fa98bc
...@@ -11,7 +11,7 @@ struct ChatView: View { ...@@ -11,7 +11,7 @@ struct ChatView: View {
@EnvironmentObject private var selectedTabModel: SelectedTabModel @EnvironmentObject private var selectedTabModel: SelectedTabModel
@ObservedObject var message = SharingData.message @ObservedObject var message = SharingData.message
@State var isShowMember: Bool = false @State var isShowMember: Bool = false
@StateObject var scrollBottom = ScrollBottom()
var body: some View { var body: some View {
ZStack { ZStack {
if message.mode == true{ if message.mode == true{
...@@ -20,7 +20,7 @@ struct ChatView: View { ...@@ -20,7 +20,7 @@ struct ChatView: View {
} }
VStack(spacing: 0){ VStack(spacing: 0){
ChatTitleView(isShowMember: $isShowMember) ChatTitleView(isShowMember: $isShowMember)
ZStack{ ZStack{
ScrollViewReader { proxy in ScrollViewReader { proxy in
ScrollView(.vertical) { ScrollView(.vertical) {
...@@ -29,7 +29,6 @@ struct ChatView: View { ...@@ -29,7 +29,6 @@ struct ChatView: View {
.frame(height: 20) .frame(height: 20)
ForEach(message.messages, id: \.messageId) { msg in ForEach(message.messages, id: \.messageId) { msg in
// if msg.fromId == String(SharingData.my.id) {
if msg.from == Preferences.UserName { if msg.from == Preferences.UserName {
//自分のメッセージ //自分のメッセージ
MyChatContentView(message: msg) MyChatContentView(message: msg)
...@@ -42,20 +41,28 @@ struct ChatView: View { ...@@ -42,20 +41,28 @@ struct ChatView: View {
} }
} }
.onAppear { .onAppear {
self.scrollBottom.onAppear() if let id = message.messages.last?.messageId {
print(debug: "ChatView:onAppear")
proxy.scrollTo(id, anchor: .center)
}
} }
} .onChange(of: message.messages.count) { newValue in
.onReceive(message.$messages) { (value) in if let id = message.messages.last?.messageId {
withAnimation { withAnimation {
guard !value.isEmpty else {return} print(debug: "ChatView:onChange")
proxy.scrollTo(value.last?.messageId, anchor: .center) proxy.scrollTo(id, anchor: .center)
if let lastId = value.last?.messageId { }
print(debug: "\(String(describing: lastId))") }
}
.onReceive(message.$messages) { (value) in
withAnimation {
guard !value.isEmpty else {return}
if let id = message.messages.last?.messageId {
print(debug: "ChatView:onReceive")
proxy.scrollTo(id, anchor: .center)
}
} }
} }
}
.onChange(of: scrollBottom.scrollID) { id in
proxy.scrollTo(id, anchor: .center)
} }
} }
HStack { HStack {
...@@ -84,18 +91,6 @@ struct ChatView: View { ...@@ -84,18 +91,6 @@ struct ChatView: View {
} }
} }
class ScrollBottom: ObservableObject {
@Published var scrollID: String = ""
func onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let id = SharingData.message.messages.last?.messageId {
self.scrollID = id
}
}
}
}
#Preview { #Preview {
ChatView() ChatView()
} }
...@@ -77,6 +77,14 @@ struct OtherChatContentView: View { ...@@ -77,6 +77,14 @@ struct OtherChatContentView: View {
} }
} }
} }
.onAppear {
//既読確認
let result = message.viewer.first(where: {$0.id == String(SharingData.my.id)})
if result == nil {
let signalRService = SignalR()
signalRService.ackMessage(messageId: message.messageId)
}
}
.padding(.leading, 20) .padding(.leading, 20)
Spacer() Spacer()
} }
......
...@@ -9,8 +9,8 @@ import Foundation ...@@ -9,8 +9,8 @@ import Foundation
struct ResAckMessage: Codable { struct ResAckMessage: Codable {
var shipId: Int var shipId: Int
var messageId: Int //UUID var messageId: String //UUID
var time: String //投稿日時 var time: String //投稿日時
var location: Int //1:Shore、2:Ship var location: Int //1:Shore、2:Ship
var fromId: Int //ユーザーID var fromId: String //ユーザーID
} }
...@@ -9,12 +9,12 @@ import Foundation ...@@ -9,12 +9,12 @@ import Foundation
struct ResChatMessage: Codable { struct ResChatMessage: Codable {
var shipId: Int var shipId: Int
var messageId: Int //UUID var messageId: String //UUID
var type: Int //0:テキスト、1:スタンプ var type: Int //0:テキスト、1:スタンプ
var time: String //投稿日時 var time: String //投稿日時
var location: Int //1:Shore、2:Ship var location: Int //1:Shore、2:Ship
var from: String //投稿者名 var from: String //投稿者名
var fromId: Int //ユーザーID var fromId: String //ユーザーID
var message: String //テキスト var message: String //テキスト
var stampId: Int //スタンプ番号 var stampId: Int //スタンプ番号
} }
...@@ -11,6 +11,6 @@ struct ResChatMode: Codable { ...@@ -11,6 +11,6 @@ struct ResChatMode: Codable {
var shipId: Int var shipId: Int
var time: String //投稿日時 var time: String //投稿日時
var location: Int //1:Shore、2:Ship var location: Int //1:Shore、2:Ship
var fromId: Int //ユーザーID var fromId: String //ユーザーID
var mode: Int //0:通常、1:Warning中 var mode: Int //0:通常、1:Warning中
} }
...@@ -187,9 +187,11 @@ struct LoginView: View { ...@@ -187,9 +187,11 @@ struct LoginView: View {
let resjson = serverSession.fromJSON(resultData: resultData, resltType: ResLogin.self) let resjson = serverSession.fromJSON(resultData: resultData, resltType: ResLogin.self)
if let res = resjson { if let res = resjson {
SharingData.my.id = res.id SharingData.my.id = res.id
SharingData.my.shipId = res.shipId
SharingData.my.shipName = res.shipName SharingData.my.shipName = res.shipName
SharingData.my.imo = res.imo SharingData.my.imo = res.imo
SharingData.my.mmsi = res.mmsi SharingData.my.mmsi = res.mmsi
SharingData.my.isFuelSwitchTask = res.enableFuelSwitchTask
Preferences.shipId = res.shipId Preferences.shipId = res.shipId
...@@ -247,6 +249,7 @@ struct LoginView: View { ...@@ -247,6 +249,7 @@ struct LoginView: View {
let resjson = serverSession.fromJSON(resultData: resultData, resltType: ResLogin.self) let resjson = serverSession.fromJSON(resultData: resultData, resltType: ResLogin.self)
if let res = resjson { if let res = resjson {
SharingData.my.id = res.id SharingData.my.id = res.id
SharingData.my.shipId = res.shipId
SharingData.my.shipName = res.shipName SharingData.my.shipName = res.shipName
SharingData.my.imo = res.imo SharingData.my.imo = res.imo
SharingData.my.mmsi = res.mmsi SharingData.my.mmsi = res.mmsi
......
...@@ -17,10 +17,12 @@ struct MapRepresentable: UIViewControllerRepresentable{ ...@@ -17,10 +17,12 @@ struct MapRepresentable: UIViewControllerRepresentable{
@ObservedObject var pushHistory = SharingData.pushHistory @ObservedObject var pushHistory = SharingData.pushHistory
@State var mapVC = MapViewController() @State var mapVC = MapViewController()
//作成したいViewControllerを返すメソッド
func makeUIViewController(context: Context) -> some UIViewController { func makeUIViewController(context: Context) -> some UIViewController {
mapVC mapVC
} }
//Viewが更新された場合に必要な処理を実装
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
let ecaArea = ecaData.ecaArea.map{ $0.1 }.filter{ $0.isRunning }.first let ecaArea = ecaData.ecaArea.map{ $0.1 }.filter{ $0.isRunning }.first
if let ecaArea = ecaArea{ if let ecaArea = ecaArea{
...@@ -29,6 +31,7 @@ struct MapRepresentable: UIViewControllerRepresentable{ ...@@ -29,6 +31,7 @@ struct MapRepresentable: UIViewControllerRepresentable{
mapVC.removeEcaLine() mapVC.removeEcaLine()
} }
//ECA領域を画面中央に表示
if let focusEcaAreaId = ecaData.focusEca, let focusEca = ecaData.ecaArea[focusEcaAreaId]{ if let focusEcaAreaId = ecaData.focusEca, let focusEca = ecaData.ecaArea[focusEcaAreaId]{
mapVC.updateCamera(location: focusEca.centerPosition, zoomlevel: focusEca.zoomLevel) mapVC.updateCamera(location: focusEca.centerPosition, zoomlevel: focusEca.zoomLevel)
mapVC.updateOneTimeEca(eca: focusEca.points) mapVC.updateOneTimeEca(eca: focusEca.points)
...@@ -38,6 +41,7 @@ struct MapRepresentable: UIViewControllerRepresentable{ ...@@ -38,6 +41,7 @@ struct MapRepresentable: UIViewControllerRepresentable{
} }
} }
//通知場所を画面中央に表示
if let focusPushHistoryId = pushHistory.focusPushHistory, let focusPushHistory = pushHistory.pushHistoryData[focusPushHistoryId]{ if let focusPushHistoryId = pushHistory.focusPushHistory, let focusPushHistory = pushHistory.pushHistoryData[focusPushHistoryId]{
if let position = focusPushHistory.position { if let position = focusPushHistory.position {
if let latitude = position.lat, let longitude = position.lon { if let latitude = position.lat, let longitude = position.lon {
...@@ -46,6 +50,11 @@ struct MapRepresentable: UIViewControllerRepresentable{ ...@@ -46,6 +50,11 @@ struct MapRepresentable: UIViewControllerRepresentable{
} }
} }
//自船を画面中央に表示
if location.focusOwnShip {
mapVC.updateCamera(location: location.location, zoomlevel: nil)
}
if let mylocation = location.location { if let mylocation = location.location {
mapVC.updateOwnShip(location: mylocation, bearing: location.heading) mapVC.updateOwnShip(location: mylocation, bearing: location.heading)
...@@ -84,7 +93,6 @@ class MapViewController : UIViewController{ ...@@ -84,7 +93,6 @@ class MapViewController : UIViewController{
mapView.mapboxMap.onNext(event: .mapLoaded) { [self] _ in mapView.mapboxMap.onNext(event: .mapLoaded) { [self] _ in
self.addLayers() self.addLayers()
} }
} }
...@@ -284,6 +292,9 @@ class MapViewController : UIViewController{ ...@@ -284,6 +292,9 @@ class MapViewController : UIViewController{
if SharingData.pushHistory.focusPushHistory != nil { if SharingData.pushHistory.focusPushHistory != nil {
SharingData.pushHistory.focusPushHistory = nil SharingData.pushHistory.focusPushHistory = nil
} }
if SharingData.location.focusOwnShip {
SharingData.location.focusOwnShip = false
}
} }
///Ecaの線3本 ///Ecaの線3本
......
...@@ -213,7 +213,7 @@ struct MenuView: View { ...@@ -213,7 +213,7 @@ struct MenuView: View {
loginViewModel.isLogin = false loginViewModel.isLogin = false
sceneDelegate.tabWindow?.isHidden = true sceneDelegate.tabWindow?.isHidden = true
SharingData.message.mode = 0 SharingData.message.mode = false
SharingData.message.messages = [] SharingData.message.messages = []
SharingData.pushHistory.focusPushHistory = nil SharingData.pushHistory.focusPushHistory = nil
...@@ -223,6 +223,8 @@ struct MenuView: View { ...@@ -223,6 +223,8 @@ struct MenuView: View {
SharingData.map.legLine = [] SharingData.map.legLine = []
SharingData.map.portLine = [] SharingData.map.portLine = []
SharingData.map.starboardLine = [] SharingData.map.starboardLine = []
connection!.stop()
} }
Button("No") {} Button("No") {}
} message: { Text("Are you sure?") } } message: { Text("Are you sure?") }
......
...@@ -17,7 +17,6 @@ struct SailassistApp: App { ...@@ -17,7 +17,6 @@ struct SailassistApp: App {
@State private var isPrivAgree = Preferences.privacyPolicyAgreeDate != nil @State private var isPrivAgree = Preferences.privacyPolicyAgreeDate != nil
let locationViewModel = LocationViewModel() let locationViewModel = LocationViewModel()
let signalRService = SignalRService()
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
...@@ -76,7 +75,7 @@ class NotificationTags: NSObject { ...@@ -76,7 +75,7 @@ class NotificationTags: NSObject {
} }
} }
private var connection: HubConnection? var connection: HubConnection?
class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, MSInstallationLifecycleDelegate { class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, MSInstallationLifecycleDelegate {
private var hubConnectionDelegate: HubConnectionDelegate? private var hubConnectionDelegate: HubConnectionDelegate?
...@@ -110,23 +109,28 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M ...@@ -110,23 +109,28 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
connection = HubConnectionBuilder(url: URL(string : HttpRequestType.SignalR.rawValue)!) connection = HubConnectionBuilder(url: URL(string : HttpRequestType.SignalR.rawValue)!)
.withHubConnectionDelegate(delegate: hubConnectionDelegate!) .withHubConnectionDelegate(delegate: hubConnectionDelegate!)
.withAutoReconnect() .withAutoReconnect()
.withLogging(minLogLevel: .error) // .withLogging(minLogLevel: .error)
.withHubConnectionOptions(configureHubConnectionOptions: {options in options.keepAliveInterval = 20 }) .withLogging(minLogLevel: .debug)
// .withHubConnectionOptions(configureHubConnectionOptions: {options in options.keepAliveInterval = 20 })
.build() .build()
connection!.on(method: "chatMessage", callback: { (message: ResChatMessage) in if let r_connection = connection {
self.handleChatMessage(message: message) r_connection.stop()
})
connection!.on(method: "ackMessage", callback: { (message: ResAckMessage) in r_connection.on(method: "chatMessage", callback: { (message: ResChatMessage) in
self.handleAckMessage(message: message) self.handleChatMessage(message: message)
}) })
connection!.on(method: "chatMode", callback: { (message: ResChatMode) in r_connection.on(method: "ackMessage", callback: { (message: ResAckMessage) in
self.handleChatMessage(message: message) self.handleAckMessage(message: message)
}) })
connection!.start() r_connection.on(method: "chatMode", callback: { (message: ResChatMode) in
self.handleChatMessage(message: message)
})
r_connection.start()
}
return true return true
} }
...@@ -174,14 +178,17 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M ...@@ -174,14 +178,17 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
private func handleChatMessage(message: ResChatMessage) { private func handleChatMessage(message: ResChatMessage) {
print(debug: "called") print(debug: "called")
// print(debug: "called \(ResMessage)")
} }
private func handleAckMessage(message: ResAckMessage) { private func handleAckMessage(message: ResAckMessage) {
print(debug: "called") print(debug: "called")
// print(debug: "called \(ResAckMessage)")
} }
private func handleChatMessage(message: ResChatMode) { private func handleChatMessage(message: ResChatMode) {
print(debug: "called") print(debug: "called")
// print(debug: "called \(ResChatMode)")
} }
// func applicationWillTerminate(_ aNotification: Notification) { // func applicationWillTerminate(_ aNotification: Notification) {
...@@ -190,6 +197,8 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M ...@@ -190,6 +197,8 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
} }
class SignalR: NSObject { class SignalR: NSObject {
@ObservedObject var msg = SharingData.message
func chatMessage(message: String) { func chatMessage(message: String) {
var request = ReqMessage(shipId: Preferences.shipId, messageId: UUID().uuidString) var request = ReqMessage(shipId: Preferences.shipId, messageId: UUID().uuidString)
request.type = 0 //0:テキスト, 1:スタンプ request.type = 0 //0:テキスト, 1:スタンプ
...@@ -205,6 +214,9 @@ class SignalR: NSObject { ...@@ -205,6 +214,9 @@ class SignalR: NSObject {
if let e = error { if let e = error {
print(debug: "Error:\(e)") print(debug: "Error:\(e)")
} }
let viewer = Viewer(time: "", location: 0, id: "")
let ownMsg = ChatMessage(shipId: request.shipId, messageId: request.messageId, type: request.type, time: request.time, location: request.location, from: request.from, message: request.message, stampId: request.stampId, viewer: [viewer])
self.msg.messages.append(ownMsg)
} }
} }
} }
...@@ -249,22 +261,27 @@ class ChatHubConnectionDelegate: HubConnectionDelegate { ...@@ -249,22 +261,27 @@ class ChatHubConnectionDelegate: HubConnectionDelegate {
} }
func connectionDidOpen(hubConnection: HubConnection) { func connectionDidOpen(hubConnection: HubConnection) {
print(debug: "connectionDidOpen")
// app?.connectionDidStart() // app?.connectionDidStart()
} }
func connectionDidFailToOpen(error: Error) { func connectionDidFailToOpen(error: Error) {
print(debug: "connectionDidFailToOpen")
// app?.connectionDidFailToOpen(error: error) // app?.connectionDidFailToOpen(error: error)
} }
func connectionDidClose(error: Error?) { func connectionDidClose(error: Error?) {
print(debug: "connectionDidClose")
// app?.connectionDidClose(error: error) // app?.connectionDidClose(error: error)
} }
func connectionWillReconnect(error: Error) { func connectionWillReconnect(error: Error) {
print(debug: "connectionWillReconnect")
// app?.connectionWillReconnect(error: error) // app?.connectionWillReconnect(error: error)
} }
func connectionDidReconnect() { func connectionDidReconnect() {
print(debug: "connectionDidReconnect")
// app?.connectionDidReconnect() // app?.connectionDidReconnect()
} }
} }
...@@ -289,8 +306,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { ...@@ -289,8 +306,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
return return
} }
let subtitle = arrAlert["subtitle"] as? String ?? "" //送信先名称 let subtitle = arrAlert["subtitle"] as? String ?? "" //送信先名称
let strTitle = arrAlert["title"] as? String ?? "" //送信内容 let strTitle = arrAlert["title"] as? String ?? "" //船名
let strBody = arrAlert["body"] as? String ?? "" //船名 let strBody = arrAlert["body"] as? String ?? "" //送信内容
let message = GetMessage() let message = GetMessage()
message.start() message.start()
...@@ -336,8 +353,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { ...@@ -336,8 +353,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
return return
} }
let subtitle = arrAlert["subtitle"] as? String ?? "" //送信先名称 let subtitle = arrAlert["subtitle"] as? String ?? "" //送信先名称
let strTitle = arrAlert["title"] as? String ?? "" //送信内容 let strTitle = arrAlert["title"] as? String ?? "" //船名
let strBody = arrAlert["body"] as? String ?? "" //船名 let strBody = arrAlert["body"] as? String ?? "" //送信内容
let message = GetMessage() let message = GetMessage()
message.start() message.start()
......
...@@ -235,7 +235,7 @@ class ServerSession{ ...@@ -235,7 +235,7 @@ class ServerSession{
httpBody1 += "Content-Type: image/jpeg\r\n" httpBody1 += "Content-Type: image/jpeg\r\n"
httpBody1 += "\r\n" httpBody1 += "\r\n"
var tmp = httpBody1.data(using: .utf8)! // var tmp = httpBody1.data(using: .utf8)!
var httpBody = Data() var httpBody = Data()
httpBody.append(httpBody1.data(using: .utf8)!) httpBody.append(httpBody1.data(using: .utf8)!)
......
...@@ -12,6 +12,7 @@ class SharingData{ ...@@ -12,6 +12,7 @@ class SharingData{
static var my = My() static var my = My()
class My: ObservableObject { class My: ObservableObject {
@Published var id: Int = 0 @Published var id: Int = 0
@Published var shipId: Int = 0
@Published var shipName: String = "" @Published var shipName: String = ""
@Published var imo: Int = 0 @Published var imo: Int = 0
@Published var mmsi: Int = 0 @Published var mmsi: Int = 0
...@@ -36,6 +37,8 @@ class SharingData{ ...@@ -36,6 +37,8 @@ class SharingData{
@Published var heading: Double = 0.0 @Published var heading: Double = 0.0
@Published var dataTime: String = "" //2023-11-02T05:25:49.4362123Z @Published var dataTime: String = "" //2023-11-02T05:25:49.4362123Z
@Published var focusOwnShip: Bool = false
func setLocation(){ func setLocation(){
if Preferences.LocationType == 0 { if Preferences.LocationType == 0 {
self.location = self.gps self.location = self.gps
...@@ -113,7 +116,7 @@ class SharingData{ ...@@ -113,7 +116,7 @@ class SharingData{
static var message = Message() static var message = Message()
class Message: ObservableObject { class Message: ObservableObject {
@Published var mode: Int = 0 // 0:通常 , 1:Warning中 @Published var mode: Bool = false // false:通常 , true:Warning中
@Published var messages: [ChatMessage] = [] @Published var messages: [ChatMessage] = []
@Published var users: [ChatUser] = [] @Published var users: [ChatUser] = []
......
...@@ -30,6 +30,7 @@ struct MainTabView: View { ...@@ -30,6 +30,7 @@ struct MainTabView: View {
appearance.backgroundColor = .clear appearance.backgroundColor = .clear
UITabBar.appearance().scrollEdgeAppearance = appearance UITabBar.appearance().scrollEdgeAppearance = appearance
UITabBar.appearance().standardAppearance = appearance UITabBar.appearance().standardAppearance = appearance
SharingData.location.focusOwnShip = true
// EcaCoordinatesTable().setEcaData() // EcaCoordinatesTable().setEcaData()
} }
...@@ -103,6 +104,7 @@ struct CustomTabBar: View { ...@@ -103,6 +104,7 @@ struct CustomTabBar: View {
@State var isLocationAlert = false @State var isLocationAlert = false
@Environment(\.openURL) var openURL @Environment(\.openURL) var openURL
@ObservedObject var my = SharingData.my @ObservedObject var my = SharingData.my
@ObservedObject var location = SharingData.location
var body: some View { var body: some View {
VStack(spacing: 0){ VStack(spacing: 0){
...@@ -114,6 +116,7 @@ struct CustomTabBar: View { ...@@ -114,6 +116,7 @@ struct CustomTabBar: View {
selectedTabModel.activeTab = tab selectedTabModel.activeTab = tab
if tab == .task { if tab == .task {
selectedTabModel.isPoppver.toggle() selectedTabModel.isPoppver.toggle()
location.focusOwnShip = true
} else { } else {
selectedTabModel.isPoppver = false selectedTabModel.isPoppver = false
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment