Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
Sailassist
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
54
Merge Requests
54
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gr-ssv
Sailassist
Commits
8cec0939
Commit
8cec0939
authored
Sep 29, 2025
by
shigemi miura
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
・クラス分割
・チャットのイメージデータの先読み ・イメージデータ送信時はテンポラリを表示
parent
d2befa8b
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
1427 additions
and
232 deletions
+1427
-232
project.pbxproj
Seilassist/Sailassist.xcodeproj/project.pbxproj
+0
-0
NotificationView.swift
Seilassist/Sailassist/Alert/NotificationView.swift
+3
-3
GetMessage.swift
Seilassist/Sailassist/Chat/GetMessage.swift
+6
-0
ImageQualityPreset.swift
Seilassist/Sailassist/Chat/ImageQualityPreset.swift
+8
-7
Imagepicker.swift
Seilassist/Sailassist/Chat/Imagepicker.swift
+34
-7
KeyboardResponder.swift
Seilassist/Sailassist/Chat/KeyboardResponder.swift
+36
-0
ChatInputView.swift
Seilassist/Sailassist/Chat/View/ChatInputView.swift
+0
-0
ChatUrlImageView.swift
Seilassist/Sailassist/Chat/View/ChatUrlImageView.swift
+28
-41
ChatUrlVideoView.swift
Seilassist/Sailassist/Chat/View/ChatUrlVideoView.swift
+41
-0
ChatView.swift
Seilassist/Sailassist/Chat/View/ChatView.swift
+85
-14
CustomCornerRadius.swift
Seilassist/Sailassist/Chat/View/CustomCornerRadius.swift
+0
-1
MyChatContentView.swift
Seilassist/Sailassist/Chat/View/MyChatContentView.swift
+7
-3
OtherChatContentView.swift
Seilassist/Sailassist/Chat/View/OtherChatContentView.swift
+6
-2
ChatInputViewModel.swift
...assist/Sailassist/Chat/ViewModel/ChatInputViewModel.swift
+170
-0
ChatViewModel.swift
Seilassist/Sailassist/Chat/ViewModel/ChatViewModel.swift
+43
-0
ContentView.swift
Seilassist/Sailassist/ContentView.swift
+2
-0
LocationCalculation.swift
Seilassist/Sailassist/Location/LocationCalculation.swift
+2
-2
LoginView.swift
Seilassist/Sailassist/Login/LoginView.swift
+5
-6
MapRepresentable.swift
Seilassist/Sailassist/Map/MapRepresentable.swift
+3
-1
EcaListMainView.swift
Seilassist/Sailassist/Map/Task/View/EcaListMainView.swift
+50
-0
EcaMenuView.swift
Seilassist/Sailassist/Map/Task/View/EcaMenuView.swift
+54
-0
EcaSettingMainView.swift
Seilassist/Sailassist/Map/Task/View/EcaSettingMainView.swift
+53
-0
FuelSwitchingMainView.swift
...sist/Sailassist/Map/Task/View/FuelSwitchingMainView.swift
+51
-0
FuelSwitchingView.swift
Seilassist/Sailassist/Map/Task/View/FuelSwitchingView.swift
+0
-54
MenuMainView.swift
Seilassist/Sailassist/Map/Task/View/MenuMainView.swift
+78
-0
MenuTaskView.swift
Seilassist/Sailassist/Map/Task/View/MenuTaskView.swift
+0
-0
NgaMenuView.swift
Seilassist/Sailassist/Map/Task/View/NgaMenuView.swift
+49
-0
NgaNotificationMainView.swift
...st/Sailassist/Map/Task/View/NgaNotificationMainView.swift
+51
-0
NgaNotificationView.swift
...assist/Sailassist/Map/Task/View/NgaNotificationView.swift
+0
-49
NgaSettingMainView.swift
Seilassist/Sailassist/Map/Task/View/NgaSettingMainView.swift
+60
-0
SailassistApp.swift
Seilassist/Sailassist/SailassistApp.swift
+75
-24
SessionUploadImage.swift
Seilassist/Sailassist/ServerSession/SessionUploadImage.swift
+30
-18
CustomTabBar.swift
Seilassist/Sailassist/Tab/CustomTabBar.swift
+119
-0
CustomTabBarViewModel.swift
Seilassist/Sailassist/Tab/CustomTabBarViewModel.swift
+115
-0
MainTabView.swift
Seilassist/Sailassist/Tab/MainTabView.swift
+0
-0
NotificationBadge.swift
Seilassist/Sailassist/Tab/NotificationBadge.swift
+47
-0
MainTabView.swift
Seilassist/Sailassist/Tab/View/MainTabView.swift
+116
-0
No files found.
Seilassist/Sailassist.xcodeproj/project.pbxproj
View file @
8cec0939
This diff is collapsed.
Click to expand it.
Seilassist/Sailassist/Alert/NotificationView.swift
View file @
8cec0939
...
@@ -38,9 +38,9 @@ struct NotificationView: View {
...
@@ -38,9 +38,9 @@ struct NotificationView: View {
.
onAppear
{
.
onAppear
{
let
pushCount
=
pushHist
.
viewCnt
let
pushCount
=
pushHist
.
viewCnt
print
(
debug
:
"
\(
pushCount
)
"
)
print
(
debug
:
"
\(
pushCount
)
"
)
DispatchQueue
.
main
.
async
{
//
DispatchQueue.main.async {
pushHist
.
viewCnt
=
0
//
pushHist.viewCnt = 0
}
//
}
}
}
}
}
...
...
Seilassist/Sailassist/Chat/GetMessage.swift
View file @
8cec0939
...
@@ -9,6 +9,7 @@ import Foundation
...
@@ -9,6 +9,7 @@ import Foundation
class
GetMessage
{
class
GetMessage
{
var
sessionGetMessage
=
SessionGetMessage
()
var
sessionGetMessage
=
SessionGetMessage
()
var
chatViewModel
:
ChatViewModel
?
func
start
()
{
func
start
()
{
print
(
debug
:
"called"
)
print
(
debug
:
"called"
)
...
@@ -27,6 +28,11 @@ class GetMessage {
...
@@ -27,6 +28,11 @@ class GetMessage {
if
let
msg
=
res
.
messages
{
if
let
msg
=
res
.
messages
{
//既読マーク確認
//既読マーク確認
SharingData
.
message
.
messages
=
msg
SharingData
.
message
.
messages
=
msg
Task
{
@MainActor
in
self
.
chatViewModel
?
.
loadMessages
()
}
self
.
checkUnreadMessages
()
self
.
checkUnreadMessages
()
}
}
SharingData
.
message
.
users
=
[]
SharingData
.
message
.
users
=
[]
...
...
Seilassist/Sailassist/Chat/ImageQualityPreset.swift
View file @
8cec0939
...
@@ -12,20 +12,21 @@ enum ImageQualityPreset {
...
@@ -12,20 +12,21 @@ enum ImageQualityPreset {
}
}
}
}
var
targetSize
:
CGSize
{
var
scale
:
CGFloat
{
switch
self
{
switch
self
{
case
.
low
:
return
CGSize
(
width
:
640
,
height
:
480
)
case
.
low
:
return
0.3
case
.
middle
:
return
CGSize
(
width
:
1280
,
height
:
960
)
case
.
middle
:
return
0.6
case
.
high
:
return
CGSize
(
width
:
1920
,
height
:
1440
)
case
.
high
:
return
1.0
}
}
}
}
}
}
extension
UIImage
{
extension
UIImage
{
func
resized
(
to
targetSize
:
CGSize
)
->
UIImage
{
func
resized
(
to
scale
:
CGFloat
)
->
UIImage
?
{
let
renderer
=
UIGraphicsImageRenderer
(
size
:
targetSize
)
let
newSize
=
CGSize
(
width
:
self
.
size
.
width
*
scale
,
height
:
self
.
size
.
height
*
scale
)
let
renderer
=
UIGraphicsImageRenderer
(
size
:
newSize
)
return
renderer
.
image
{
_
in
return
renderer
.
image
{
_
in
self
.
draw
(
in
:
CGRect
(
origin
:
.
zero
,
size
:
target
Size
))
self
.
draw
(
in
:
CGRect
(
origin
:
.
zero
,
size
:
new
Size
))
}
}
}
}
}
}
Seilassist/Sailassist/Chat/Imagepicker.swift
View file @
8cec0939
...
@@ -41,19 +41,46 @@ struct Imagepicker : UIViewControllerRepresentable {
...
@@ -41,19 +41,46 @@ struct Imagepicker : UIViewControllerRepresentable {
}
}
//MARK: - Use Photo
//MARK: - Use Photo
func
imagePickerController
(
_
picker
:
UIImagePickerController
,
didFinishPickingMediaWithInfo
info
:
[
UIImagePickerController
.
InfoKey
:
Any
])
{
func
imagePickerController
(
_
picker
:
UIImagePickerController
,
didFinishPickingMediaWithInfo
info
:
[
UIImagePickerController
.
InfoKey
:
Any
])
{
if
let
image
=
info
[
.
originalImage
]
as?
UIImage
{
if
let
image
=
info
[
.
originalImage
]
as?
UIImage
{
let
resizedImage
=
image
.
resized
(
to
:
parent
.
preset
.
targetSize
)
//MARK: - 画像データのリサイズ
if
let
data
=
resizedImage
.
jpegData
(
compressionQuality
:
parent
.
preset
.
compressionQuality
)
{
DispatchQueue
.
global
(
qos
:
.
userInitiated
)
.
async
{
parent
.
image
=
data
// リサイズ処理
let
scale
:
CGFloat
=
min
(
0.1
,
max
(
self
.
parent
.
preset
.
scale
,
1.0
))
if
let
resizedImage
=
image
.
resized
(
to
:
scale
),
let
data
=
resizedImage
.
jpegData
(
compressionQuality
:
self
.
parent
.
preset
.
compressionQuality
)
{
DispatchQueue
.
main
.
async
{
self
.
parent
.
image
=
data
self
.
parent
.
show
.
toggle
()
}
}
else
{
DispatchQueue
.
main
.
async
{
print
(
"Failed to resize or compress image"
)
self
.
parent
.
show
.
toggle
()
}
}
}
}
}
else
if
let
videoURL
=
info
[
.
mediaURL
]
as?
URL
{
}
else
if
let
videoURL
=
info
[
.
mediaURL
]
as?
URL
{
// 動画ファイルの処理(例: Dataに変換して保存)
//MARK: - 動画データのリサイズ
DispatchQueue
.
global
(
qos
:
.
userInitiated
)
.
async
{
if
let
videoData
=
try
?
Data
(
contentsOf
:
videoURL
)
{
if
let
videoData
=
try
?
Data
(
contentsOf
:
videoURL
)
{
parent
.
image
=
videoData
DispatchQueue
.
main
.
async
{
self
.
parent
.
image
=
videoData
self
.
parent
.
show
.
toggle
()
}
}
else
{
DispatchQueue
.
main
.
async
{
print
(
"Failed to process video data"
)
self
.
parent
.
show
.
toggle
()
}
}
}
}
else
{
DispatchQueue
.
main
.
async
{
print
(
"No valid media selected"
)
self
.
parent
.
show
.
toggle
()
}
}
}
}
parent
.
show
.
toggle
()
}
}
}
}
}
}
Seilassist/Sailassist/Chat/KeyboardResponder.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
import
Combine
class
KeyboardResponder
:
ObservableObject
{
@Published
var
currentHeight
:
CGFloat
=
0
@Published
var
isKeyboardVisible
:
Bool
=
false
private
var
cancellableSet
:
Set
<
AnyCancellable
>
=
[]
init
()
{
let
willShow
=
NotificationCenter
.
default
.
publisher
(
for
:
UIResponder
.
keyboardWillShowNotification
)
.
compactMap
{
notification
->
CGFloat
?
in
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.1
)
{
self
.
isKeyboardVisible
=
true
}
return
(
notification
.
userInfo
?[
UIResponder
.
keyboardFrameEndUserInfoKey
]
as?
CGRect
)?
.
height
??
0
}
let
willHide
=
NotificationCenter
.
default
.
publisher
(
for
:
UIResponder
.
keyboardWillHideNotification
)
.
map
{
_
->
CGFloat
in
DispatchQueue
.
main
.
async
{
self
.
isKeyboardVisible
=
false
}
return
0
}
Publishers
.
Merge
(
willShow
,
willHide
)
.
receive
(
on
:
RunLoop
.
main
)
.
assign
(
to
:
\
KeyboardResponder
.
currentHeight
,
on
:
self
)
.
store
(
in
:
&
cancellableSet
)
}
deinit
{
cancellableSet
.
forEach
{
$0
.
cancel
()
}
}
}
Seilassist/Sailassist/Chat/View/ChatInputView.swift
View file @
8cec0939
This diff is collapsed.
Click to expand it.
Seilassist/Sailassist/Chat/View/ChatUrlImageView.swift
View file @
8cec0939
...
@@ -8,55 +8,42 @@
...
@@ -8,55 +8,42 @@
import
SwiftUI
import
SwiftUI
struct
ChatUrlImageView
:
View
{
struct
ChatUrlImageView
:
View
{
var
imageUrl
=
""
let
imageUrl
:
String
var
onLoad
:
(()
->
Void
)?
=
nil
init
(
message
:
ChatMessage
)
{
if
let
url
=
message
.
message
{
self
.
imageUrl
=
url
}
}
var
body
:
some
View
{
var
body
:
some
View
{
AsyncImage
(
url
:
URL
(
string
:
imageUrl
))
{
phase
in
if
let
url
=
URL
(
string
:
imageUrl
)
{
if
let
image
=
phase
.
image
{
AsyncImage
(
url
:
url
)
{
phase
in
switch
phase
{
case
.
success
(
let
image
):
image
image
.
resizable
(
resizingMode
:
.
stretch
)
.
resizable
(
)
.
aspectRatio
(
contentMode
:
.
fit
)
.
aspectRatio
(
contentMode
:
.
fit
)
.
frame
(
width
:
250
)
.
frame
(
width
:
250
)
}
else
if
phase
.
error
!=
nil
{
.
onAppear
{
Color
.
gray
.
opacity
(
0.2
)
onLoad
?()
.
overlay
(
Image
(
systemName
:
"rectangle.slash"
))
}
}
else
{
case
.
failure
(
_
):
Color
.
gray
.
opacity
(
0.2
)
VStack
{
Image
(
systemName
:
"exclamationmark.triangle"
)
Text
(
"Image loading failed"
)
.
font
(
.
caption
)
}
.
frame
(
width
:
250
,
height
:
250
)
.
background
(
Color
.
gray
.
opacity
(
0.2
))
case
.
empty
:
ProgressView
()
ProgressView
()
.
frame
(
width
:
250
,
height
:
250
)
.
background
(
Color
.
gray
.
opacity
(
0.2
))
@unknown
default
:
EmptyView
()
}
}
}
}
else
{
Color
.
gray
.
opacity
(
0.2
)
.
frame
(
width
:
250
,
height
:
250
)
.
overlay
(
Text
(
"Invalid URL"
))
}
}
.
cornerRadius
(
16
)
.
frame
(
height
:
250
)
}
}
}
}
#Preview {
ChatUrlImageView
(
message
:
ChatMessage
(
shipId
:
10000003
,
messageId
:
"92c2dfb5-f5ed-4943-98a3-9848d7f9a962"
,
type
:
0
,
time
:
"2023-10-06T01:51:01.872Z"
,
location
:
1
,
from
:
"はだだ"
,
fromId
:
"487420489"
,
mode
:
0
,
message
:
"999"
,
stampId
:
0
,
viewer
:
[
Viewer
(
time
:
"2023-10-06T01:51:12.973Z"
,
location
:
1
,
id
:
""
),
Viewer
(
time
:
"2023-10-06T01:51:12.973Z"
,
location
:
2
,
id
:
""
)
]
))
}
Seilassist/Sailassist/Chat/View/ChatUrlVideoView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
import
AVKit
struct
ChatUrlVideoView
:
View
{
@State
private
var
player
:
AVPlayer
?
@State
private
var
isPlaying
=
false
let
imageUrl
:
String
var
onLoad
:
(()
->
Void
)?
=
nil
var
body
:
some
View
{
ZStack
{
if
let
url
=
URL
(
string
:
imageUrl
)
{
VideoPlayer
(
player
:
player
)
.
frame
(
height
:
250
)
.
cornerRadius
(
16
)
.
onAppear
{
player
=
AVPlayer
(
url
:
url
)
onLoad
?()
}
.
onTapGesture
{
if
isPlaying
{
player
?
.
pause
()
}
else
{
player
?
.
play
()
}
isPlaying
.
toggle
()
}
}
else
{
Color
.
gray
.
opacity
(
0.2
)
.
frame
(
height
:
250
)
.
overlay
(
VStack
{
Image
(
systemName
:
"video.slash"
)
Text
(
"Video URL is invalid"
)
.
font
(
.
caption
)
}
)
}
}
}
}
Seilassist/Sailassist/Chat/ChatView.swift
→
Seilassist/Sailassist/Chat/
View/
ChatView.swift
View file @
8cec0939
...
@@ -13,6 +13,10 @@ struct ChatView: View {
...
@@ -13,6 +13,10 @@ struct ChatView: View {
@State
var
isShowMember
:
Bool
=
false
@State
var
isShowMember
:
Bool
=
false
@State
var
isNotification
=
Preferences
.
ChatNotification
@State
var
isNotification
=
Preferences
.
ChatNotification
@State
var
isFocus
:
Bool
=
true
@State
var
isFocus
:
Bool
=
true
@State
private
var
loadedMediaCount
:
Int
=
0
@State
private
var
totalMediaCount
:
Int
=
0
@State
private
var
isMediaLoading
:
Bool
=
false
@State
private
var
isUploadingDialogPresented
:
Bool
=
false
var
body
:
some
View
{
var
body
:
some
View
{
ZStack
{
ZStack
{
...
@@ -30,12 +34,12 @@ struct ChatView: View {
...
@@ -30,12 +34,12 @@ struct ChatView: View {
ForEach
(
message
.
messages
,
id
:
\
.
messageId
)
{
msg
in
ForEach
(
message
.
messages
,
id
:
\
.
messageId
)
{
msg
in
if
msg
.
message
!=
nil
{
if
msg
.
message
!=
nil
{
if
msg
.
from
==
Preferences
.
UserName
{
if
msg
.
from
==
Preferences
.
UserName
{
//自分のメッセージ
//
MARK: -
自分のメッセージ
MyChatContentView
(
message
:
msg
)
MyChatContentView
(
message
:
msg
,
onMediaLoaded
:
{
handleMediaLoaded
(
proxy
:
proxy
)}
)
.
padding
(
.
bottom
,
24
)
.
padding
(
.
bottom
,
24
)
}
else
{
}
else
{
//他人のメッセージ
//
MARK: -
他人のメッセージ
OtherChatContentView
(
message
:
msg
)
OtherChatContentView
(
message
:
msg
,
onMediaLoaded
:
{
handleMediaLoaded
(
proxy
:
proxy
)}
)
.
padding
(
.
bottom
,
24
)
.
padding
(
.
bottom
,
24
)
}
}
}
else
{
}
else
{
...
@@ -48,17 +52,23 @@ struct ChatView: View {
...
@@ -48,17 +52,23 @@ struct ChatView: View {
.
onAppear
{
.
onAppear
{
guard
!
message
.
messages
.
isEmpty
,
guard
!
message
.
messages
.
isEmpty
,
let
id
=
message
.
messages
.
last
?
.
messageId
else
{
return
}
let
id
=
message
.
messages
.
last
?
.
messageId
else
{
return
}
totalMediaCount
=
message
.
messages
.
reduce
(
0
)
{
count
,
msg
in
count
+
(
msg
.
type
>=
2
?
1
:
0
)
}
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.2
)
{
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
DispatchQueue
.
main
.
async
{
message
.
viewCnt
=
0
}
}
}
}
.
onChange
(
of
:
message
.
messages
.
count
)
{
newValue
in
.
onChange
(
of
:
message
.
messages
.
count
)
{
newValue
in
print
(
debug
:
"ChatView: onChange { newValue:
\(
newValue
)
"
)
withAnimation
{
withAnimation
{
if
let
id
=
message
.
messages
.
last
?
.
messageId
{
if
let
id
=
message
.
messages
.
last
?
.
messageId
{
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.2
)
{
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
//
Warningモードは既読は返さない
//MARK: -
Warningモードは既読は返さない
if
message
.
messages
.
last
?
.
message
!=
nil
{
if
message
.
messages
.
last
?
.
message
!=
nil
{
if
message
.
messages
.
last
?
.
from
!=
Preferences
.
UserName
{
if
message
.
messages
.
last
?
.
from
!=
Preferences
.
UserName
{
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.5
)
{
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.5
)
{
...
@@ -67,8 +77,7 @@ struct ChatView: View {
...
@@ -67,8 +77,7 @@ struct ChatView: View {
}
}
}
}
}
}
}
message
.
viewCnt
=
0
}
}
}
}
}
}
...
@@ -77,7 +86,6 @@ struct ChatView: View {
...
@@ -77,7 +86,6 @@ struct ChatView: View {
guard
!
value
.
isEmpty
else
{
return
}
guard
!
value
.
isEmpty
else
{
return
}
if
let
id
=
message
.
messages
.
last
?
.
messageId
{
if
let
id
=
message
.
messages
.
last
?
.
messageId
{
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
message
.
viewCnt
=
0
}
}
}
}
}
}
...
@@ -116,19 +124,80 @@ struct ChatView: View {
...
@@ -116,19 +124,80 @@ struct ChatView: View {
.
onTapGesture
{
.
onTapGesture
{
isFocus
=
false
isFocus
=
false
}
}
ChatInputView
(
isFocus
:
$
isFocus
)
.
onAppear
{
//MARK: - 画面が表示された時に実行 既読処理
if
message
.
viewCnt
>
0
{
message
.
viewCnt
=
0
}
}
ChatInputView
(
isFocus
:
$
isFocus
,
isUploadingDialogPresented
:
$
isUploadingDialogPresented
)
}
if
!
isMediaLoading
{
LoadingView
()
}
if
isUploadingDialogPresented
{
// UpLoadingView()
}
}
}
}
.
background
(
ColorSet
.
BackgroundPrimary
.
color
)
.
background
(
ColorSet
.
BackgroundPrimary
.
color
)
}
}
//MARK: - 動画・画像読み込み完了後に最新チャットに移動
private
func
handleMediaLoaded
(
proxy
:
ScrollViewProxy
)
{
guard
!
message
.
messages
.
isEmpty
,
let
id
=
message
.
messages
.
last
?
.
messageId
else
{
return
}
loadedMediaCount
+=
1
if
loadedMediaCount
>=
totalMediaCount
{
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
0.2
)
{
proxy
.
scrollTo
(
id
,
anchor
:
.
bottom
)
isMediaLoading
=
true
}
}
}
}
// MARK: - 全てのチャットのローディング画面
struct
LoadingView
:
View
{
var
body
:
some
View
{
ZStack
{
Color
.
black
.
opacity
(
1.0
)
.
edgesIgnoringSafeArea
(
.
all
)
ProgressView
(
"Loading..."
)
.
progressViewStyle
(
CircularProgressViewStyle
(
tint
:
.
white
))
.
foregroundColor
(
.
white
)
.
padding
()
.
background
(
Color
.
gray
.
opacity
(
0.7
))
.
cornerRadius
(
10
)
}
}
}
// MARK: - 動画・画像のアップロード画面
struct
UpLoadingView
:
View
{
var
body
:
some
View
{
ZStack
{
Color
.
black
.
opacity
(
0.8
)
.
edgesIgnoringSafeArea
(
.
all
)
ProgressView
(
"UpLoading..."
)
.
progressViewStyle
(
CircularProgressViewStyle
(
tint
:
.
white
))
.
foregroundColor
(
.
white
)
.
padding
()
.
background
(
Color
.
gray
.
opacity
(
0.7
))
.
cornerRadius
(
10
)
}
}
}
}
struct
AlertChatMessage
:
View
{
struct
AlertChatMessage
:
View
{
@Environment(\
.colorScheme)
var
colorScheme
@Environment(\.colorScheme)
var
colorScheme
var
message
:
ChatMessage
var
message
:
ChatMessage
var
body
:
some
View
{
var
body
:
some
View
{
if
message
.
mode
==
ChatMode
.
warningProgress
.
rawValue
{
switch
message
.
mode
{
case
ChatMode
.
warningProgress
.
rawValue
:
HStack
()
{
HStack
()
{
Rectangle
()
Rectangle
()
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
fill
(
ColorSet
.
ChatDate
.
color
)
...
@@ -140,7 +209,7 @@ struct AlertChatMessage: View {
...
@@ -140,7 +209,7 @@ struct AlertChatMessage: View {
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
frame
(
width
:
20
,
height
:
1
)
.
frame
(
width
:
20
,
height
:
1
)
}
}
}
else
{
case
ChatMode
.
normal
.
rawValue
:
HStack
()
{
HStack
()
{
Rectangle
()
Rectangle
()
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
fill
(
ColorSet
.
ChatDate
.
color
)
...
@@ -152,6 +221,8 @@ struct AlertChatMessage: View {
...
@@ -152,6 +221,8 @@ struct AlertChatMessage: View {
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
fill
(
ColorSet
.
ChatDate
.
color
)
.
frame
(
width
:
20
,
height
:
1
)
.
frame
(
width
:
20
,
height
:
1
)
}
}
default
:
EmptyView
()
}
}
}
}
}
}
...
...
Seilassist/Sailassist/Chat/View/CustomCornerRadius.swift
View file @
8cec0939
...
@@ -46,7 +46,6 @@ enum Corners{
...
@@ -46,7 +46,6 @@ enum Corners{
case
br
case
br
}
}
fileprivate
struct
CustomCornerRadiusModifier
:
ViewModifier
{
fileprivate
struct
CustomCornerRadiusModifier
:
ViewModifier
{
let
cornerRadius
:
CGFloat
let
cornerRadius
:
CGFloat
let
corners
:
[
Corners
]
let
corners
:
[
Corners
]
...
...
Seilassist/Sailassist/Chat/View/MyChatContentView.swift
View file @
8cec0939
...
@@ -9,14 +9,18 @@ import SwiftUI
...
@@ -9,14 +9,18 @@ import SwiftUI
struct
MyChatContentView
:
View
{
struct
MyChatContentView
:
View
{
var
message
:
ChatMessage
var
message
:
ChatMessage
var
onMediaLoaded
:
(()
->
Void
)?
=
nil
var
body
:
some
View
{
var
body
:
some
View
{
HStack
{
HStack
{
Spacer
()
Spacer
()
VStack
(
alignment
:
.
trailing
,
spacing
:
6
)
{
VStack
(
alignment
:
.
trailing
,
spacing
:
6
)
{
Group
{
Group
{
if
let
msg
=
message
.
message
{
if
let
msg
=
message
.
message
{
if
msg
.
contains
(
"https://"
)
{
if
msg
.
contains
(
".jpg"
)
||
msg
.
contains
(
".png"
)
{
ChatUrlImageView
(
message
:
message
)
ChatUrlImageView
(
imageUrl
:
msg
,
onLoad
:
onMediaLoaded
)
}
else
if
msg
.
contains
(
".mp4"
)
||
msg
.
contains
(
".mov"
)
{
ChatUrlVideoView
(
imageUrl
:
msg
,
onLoad
:
onMediaLoaded
)
}
else
{
}
else
{
Text
(
msg
)
Text
(
msg
)
.
font
(
FontStyle
.
DefaultText
.
font
)
.
font
(
FontStyle
.
DefaultText
.
font
)
...
@@ -36,7 +40,7 @@ struct MyChatContentView: View {
...
@@ -36,7 +40,7 @@ struct MyChatContentView: View {
}
}
HStack
(
spacing
:
5
){
HStack
(
spacing
:
5
){
//既読マーク
//
MARK: -
既読マーク
Text
(
DateTextLib
.
ISO86012FormatText
(
message
.
time
,
format
:
"yyyy/MM/dd HH:mm"
,
errFormat
:
""
))
Text
(
DateTextLib
.
ISO86012FormatText
(
message
.
time
,
format
:
"yyyy/MM/dd HH:mm"
,
errFormat
:
""
))
.
padding
(
.
trailing
,
8
)
.
padding
(
.
trailing
,
8
)
...
...
Seilassist/Sailassist/Chat/View/OtherChatContentView.swift
View file @
8cec0939
...
@@ -9,6 +9,8 @@ import SwiftUI
...
@@ -9,6 +9,8 @@ import SwiftUI
struct
OtherChatContentView
:
View
{
struct
OtherChatContentView
:
View
{
var
message
:
ChatMessage
var
message
:
ChatMessage
var
onMediaLoaded
:
(()
->
Void
)?
=
nil
var
body
:
some
View
{
var
body
:
some
View
{
HStack
{
HStack
{
VStack
(
alignment
:
.
leading
,
spacing
:
4
)
{
VStack
(
alignment
:
.
leading
,
spacing
:
4
)
{
...
@@ -20,8 +22,10 @@ struct OtherChatContentView: View {
...
@@ -20,8 +22,10 @@ struct OtherChatContentView: View {
VStack
(
alignment
:
.
leading
,
spacing
:
10
)
{
VStack
(
alignment
:
.
leading
,
spacing
:
10
)
{
Group
{
Group
{
if
let
msg
=
message
.
message
{
if
let
msg
=
message
.
message
{
if
msg
.
contains
(
"https://"
)
{
if
msg
.
contains
(
".jpg"
)
||
msg
.
contains
(
".png"
)
{
ChatUrlImageView
(
message
:
message
)
ChatUrlImageView
(
imageUrl
:
msg
,
onLoad
:
onMediaLoaded
)
}
else
if
msg
.
contains
(
".mp4"
)
||
msg
.
contains
(
".mov"
)
{
ChatUrlVideoView
(
imageUrl
:
msg
,
onLoad
:
onMediaLoaded
)
}
else
{
}
else
{
Text
(
msg
)
Text
(
msg
)
.
font
(
FontStyle
.
DefaultText
.
font
)
.
font
(
FontStyle
.
DefaultText
.
font
)
...
...
Seilassist/Sailassist/Chat/ViewModel/ChatInputViewModel.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
import
Combine
import
Speech
enum
MediaInputType
{
case
none
case
camera
case
photoLibrary
case
fileImport
}
class
ChatInputViewModel
:
ObservableObject
{
@Published
var
failedUploadImage
:
ReqUploadImage
?
=
nil
@Published
var
isRetryDialogPresented
=
false
@Published
var
isSignalrRestart
=
false
@Published
var
isRecording
=
false
@Published
var
isChatAlert
=
false
@Published
var
inputText
=
""
@Published
var
textViewHeight
:
CGFloat
=
40
@Published
var
tempId
:
String
=
""
@Published
var
mediaInputType
:
MediaInputType
=
.
none
@Published
var
isImagePickerPresented
=
false
@Published
var
isFileImporterPresented
=
false
@Published
var
isKeyboardFocused
:
Bool
=
false
@Published
var
isUploadingDialogPresented
:
Bool
=
false
@Published
var
isFocus
:
Double
=
0.0
private
let
sessionUploadImage
=
SessionUploadImage
()
//MARK: - メディア入力
func
handleMediaInput
(
type
:
MediaInputType
)
{
self
.
mediaInputType
=
type
if
type
==
.
camera
||
type
==
.
photoLibrary
{
self
.
isImagePickerPresented
=
true
}
else
if
type
==
.
fileImport
{
self
.
isFileImporterPresented
=
true
}
}
// MARK: - Send Chat Image
func
sendChatImage
(
_
uploadImage
:
ReqUploadImage
)
{
Task
{
do
{
isUploadingDialogPresented
=
true
let
response
=
try
await
sessionUploadImage
.
requestUploadImage
(
uploadImage
)
print
(
debug
:
"Upload success:
\(
response
)
"
)
let
serverSession
=
ServerSession
()
_
=
serverSession
.
fromJSON
(
resultData
:
response
,
resltType
:
ResLogin
.
self
)
sessionUploadImage
.
progress
=
0.0
// 完了後リセット
isUploadingDialogPresented
=
false
}
catch
{
print
(
debug
:
"Upload failed:
\(
error
)
"
)
sessionUploadImage
.
progress
=
0.0
// エラー時もリセット
isUploadingDialogPresented
=
false
failedUploadImage
=
uploadImage
isRetryDialogPresented
=
true
}
}
}
// MARK: - Send Chat Message
func
sendChatMessage
()
{
guard
!
SharingData
.
message
.
sendInf
else
{
return
}
isRecording
=
false
SignalR
()
.
chatMessage
(
message
:
inputText
,
completion
:
responseChatMessage
)
SharingData
.
message
.
sendInf
=
true
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
3.0
)
{
self
.
restartChatMessage
()
}
}
// MARK: - Chat Response
private
func
responseChatMessage
(
error
:
Error
?)
{
SharingData
.
message
.
sendInf
=
false
if
let
error
=
error
{
print
(
"Chat error:
\(
error
)
"
)
isChatAlert
=
true
}
else
{
isKeyboardFocused
=
false
inputText
=
""
}
}
// MARK: - Restart Chat if No Response
private
func
restartChatMessage
()
{
if
SharingData
.
message
.
sendInf
{
SignalR
()
.
stopConnection
()
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
1.0
)
{
self
.
isSignalrRestart
=
true
}
}
}
//MARK: - テキスト高さ
func
recalculateHeight
()
{
let
textView
=
UITextView
()
textView
.
text
=
inputText
textView
.
font
=
UIFont
.
preferredFont
(
forTextStyle
:
.
body
)
let
fixedWidth
=
UIScreen
.
main
.
bounds
.
width
-
40
// 適宜調整
let
newSize
=
textView
.
sizeThatFits
(
CGSize
(
width
:
fixedWidth
,
height
:
CGFloat
.
greatestFiniteMagnitude
))
textViewHeight
=
max
(
newSize
.
height
,
40
)
// 最低高さを40に設定
}
// MARK: - ローカル画像を一時保存してURLを取得
func
saveImageToTemporaryDirectory
(
_
image
:
UIImage
)
->
String
?
{
let
fileName
=
UUID
()
.
uuidString
+
".jpg"
let
tempDir
=
FileManager
.
default
.
temporaryDirectory
let
fileURL
=
tempDir
.
appendingPathComponent
(
fileName
)
guard
let
data
=
image
.
jpegData
(
compressionQuality
:
0.8
)
else
{
print
(
"Image JPEG conversion failed"
)
return
nil
}
do
{
try
data
.
write
(
to
:
fileURL
)
return
fileURL
.
absoluteString
}
catch
{
print
(
"Image JPEG conversion failed:
\(
error
.
localizedDescription
)
"
)
return
nil
}
}
//MARK: - 送信前に送信画像をテンポラリに入れる
func
sendImageToTemporary
(
_
image
:
UIImage
)
{
if
let
localURL
=
saveImageToTemporaryDirectory
(
image
),
let
jpegData
=
image
.
jpegData
(
compressionQuality
:
1.0
)
{
tempId
=
UUID
()
.
uuidString
let
viewer
=
Viewer
(
time
:
DateTextLib
.
Date2ISO8601Text
(
Date
()),
location
:
2
,
id
:
""
,
name
:
""
)
let
tempMessage
=
ChatMessage
(
shipId
:
Preferences
.
shipId
,
messageId
:
tempId
,
type
:
2
,
time
:
DateTextLib
.
Date2ISO8601Text
(
Date
()),
location
:
2
,
from
:
Preferences
.
UserName
,
fromId
:
String
(
SharingData
.
my
.
id
),
mode
:
SharingData
.
message
.
mode
?
1
:
0
,
message
:
localURL
,
stampId
:
0
,
viewer
:
[
viewer
]
)
SharingData
.
message
.
messages
.
append
(
tempMessage
)
let
uploadImage
=
ReqUploadImage
(
shipId
:
Preferences
.
shipId
,
messageId
:
tempId
,
location
:
2
,
from
:
Preferences
.
UserName
,
fromId
:
String
(
SharingData
.
my
.
id
),
files
:
jpegData
)
sendChatImage
(
uploadImage
)
}
}
}
Seilassist/Sailassist/Chat/ViewModel/ChatViewModel.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
import
AVFoundation
@MainActor
class
ChatViewModel
:
ObservableObject
{
@Published
var
messages
:
[
ChatMessage
]
=
[]
@Published
var
isMediaPrefetched
:
Bool
=
false
func
loadMessages
()
{
messages
=
SharingData
.
message
.
messages
prefetchMedia
()
}
private
func
prefetchMedia
()
{
let
imageURLs
=
messages
.
filter
{
$0
.
type
==
2
&&
$0
.
message
!=
nil
}
.
compactMap
{
$0
.
message
}
.
compactMap
{
URL
(
string
:
$0
)
}
let
videoURLs
=
messages
.
filter
{
$0
.
type
==
3
&&
$0
.
message
!=
nil
}
.
compactMap
{
$0
.
message
}
.
compactMap
{
URL
(
string
:
$0
)
}
Task
{
await
withTaskGroup
(
of
:
Void
.
self
)
{
group
in
for
url
in
imageURLs
{
group
.
addTask
{
let
request
=
URLRequest
(
url
:
url
,
cachePolicy
:
.
returnCacheDataElseLoad
,
timeoutInterval
:
10
)
_
=
try
?
await
URLSession
.
shared
.
data
(
for
:
request
)
}
}
for
url
in
videoURLs
{
group
.
addTask
{
let
asset
=
AVURLAsset
(
url
:
url
)
do
{
_
=
try
await
asset
.
load
(
.
isPlayable
)
}
catch
{
print
(
debug
:
"Video loading failure:
\(
url
)
"
)
}
}
}
}
isMediaPrefetched
=
true
}
}
}
Seilassist/Sailassist/ContentView.swift
View file @
8cec0939
...
@@ -14,6 +14,7 @@ class LoginViewModel: ObservableObject{
...
@@ -14,6 +14,7 @@ class LoginViewModel: ObservableObject{
struct
ContentView
:
View
{
struct
ContentView
:
View
{
@StateObject
private
var
loginViewModel
=
LoginViewModel
()
@StateObject
private
var
loginViewModel
=
LoginViewModel
()
@EnvironmentObject
private
var
sceneDelegate
:
SceneDelegate
@EnvironmentObject
private
var
sceneDelegate
:
SceneDelegate
@StateObject
private
var
chatViewModel
=
ChatViewModel
()
let
selectedTabModel
=
SelectedTabModel
()
let
selectedTabModel
=
SelectedTabModel
()
...
@@ -38,6 +39,7 @@ struct ContentView: View {
...
@@ -38,6 +39,7 @@ struct ContentView: View {
},
content
:
{
},
content
:
{
LoginView
(
isLogin
:
$
loginViewModel
.
isLogin
)
LoginView
(
isLogin
:
$
loginViewModel
.
isLogin
)
.
environmentObject
(
selectedTabModel
)
.
environmentObject
(
selectedTabModel
)
.
environmentObject
(
chatViewModel
)
})
})
}
}
}
}
...
...
Seilassist/Sailassist/Location/LocationCalculation.swift
View file @
8cec0939
...
@@ -908,9 +908,9 @@ class LocationCalculation{
...
@@ -908,9 +908,9 @@ class LocationCalculation{
distance
=
rtn
.
xte
*
-
1
distance
=
rtn
.
xte
*
-
1
}
}
if
let
dist
=
distance
{
//
if let dist = distance {
// print(debug: "checkPolyline \(dist)")
// print(debug: "checkPolyline \(dist)")
}
//
}
return
distance
return
distance
}
}
...
...
Seilassist/Sailassist/Login/LoginView.swift
View file @
8cec0939
...
@@ -37,6 +37,7 @@ struct LoginView: View {
...
@@ -37,6 +37,7 @@ struct LoginView: View {
@ObservedObject
var
scannerViewModel
=
ScannerViewModel
()
@ObservedObject
var
scannerViewModel
=
ScannerViewModel
()
@EnvironmentObject
var
locationViewModel
:
LocationViewModel
@EnvironmentObject
var
locationViewModel
:
LocationViewModel
@EnvironmentObject
var
selectedTabModel
:
SelectedTabModel
@EnvironmentObject
var
selectedTabModel
:
SelectedTabModel
@EnvironmentObject
var
chatViewModel
:
ChatViewModel
@Binding
var
isLogin
:
Bool
@Binding
var
isLogin
:
Bool
@State
var
isQrRead
:
Bool
=
false
@State
var
isQrRead
:
Bool
=
false
@State
var
viewMode
:
LoginViewMode
=
.
SelectType
@State
var
viewMode
:
LoginViewMode
=
.
SelectType
...
@@ -153,9 +154,7 @@ struct LoginView: View {
...
@@ -153,9 +154,7 @@ struct LoginView: View {
}
}
}
}
/**
//MARK: - QRコードでのログイン
* QRコードでのログイン
*/
func
LoginCheckQR
()
->
()
{
func
LoginCheckQR
()
->
()
{
isProgressView
=
true
isProgressView
=
true
loginViewParam
.
shipId
=
Preferences
.
Id
loginViewParam
.
shipId
=
Preferences
.
Id
...
@@ -167,9 +166,7 @@ struct LoginView: View {
...
@@ -167,9 +166,7 @@ struct LoginView: View {
isProgressView
=
false
isProgressView
=
false
}
}
/**
//MARK: - Autoログイン
* Autoログイン
*/
func
LoginCheck
()
->
()
{
func
LoginCheck
()
->
()
{
let
lastUnixTime
=
Preferences
.
lastLoginDate_Int64
??
0
let
lastUnixTime
=
Preferences
.
lastLoginDate_Int64
??
0
let
lastDate
=
DateTextLib
.
UnixTime2Date
(
lastUnixTime
)
let
lastDate
=
DateTextLib
.
UnixTime2Date
(
lastUnixTime
)
...
@@ -211,6 +208,7 @@ struct LoginView: View {
...
@@ -211,6 +208,7 @@ struct LoginView: View {
isLogin
=
true
isLogin
=
true
let
message
=
GetMessage
()
let
message
=
GetMessage
()
message
.
chatViewModel
=
chatViewModel
message
.
start
()
message
.
start
()
timer
=
Timer
.
scheduledTimer
(
withTimeInterval
:
TimerInterval
,
repeats
:
true
)
{
_
in
timer
=
Timer
.
scheduledTimer
(
withTimeInterval
:
TimerInterval
,
repeats
:
true
)
{
_
in
...
@@ -269,4 +267,5 @@ fileprivate struct Triangle: Shape{
...
@@ -269,4 +267,5 @@ fileprivate struct Triangle: Shape{
#Preview {
#Preview {
LoginView
(
isLogin
:
.
constant
(
false
))
LoginView
(
isLogin
:
.
constant
(
false
))
.
environmentObject
(
SelectedTabModel
())
.
environmentObject
(
SelectedTabModel
())
.
environmentObject
(
ChatViewModel
())
}
}
Seilassist/Sailassist/Map/MapRepresentable.swift
View file @
8cec0939
...
@@ -63,7 +63,7 @@ struct MapRepresentable: UIViewControllerRepresentable {
...
@@ -63,7 +63,7 @@ struct MapRepresentable: UIViewControllerRepresentable {
//MARK: - 自船を画面中央に表示
//MARK: - 自船を画面中央に表示
if
location
.
focusOwnShip
{
if
location
.
focusOwnShip
{
mapVC
.
updateCamera
(
location
:
location
.
location
,
zoomlevel
:
10
.0
)
mapVC
.
updateCamera
(
location
:
location
.
location
,
zoomlevel
:
5
.0
)
}
}
if
let
mylocation
=
location
.
location
{
if
let
mylocation
=
location
.
location
{
...
@@ -86,8 +86,10 @@ struct MapRepresentable: UIViewControllerRepresentable {
...
@@ -86,8 +86,10 @@ struct MapRepresentable: UIViewControllerRepresentable {
if
SharingData
.
nga
.
editType
==
EditNgaType
.
registered
{
if
SharingData
.
nga
.
editType
==
EditNgaType
.
registered
{
mapVC
.
updateEditArea
(
remove
:
true
)
mapVC
.
updateEditArea
(
remove
:
true
)
DispatchQueue
.
main
.
async
{
SharingData
.
nga
.
editType
=
EditNgaType
.
nonEdit
SharingData
.
nga
.
editType
=
EditNgaType
.
nonEdit
}
}
}
if
SharingData
.
nga
.
editType
==
EditNgaType
.
deletePoint
{
if
SharingData
.
nga
.
editType
==
EditNgaType
.
deletePoint
{
mapVC
.
updateEditArea
(
remove
:
true
)
mapVC
.
updateEditArea
(
remove
:
true
)
...
...
Seilassist/Sailassist/Map/Task/View/EcaListMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
EcaListMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
var
body
:
some
View
{
HStack
{
Button
(
action
:
{
taskViewModel
.
viewMode
=
.
FuelSwitching
},
label
:
{
Image
(
"ink_02"
)
})
.
frame
(
width
:
48
,
height
:
48
)
Spacer
()
Text
(
TaskViewMode
.
EcaList
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
Button
(
action
:
{
},
label
:
{
})
.
frame
(
width
:
48
,
height
:
48
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
EcaListView
(
taskViewModel
:
taskViewModel
)
}
.
onChange
(
of
:
taskViewModel
.
viewMode
){
newViewMode
in
proxy
.
scrollTo
(
0
,
anchor
:
.
top
)
}
}
Spacer
()
.
frame
(
height
:
55
)
}
}
#Preview {
EcaListMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/Map/Task/View/EcaMenuView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
EcaMenuView
:
View
{
@ObservedObject
var
taskViewModel
:
TaskViewModel
@ObservedObject
var
ecaData
=
SharingData
.
eca
@Environment(\ .colorScheme)
var
colorScheme
@State
var
isDeleteAlert
:
Bool
=
false
let
deleteEcaArea
=
DeleteEcaArea
()
var
eca
:
RegisteredEca
var
body
:
some
View
{
Menu
{
Text
(
eca
.
name
)
Button
{
taskViewModel
.
edittingEcaArea
=
eca
taskViewModel
.
ecaName
=
eca
.
name
taskViewModel
.
viewMode
=
.
EcaSetting
}
label
:
{
Text
(
"Edit Notice Setting"
)
}
Button
{
taskViewModel
.
edittingEcaArea
=
eca
taskViewModel
.
ecaName
=
eca
.
name
isDeleteAlert
=
true
}
label
:
{
Text
(
"Delete ECA Task"
)
}
}
label
:
{
Image
(
systemName
:
"ellipsis"
)
.
frame
(
width
:
22
,
height
:
22
)
.
opacity
(
eca
.
isRunning
==
true
?
0.2
:
1.0
)
.
foregroundColor
(
colorScheme
==
.
light
?
.
black
:
.
white
)
}
.
disabled
(
eca
.
isRunning
)
.
alert
(
"Delete"
,
isPresented
:
$
isDeleteAlert
)
{
Button
(
"Yes"
)
{
if
let
ecaArea
=
taskViewModel
.
edittingEcaArea
{
var
newData
=
ecaArea
newData
.
isEnable
=
false
newData
.
status
=
EcaState
.
cancel
ecaData
.
editEcaArea
(
key
:
ecaArea
.
areaId
,
value
:
newData
,
type
:
EcaOperation
.
Delete
)
deleteEcaArea
.
start
(
ecaId
:
ecaArea
.
areaId
)
}
taskViewModel
.
edittingEcaArea
=
nil
}
Button
(
"No"
){}
}
message
:
{
Text
(
"Have you finished "
+
taskViewModel
.
ecaName
+
" fuel switching?"
)
}
}
}
Seilassist/Sailassist/Map/Task/View/EcaSettingMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
EcaSettingMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
var
body
:
some
View
{
//タイトルエリア
HStack
{
Button
(
action
:
{
taskViewModel
.
viewMode
=
.
FuelSwitching
},
label
:
{
Image
(
"ink_02"
)
})
.
frame
(
width
:
48
,
height
:
48
)
Spacer
()
Text
(
TaskViewMode
.
EcaSetting
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
Button
(
action
:
{
},
label
:
{
})
.
frame
(
width
:
48
,
height
:
48
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
if
let
edittingEcaArea
=
taskViewModel
.
edittingEcaArea
{
EcaSettingView
(
taskViewModel
:
taskViewModel
,
edittingEca
:
edittingEcaArea
)
}
}
.
onChange
(
of
:
taskViewModel
.
viewMode
){
newViewMode
in
proxy
.
scrollTo
(
0
,
anchor
:
.
top
)
}
}
Spacer
()
.
frame
(
height
:
55
)
}
}
#Preview {
EcaSettingMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/Map/Task/View/FuelSwitchingMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
FuelSwitchingMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
var
body
:
some
View
{
HStack
{
Button
(
action
:
{
taskViewModel
.
viewMode
=
.
MenuList
},
label
:
{
Image
(
"ink_02"
)
})
.
frame
(
width
:
48
,
height
:
48
)
Spacer
()
Text
(
TaskViewMode
.
FuelSwitching
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
Button
(
action
:
{
},
label
:
{
})
.
frame
(
width
:
48
,
height
:
48
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
//ECAリスト
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
FuelSwitchingView
(
taskViewModel
:
taskViewModel
)
}
.
onChange
(
of
:
taskViewModel
.
viewMode
){
newViewMode
in
proxy
.
scrollTo
(
0
,
anchor
:
.
top
)
}
}
Spacer
()
.
frame
(
height
:
55
)
}
}
#Preview {
FuelSwitchingMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/Map/Task/View/FuelSwitchingView.swift
View file @
8cec0939
...
@@ -4,7 +4,6 @@
...
@@ -4,7 +4,6 @@
//
//
// Created by Mamoru Sugita on 2023/10/18.
// Created by Mamoru Sugita on 2023/10/18.
//
//
import
SwiftUI
import
SwiftUI
struct
FuelSwitchingView
:
View
{
struct
FuelSwitchingView
:
View
{
...
@@ -96,59 +95,6 @@ struct FuelSwitchingView: View {
...
@@ -96,59 +95,6 @@ struct FuelSwitchingView: View {
}
}
}
}
struct
EcaMenuView
:
View
{
@ObservedObject
var
taskViewModel
:
TaskViewModel
@ObservedObject
var
ecaData
=
SharingData
.
eca
@Environment(\ .colorScheme)
var
colorScheme
@State
var
isDeleteAlert
:
Bool
=
false
let
deleteEcaArea
=
DeleteEcaArea
()
var
eca
:
RegisteredEca
var
body
:
some
View
{
Menu
{
Text
(
eca
.
name
)
Button
{
taskViewModel
.
edittingEcaArea
=
eca
taskViewModel
.
ecaName
=
eca
.
name
taskViewModel
.
viewMode
=
.
EcaSetting
}
label
:
{
Text
(
"Edit Notice Setting"
)
}
Button
{
taskViewModel
.
edittingEcaArea
=
eca
taskViewModel
.
ecaName
=
eca
.
name
isDeleteAlert
=
true
}
label
:
{
Text
(
"Delete ECA Task"
)
}
}
label
:
{
Image
(
systemName
:
"ellipsis"
)
.
frame
(
width
:
22
,
height
:
22
)
.
opacity
(
eca
.
isRunning
==
true
?
0.2
:
1.0
)
.
foregroundColor
(
colorScheme
==
.
light
?
.
black
:
.
white
)
}
.
disabled
(
eca
.
isRunning
)
.
alert
(
"Delete"
,
isPresented
:
$
isDeleteAlert
)
{
Button
(
"Yes"
)
{
if
let
ecaArea
=
taskViewModel
.
edittingEcaArea
{
var
newData
=
ecaArea
newData
.
isEnable
=
false
newData
.
status
=
EcaState
.
cancel
ecaData
.
editEcaArea
(
key
:
ecaArea
.
areaId
,
value
:
newData
,
type
:
EcaOperation
.
Delete
)
deleteEcaArea
.
start
(
ecaId
:
ecaArea
.
areaId
)
}
taskViewModel
.
edittingEcaArea
=
nil
}
Button
(
"No"
){}
}
message
:
{
Text
(
"Have you finished "
+
taskViewModel
.
ecaName
+
" fuel switching?"
)
}
}
}
#Preview {
#Preview {
FuelSwitchingView
(
taskViewModel
:
TaskViewModel
())
FuelSwitchingView
(
taskViewModel
:
TaskViewModel
())
}
}
Seilassist/Sailassist/Map/Task/View/MenuMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
MenuMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
var
body
:
some
View
{
VStack
(
spacing
:
0
)
{
headerView
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
VStack
(
spacing
:
16
)
{
Color
.
clear
.
frame
(
height
:
1
)
.
id
(
"top"
)
MenuButton
(
title
:
TaskViewMode
.
FuelSwitching
.
title
,
isEnabled
:
SharingData
.
my
.
isFuelSwitchTask
,
action
:
{
taskViewModel
.
viewMode
=
.
FuelSwitching
}
)
MenuButton
(
title
:
TaskViewMode
.
NgaNotification
.
title
,
isEnabled
:
SharingData
.
my
.
isNga
,
action
:
{
taskViewModel
.
viewMode
=
.
NgaNotification
}
)
}
.
padding
(
.
top
,
16
)
}
.
onChange
(
of
:
taskViewModel
.
viewMode
)
{
_
in
withAnimation
{
proxy
.
scrollTo
(
"top"
,
anchor
:
.
top
)
}
}
}
Spacer
()
.
frame
(
height
:
55
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
}
private
var
headerView
:
some
View
{
HStack
{
Spacer
()
Text
(
TaskViewMode
.
MenuList
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
}
}
}
struct
MenuButton
:
View
{
let
title
:
String
let
isEnabled
:
Bool
let
action
:
()
->
Void
var
body
:
some
View
{
Button
(
action
:
action
)
{
Text
(
title
)
.
font
(
FontStyle
.
DefaultText
.
font
)
.
padding
()
.
frame
(
maxWidth
:
.
infinity
,
minHeight
:
42
)
}
.
foregroundColor
(
ColorSet
.
ButtonText
.
color
)
.
background
(
isEnabled
?
ColorSet
.
PrimaryActiveIcon
.
color
:
ColorSet
.
SecondaryDisable
.
color
)
.
cornerRadius
(
30
)
.
disabled
(
!
isEnabled
)
.
frame
(
width
:
153
)
}
}
#Preview {
MenuMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/Map/Task/View/MenuTaskView.swift
View file @
8cec0939
This diff is collapsed.
Click to expand it.
Seilassist/Sailassist/Map/Task/View/NgaMenuView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
NgaMenuView
:
View
{
@ObservedObject
var
taskViewModel
:
TaskViewModel
@ObservedObject
var
ngaData
=
SharingData
.
nga
@Environment(\ .colorScheme)
var
colorScheme
@State
var
isDelete
:
Bool
=
false
var
nga
:
RegisteredNga
var
body
:
some
View
{
Menu
{
Text
(
nga
.
name
)
Button
{
ngaData
.
editNga
=
nga
taskViewModel
.
viewMode
=
.
NgaSetting
ngaData
.
editType
=
EditNgaType
.
nonEdit
}
label
:
{
Text
(
"Edit NGA Setting"
)
}
//削除
Button
{
ngaData
.
editNga
=
nga
isDelete
=
true
}
label
:
{
Text
(
"Delete NGA Task"
)
}
.
disabled
(
nga
.
isLock
)
}
label
:
{
Image
(
systemName
:
"ellipsis"
)
.
frame
(
width
:
22
,
height
:
22
)
.
opacity
(
nga
.
isRunning
?
0.2
:
1.0
)
.
foregroundColor
(
colorScheme
==
.
light
?
.
black
:
.
white
)
}
.
disabled
(
nga
.
isRunning
)
.
alert
(
"Delete"
,
isPresented
:
$
isDelete
)
{
Button
(
"Yes"
)
{
if
let
newData
=
ngaData
.
editNga
{
ngaData
.
editNgaArea
(
value
:
newData
,
type
:
NgaOperation
.
Delete
)
}
}
Button
(
"No"
){}
}
message
:
{
if
let
editNga
=
ngaData
.
editNga
{
Text
(
"Delete "
+
editNga
.
name
+
" ?"
)
}
}
}
}
Seilassist/Sailassist/Map/Task/View/NgaNotificationMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
NgaNotificationMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
var
body
:
some
View
{
//タイトルエリア
HStack
{
Button
(
action
:
{
taskViewModel
.
viewMode
=
.
MenuList
},
label
:
{
Image
(
"ink_02"
)
})
.
frame
(
width
:
48
,
height
:
48
)
Spacer
()
Text
(
TaskViewMode
.
NgaNotification
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
Button
(
action
:
{
},
label
:
{
})
.
frame
(
width
:
48
,
height
:
48
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
NgaNotificationView
(
taskViewModel
:
taskViewModel
)
}
.
onChange
(
of
:
taskViewModel
.
viewMode
){
newViewMode
in
proxy
.
scrollTo
(
0
,
anchor
:
.
top
)
}
}
Spacer
()
.
frame
(
height
:
55
)
}
}
#Preview {
NgaNotificationMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/Map/Task/View/NgaNotificationView.swift
View file @
8cec0939
...
@@ -4,7 +4,6 @@
...
@@ -4,7 +4,6 @@
//
//
// Created by Mamoru Sugita on 2023/10/18.
// Created by Mamoru Sugita on 2023/10/18.
//
//
import
SwiftUI
import
SwiftUI
struct
NgaNotificationView
:
View
{
struct
NgaNotificationView
:
View
{
...
@@ -87,54 +86,6 @@ struct NgaNotificationView: View {
...
@@ -87,54 +86,6 @@ struct NgaNotificationView: View {
}
}
}
}
struct
NgaMenuView
:
View
{
@ObservedObject
var
taskViewModel
:
TaskViewModel
@ObservedObject
var
ngaData
=
SharingData
.
nga
@Environment(\ .colorScheme)
var
colorScheme
@State
var
isDelete
:
Bool
=
false
var
nga
:
RegisteredNga
var
body
:
some
View
{
Menu
{
Text
(
nga
.
name
)
Button
{
ngaData
.
editNga
=
nga
taskViewModel
.
viewMode
=
.
NgaSetting
ngaData
.
editType
=
EditNgaType
.
nonEdit
}
label
:
{
Text
(
"Edit NGA Setting"
)
}
//削除
Button
{
ngaData
.
editNga
=
nga
isDelete
=
true
}
label
:
{
Text
(
"Delete NGA Task"
)
}
.
disabled
(
nga
.
isLock
)
}
label
:
{
Image
(
systemName
:
"ellipsis"
)
.
frame
(
width
:
22
,
height
:
22
)
.
opacity
(
nga
.
isRunning
?
0.2
:
1.0
)
.
foregroundColor
(
colorScheme
==
.
light
?
.
black
:
.
white
)
}
.
disabled
(
nga
.
isRunning
)
.
alert
(
"Delete"
,
isPresented
:
$
isDelete
)
{
Button
(
"Yes"
)
{
if
let
newData
=
ngaData
.
editNga
{
ngaData
.
editNgaArea
(
value
:
newData
,
type
:
NgaOperation
.
Delete
)
}
}
Button
(
"No"
){}
}
message
:
{
if
let
editNga
=
ngaData
.
editNga
{
Text
(
"Delete "
+
editNga
.
name
+
" ?"
)
}
}
}
}
#Preview {
#Preview {
NgaNotificationView
(
taskViewModel
:
TaskViewModel
())
NgaNotificationView
(
taskViewModel
:
TaskViewModel
())
}
}
Seilassist/Sailassist/Map/Task/View/NgaSettingMainView.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
NgaSettingMainView
:
View
{
@ObservedObject
var
taskViewModel
=
TaskViewModel
()
@ObservedObject
var
nga
=
SharingData
.
nga
@ObservedObject
var
map
=
SharingData
.
map
var
body
:
some
View
{
//タイトルエリア
HStack
{
Button
(
action
:
{
if
taskViewModel
.
viewMode
==
.
NgaSetting
&&
nga
.
editType
==
EditNgaType
.
movePoint
{
nga
.
editNga
=
nga
.
moveNga
nga
.
editType
=
EditNgaType
.
addPoint
map
.
isMapFree
=
true
}
else
{
taskViewModel
.
viewMode
=
.
NgaNotification
nga
.
editType
=
EditNgaType
.
registered
}
},
label
:
{
Image
(
"ink_02"
)
})
.
frame
(
width
:
48
,
height
:
48
)
Spacer
()
Text
(
TaskViewMode
.
NgaSetting
.
title
)
.
font
(
FontStyle
.
TitleL
.
font
)
.
frame
(
height
:
20
)
.
padding
(
.
vertical
,
14
)
Spacer
()
Button
(
action
:
{
},
label
:
{
})
.
frame
(
width
:
48
,
height
:
48
)
}
.
padding
(
EdgeInsets
(
top
:
10
,
leading
:
8
,
bottom
:
13
,
trailing
:
17
))
Divider
()
.
background
(
ColorSet
.
LineColor03
.
color
)
ScrollViewReader
{
proxy
in
ScrollView
(
.
vertical
){
NgaSettingView
(
taskViewModel
:
taskViewModel
)
}
.
onChange
(
of
:
taskViewModel
.
viewMode
){
newViewMode
in
proxy
.
scrollTo
(
0
,
anchor
:
.
top
)
}
}
Spacer
()
.
frame
(
height
:
55
)
}
}
#Preview {
NgaSettingMainView
(
taskViewModel
:
TaskViewModel
())
}
Seilassist/Sailassist/SailassistApp.swift
View file @
8cec0939
...
@@ -84,6 +84,7 @@ var connection: HubConnection?
...
@@ -84,6 +84,7 @@ var connection: HubConnection?
class
AppDelegate
:
NSObject
,
UIApplicationDelegate
,
MSNotificationHubDelegate
,
MSInstallationLifecycleDelegate
{
class
AppDelegate
:
NSObject
,
UIApplicationDelegate
,
MSNotificationHubDelegate
,
MSInstallationLifecycleDelegate
{
@ObservedObject
var
msg
=
SharingData
.
message
@ObservedObject
var
msg
=
SharingData
.
message
private
var
hubConnectionDelegate
:
HubConnectionDelegate
?
private
var
hubConnectionDelegate
:
HubConnectionDelegate
?
private
var
isReconnecting
=
false
// 再接続中かどうかを管理するフラグ
func
application
(
_
application
:
UIApplication
,
didFinishLaunchingWithOptions
launchOptions
:
[
UIApplication
.
LaunchOptionsKey
:
Any
]?
=
nil
)
->
Bool
{
func
application
(
_
application
:
UIApplication
,
didFinishLaunchingWithOptions
launchOptions
:
[
UIApplication
.
LaunchOptionsKey
:
Any
]?
=
nil
)
->
Bool
{
print
(
debug
:
"called"
)
print
(
debug
:
"called"
)
...
@@ -105,7 +106,7 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -105,7 +106,7 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
application
.
registerForRemoteNotifications
()
application
.
registerForRemoteNotifications
()
}
}
//ユニークな値の生成
//
MARK: -
ユニークな値の生成
if
Preferences
.
DeviceId
==
""
{
if
Preferences
.
DeviceId
==
""
{
Preferences
.
DeviceId
=
UUID
()
.
uuidString
Preferences
.
DeviceId
=
UUID
()
.
uuidString
}
}
...
@@ -117,32 +118,37 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -117,32 +118,37 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
.
withAutoReconnect
()
.
withAutoReconnect
()
// .withLogging(minLogLevel: .error)
// .withLogging(minLogLevel: .error)
.
withLogging
(
minLogLevel
:
.
debug
)
.
withLogging
(
minLogLevel
:
.
debug
)
.
withHubConnectionOptions
(
configureHubConnectionOptions
:
{
options
in
options
.
keepAliveInterval
=
3
0
})
.
withHubConnectionOptions
(
configureHubConnectionOptions
:
{
options
in
options
.
keepAliveInterval
=
2
0
})
.
build
()
.
build
()
if
let
r_connection
=
connection
{
if
let
r_connection
=
connection
{
//Chat
setupSignalRHandlers
(
connection
:
r_connection
)
r_connection
.
on
(
method
:
"ChatMessage"
,
callback
:
{
(
message
:
ResChatMessage
)
in
r_connection
.
start
()
}
return
true
}
private
func
setupSignalRHandlers
(
connection
:
HubConnection
)
{
// Chatメッセージ処理
connection
.
on
(
method
:
"ChatMessage"
,
callback
:
{
(
message
:
ResChatMessage
)
in
self
.
handleChatMessage
(
message
:
message
)
self
.
handleChatMessage
(
message
:
message
)
})
})
//
Photo / Image
//
Photo / Image
r_
connection
.
on
(
method
:
"chatMessage"
,
callback
:
{
(
message
:
ResChatMessage
)
in
connection
.
on
(
method
:
"chatMessage"
,
callback
:
{
(
message
:
ResChatMessage
)
in
self
.
handleChatMessage
(
message
:
message
)
self
.
handleChatMessage
(
message
:
message
)
})
})
r_connection
.
on
(
method
:
"AckMessage"
,
callback
:
{
(
message
:
ResAckMessage
)
in
// Ackメッセージ処理
connection
.
on
(
method
:
"AckMessage"
,
callback
:
{
(
message
:
ResAckMessage
)
in
self
.
handleAckMessage
(
message
:
message
)
self
.
handleAckMessage
(
message
:
message
)
})
})
r_connection
.
on
(
method
:
"ChatMode"
,
callback
:
{
(
message
:
ResChatMode
)
in
// Chatモード処理
connection
.
on
(
method
:
"ChatMode"
,
callback
:
{
(
message
:
ResChatMode
)
in
self
.
handleChatMode
(
message
:
message
)
self
.
handleChatMode
(
message
:
message
)
})
})
r_connection
.
start
()
}
return
true
}
}
func
application
(
_
application
:
UIApplication
,
configurationForConnecting
connectingSceneSession
:
UISceneSession
,
options
:
UIScene
.
ConnectionOptions
)
->
UISceneConfiguration
{
func
application
(
_
application
:
UIApplication
,
configurationForConnecting
connectingSceneSession
:
UISceneSession
,
options
:
UIScene
.
ConnectionOptions
)
->
UISceneConfiguration
{
...
@@ -162,7 +168,7 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -162,7 +168,7 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
completionHandler
(
.
noData
)
completionHandler
(
.
noData
)
}
}
// Push通知を受信した時(サイレントプッシュ)
//
MARK: -
Push通知を受信した時(サイレントプッシュ)
func
notificationHub
(
_
notificationHub
:
MSNotificationHub
,
didReceivePushNotification
notification
:
MSNotificationHubMessage
)
{
func
notificationHub
(
_
notificationHub
:
MSNotificationHub
,
didReceivePushNotification
notification
:
MSNotificationHubMessage
)
{
print
(
debug
:
"called"
)
print
(
debug
:
"called"
)
// let title = notification.title ?? ""
// let title = notification.title ?? ""
...
@@ -188,9 +194,36 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -188,9 +194,36 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
}
}
private
func
handleChatMessage
(
message
:
ResChatMessage
)
{
private
func
handleChatMessage
(
message
:
ResChatMessage
)
{
DispatchQueue
.
main
.
async
{
print
(
debug
:
"called"
)
print
(
debug
:
"called"
)
let
ownMsg
=
ChatMessage
(
shipId
:
message
.
shipId
,
messageId
:
message
.
messageId
,
type
:
message
.
type
,
time
:
message
.
time
,
location
:
message
.
location
,
from
:
message
.
from
,
fromId
:
message
.
fromId
,
mode
:
message
.
mode
,
message
:
message
.
message
,
stampId
:
message
.
stampId
,
viewer
:
[])
self
.
msg
.
messages
.
append
(
ownMsg
)
let
newMsg
=
ChatMessage
(
shipId
:
message
.
shipId
,
messageId
:
message
.
messageId
,
type
:
message
.
type
,
time
:
message
.
time
,
location
:
message
.
location
,
from
:
message
.
from
,
fromId
:
message
.
fromId
,
mode
:
message
.
mode
,
message
:
message
.
message
,
stampId
:
message
.
stampId
,
viewer
:
[]
)
if
let
index
=
self
.
msg
.
messages
.
lastIndex
(
where
:
{
$0
.
messageId
.
lowercased
()
==
newMsg
.
messageId
.
lowercased
()
})
{
var
updatedMsg
=
newMsg
let
existingMsg
=
self
.
msg
.
messages
[
index
]
if
existingMsg
.
message
!.
hasPrefix
(
"file://"
)
{
updatedMsg
.
message
=
existingMsg
.
message
}
self
.
msg
.
messages
[
index
]
=
updatedMsg
}
else
{
self
.
msg
.
messages
.
append
(
newMsg
)
}
}
}
}
private
func
handleAckMessage
(
message
:
ResAckMessage
)
{
private
func
handleAckMessage
(
message
:
ResAckMessage
)
{
...
@@ -219,7 +252,20 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -219,7 +252,20 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
self
.
msg
.
messages
.
append
(
ownMsg
)
self
.
msg
.
messages
.
append
(
ownMsg
)
}
}
//アプリ終了時
//MARK: - Signal-R再接続
func
reconnect
()
{
guard
!
isReconnecting
else
{
return
}
// 再接続中なら何もしない
isReconnecting
=
true
print
(
debug
:
"Attempting to reconnect..."
)
DispatchQueue
.
global
()
.
asyncAfter
(
deadline
:
.
now
()
+
5.0
)
{
[
weak
self
]
in
guard
let
self
=
self
else
{
return
}
connection
?
.
start
()
self
.
isReconnecting
=
false
}
}
//MARK: - アプリ終了時
func
applicationWillTerminate
(
_
aNotification
:
UIApplication
)
{
func
applicationWillTerminate
(
_
aNotification
:
UIApplication
)
{
if
let
r_connection
=
connection
{
if
let
r_connection
=
connection
{
r_connection
.
stop
()
r_connection
.
stop
()
...
@@ -232,12 +278,14 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
...
@@ -232,12 +278,14 @@ class AppDelegate: NSObject, UIApplicationDelegate ,MSNotificationHubDelegate, M
func
connectionDidFailToOpen
(
error
:
Error
)
{
func
connectionDidFailToOpen
(
error
:
Error
)
{
print
(
debug
:
"connectionDidFailToOpen:
\(
error
)
"
)
print
(
debug
:
"connectionDidFailToOpen:
\(
error
)
"
)
reconnect
()
}
}
func
connectionDidClose
(
error
:
Error
?)
{
func
connectionDidClose
(
error
:
Error
?)
{
if
let
err
=
error
{
if
let
err
=
error
{
print
(
debug
:
"connectionDidClose:
\(
err
)
"
)
print
(
debug
:
"connectionDidClose:
\(
err
)
"
)
}
}
reconnect
()
}
}
func
connectionWillReconnect
(
error
:
Error
)
{
func
connectionWillReconnect
(
error
:
Error
)
{
...
@@ -253,17 +301,15 @@ class SignalR: NSObject {
...
@@ -253,17 +301,15 @@ class SignalR: NSObject {
@ObservedObject
var
msg
=
SharingData
.
message
@ObservedObject
var
msg
=
SharingData
.
message
func
chatMessage
(
message
:
String
,
completion
:
@escaping
(
_
error
:
Error
?)
->
Void
)
{
func
chatMessage
(
message
:
String
,
completion
:
@escaping
(
_
error
:
Error
?)
->
Void
)
{
print
(
debug
:
"called"
)
var
request
=
ReqMessage
(
shipId
:
Preferences
.
shipId
,
messageId
:
UUID
()
.
uuidString
.
lowercased
())
var
request
=
ReqMessage
(
shipId
:
Preferences
.
shipId
,
messageId
:
UUID
()
.
uuidString
.
lowercased
())
request
.
type
=
0
//0:テキスト, 1:スタンプ
request
.
type
=
0
//0:テキスト, 1:スタンプ
request
.
time
=
DateTextLib
.
Date2ISO8601Text
(
Date
())
request
.
time
=
DateTextLib
.
Date2ISO8601Text
(
Date
())
request
.
location
=
2
//1:Shore , 2:Ship
request
.
location
=
2
//1:Shore , 2:Ship
request
.
from
=
Preferences
.
UserName
//投稿者名
request
.
from
=
Preferences
.
UserName
//投稿者名
request
.
fromId
=
String
(
SharingData
.
my
.
id
)
//ユーザーID
request
.
fromId
=
String
(
SharingData
.
my
.
id
)
//ユーザーID
if
SharingData
.
message
.
mode
{
request
.
mode
=
SharingData
.
message
.
mode
?
1
:
0
request
.
mode
=
1
}
else
{
request
.
mode
=
0
}
request
.
message
=
message
request
.
message
=
message
request
.
stampId
=
0
//スタンプ番号 0:Fire~
request
.
stampId
=
0
//スタンプ番号 0:Fire~
...
@@ -299,11 +345,15 @@ class SignalR: NSObject {
...
@@ -299,11 +345,15 @@ class SignalR: NSObject {
let
test
=
connection
.
debugDescription
let
test
=
connection
.
debugDescription
print
(
debug
:
"Test: Chat Message
\(
test
)
"
)
print
(
debug
:
"Test: Chat Message
\(
test
)
"
)
connection
!.
stop
()
guard
let
connection
=
connection
else
{
return
}
connection
.
stop
()
}
}
func
startConnection
()
{
func
startConnection
()
{
connection
!.
start
()
guard
let
connection
=
connection
else
{
return
}
if
connection
.
connectionId
==
nil
{
connection
.
start
()
}
}
}
}
}
...
@@ -355,6 +405,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
...
@@ -355,6 +405,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
let
arrCategory
=
arrAPS
[
"category"
]
as?
String
??
""
let
arrCategory
=
arrAPS
[
"category"
]
as?
String
??
""
switch
arrCategory
{
switch
arrCategory
{
case
"chat"
:
case
"chat"
:
print
(
debug
:
"chat"
)
guard
let
arrAlert
=
arrAPS
[
"alert"
]
as?
[
String
:
Any
]
else
{
guard
let
arrAlert
=
arrAPS
[
"alert"
]
as?
[
String
:
Any
]
else
{
completionHandler
([[
.
banner
,
.
badge
,
.
sound
]])
completionHandler
([[
.
banner
,
.
badge
,
.
sound
]])
return
return
...
...
Seilassist/Sailassist/ServerSession/SessionUploadImage.swift
View file @
8cec0939
...
@@ -20,29 +20,25 @@ class SessionUploadImage : ObservableObject {
...
@@ -20,29 +20,25 @@ class SessionUploadImage : ObservableObject {
func
cancelUpload
()
{
func
cancelUpload
()
{
uploadTask
?
.
cancel
()
uploadTask
?
.
cancel
()
uploadTask
=
nil
isUploading
=
false
isUploading
=
false
progress
=
0.0
progress
=
0.0
}
}
/**
//MARK: - 画像のアップロード
* メッセージ
*/
func
requestUploadImage
(
_
uploadImage
:
ReqUploadImage
)
async
throws
->
Data
{
func
requestUploadImage
(
_
uploadImage
:
ReqUploadImage
)
async
throws
->
Data
{
print
(
debug
:
"calld"
)
print
(
debug
:
"call
e
d"
)
guard
!
Calling
else
{
guard
!
Calling
else
{
throw
APIError
.
busy
throw
APIError
.
busy
}
}
Calling
=
true
Calling
=
true
defer
{
Calling
=
false
}
defer
{
Calling
=
false
}
// リクエストURLの組み立て
guard
let
req_url
=
URL
(
string
:
HttpRequestType
.
UploadImage
.
rawValue
)
else
{
guard
let
req_url
=
URL
(
string
:
HttpRequestType
.
UploadImage
.
rawValue
)
else
{
throw
APIError
.
invalidURL
throw
APIError
.
invalidURL
}
}
let
imageFileName
=
"itemp.jpg"
let
boundary
=
"----------
\(
UUID
()
.
uuidString
)
"
let
boundary
=
"----------
\(
UUID
()
.
uuidString
)
"
var
httpBody
=
Data
()
var
httpBody
=
Data
()
func
appendFormField
(
name
:
String
,
value
:
String
)
{
func
appendFormField
(
name
:
String
,
value
:
String
)
{
httpBody
.
append
(
"--
\(
boundary
)\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"--
\(
boundary
)\r\n
"
.
data
(
using
:
.
utf8
)
!
)
...
@@ -57,7 +53,7 @@ class SessionUploadImage : ObservableObject {
...
@@ -57,7 +53,7 @@ class SessionUploadImage : ObservableObject {
appendFormField
(
name
:
"FromId"
,
value
:
uploadImage
.
fromId
)
appendFormField
(
name
:
"FromId"
,
value
:
uploadImage
.
fromId
)
httpBody
.
append
(
"--
\(
boundary
)\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"--
\(
boundary
)\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"Content-Disposition: form-data; name=
\"
files
\"
; filename=
\"
\(
imageFileName
)
\"\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"Content-Disposition: form-data; name=
\"
files
\"
; filename=
\"
itemp.jpg
\"\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"Content-Type: image/jpeg
\r\n\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"Content-Type: image/jpeg
\r\n\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
uploadImage
.
files
)
httpBody
.
append
(
uploadImage
.
files
)
httpBody
.
append
(
"
\r\n
--
\(
boundary
)
--
\r\n
"
.
data
(
using
:
.
utf8
)
!
)
httpBody
.
append
(
"
\r\n
--
\(
boundary
)
--
\r\n
"
.
data
(
using
:
.
utf8
)
!
)
...
@@ -65,22 +61,35 @@ class SessionUploadImage : ObservableObject {
...
@@ -65,22 +61,35 @@ class SessionUploadImage : ObservableObject {
var
request
=
URLRequest
(
url
:
req_url
)
var
request
=
URLRequest
(
url
:
req_url
)
request
.
httpMethod
=
"POST"
request
.
httpMethod
=
"POST"
request
.
setValue
(
"multipart/form-data; boundary=
\(
boundary
)
"
,
forHTTPHeaderField
:
"Content-Type"
)
request
.
setValue
(
"multipart/form-data; boundary=
\(
boundary
)
"
,
forHTTPHeaderField
:
"Content-Type"
)
let
session
=
URLSession
(
configuration
:
.
default
,
delegate
:
UploadProgressDelegate
(
parent
:
self
),
delegateQueue
:
nil
)
let
session
=
URLSession
(
configuration
:
.
default
,
delegate
:
UploadProgressDelegate
(
parent
:
self
),
delegateQueue
:
nil
)
isUploading
=
true
isUploading
=
true
uploadTask
=
session
.
uploadTask
(
with
:
request
,
from
:
httpBody
)
uploadTask
?
.
resume
()
let
(
data
,
response
)
=
try
await
session
.
upload
(
for
:
request
,
from
:
httpBody
)
return
try
await
withCheckedThrowingContinuation
{
continuation
in
self
.
uploadTask
=
session
.
uploadTask
(
with
:
request
,
from
:
httpBody
)
{
data
,
response
,
error
in
DispatchQueue
.
main
.
async
{
self
.
isUploading
=
false
self
.
progress
=
0.0
}
isUploading
=
false
if
let
error
=
error
{
progress
=
0.0
continuation
.
resume
(
throwing
:
error
)
return
}
guard
let
httpResponse
=
response
as?
HTTPURLResponse
,
(
200
...
299
)
.
contains
(
httpResponse
.
statusCode
)
else
{
guard
let
httpResponse
=
response
as?
HTTPURLResponse
,
throw
APIError
.
serverError
(
200
...
299
)
.
contains
(
httpResponse
.
statusCode
),
let
data
=
data
else
{
continuation
.
resume
(
throwing
:
APIError
.
serverError
)
return
}
}
return
data
continuation
.
resume
(
returning
:
data
)
}
self
.
uploadTask
?
.
resume
()
}
}
}
func
postFormAsync
(
boundary
:
String
,
url
:
URL
,
body
:
Data
)
async
throws
->
Data
{
func
postFormAsync
(
boundary
:
String
,
url
:
URL
,
body
:
Data
)
async
throws
->
Data
{
...
@@ -105,8 +114,11 @@ class SessionUploadImage : ObservableObject {
...
@@ -105,8 +114,11 @@ class SessionUploadImage : ObservableObject {
self
.
parent
=
parent
self
.
parent
=
parent
}
}
func
urlSession
(
_
session
:
URLSession
,
task
:
URLSessionTask
,
didSendBodyData
bytesSent
:
Int64
,
func
urlSession
(
_
session
:
URLSession
,
task
:
URLSessionTask
,
totalBytesSent
:
Int64
,
totalBytesExpectedToSend
:
Int64
)
{
didSendBodyData
bytesSent
:
Int64
,
totalBytesSent
:
Int64
,
totalBytesExpectedToSend
:
Int64
)
{
guard
totalBytesExpectedToSend
>
0
else
{
return
}
DispatchQueue
.
main
.
async
{
DispatchQueue
.
main
.
async
{
self
.
parent
?
.
progress
=
Double
(
totalBytesSent
)
/
Double
(
totalBytesExpectedToSend
)
self
.
parent
?
.
progress
=
Double
(
totalBytesSent
)
/
Double
(
totalBytesExpectedToSend
)
}
}
...
...
Seilassist/Sailassist/Tab/CustomTabBar.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
struct
CustomTabBar
:
View
{
@EnvironmentObject
private
var
selectedTabModel
:
SelectedTabModel
@StateObject
private
var
viewModel
=
CustomTabBarViewModel
()
@Environment(\.openURL)
var
openURL
@ObservedObject
var
my
=
SharingData
.
my
@ObservedObject
var
message
=
SharingData
.
message
@ObservedObject
var
location
=
SharingData
.
location
@ObservedObject
var
pushHistory
=
SharingData
.
pushHistory
var
body
:
some
View
{
VStack
(
spacing
:
0
){
Divider
()
HStack
(
spacing
:
0
){
ForEach
(
Tab
.
allCases
,
id
:
\
.
rawValue
)
{
tab
in
VStack
{
ZStack
(
alignment
:
.
bottomTrailing
)
{
if
!
SharingData
.
my
.
isCommunication
&&
tab
==
Tab
.
chat
{
Image
(
"tab_chat_Invalid"
)
.
onTapGesture
{
viewModel
.
handleTabSelection
(
selectedTabModel
:
selectedTabModel
,
tab
:
tab
,
message
:
message
,
history
:
pushHistory
,
location
:
location
,
)
}
}
else
{
Image
(
selectedTabModel
.
activeTab
==
tab
?
tab
.
rawValue
+
"_selected"
:
tab
.
rawValue
)
.
onTapGesture
{
viewModel
.
handleTabSelection
(
selectedTabModel
:
selectedTabModel
,
tab
:
tab
,
message
:
message
,
history
:
pushHistory
,
location
:
location
,
)
}
}
if
tab
==
Tab
.
chat
{
NotificationBadge
(
viewModel
:
viewModel
.
chatBadgeViewModel
,
font
:
FontStyle
.
VersionText
.
font
)
}
if
tab
==
Tab
.
alert
{
NotificationBadge
(
viewModel
:
viewModel
.
alertBadgeViewModel
,
font
:
FontStyle
.
VersionText
.
font
)
}
}
Text
(
tab
.
title
)
.
font
(
.
caption
)
}
.
frame
(
maxWidth
:
.
infinity
,
maxHeight
:
.
infinity
)
.
contentShape
(
Rectangle
())
}
}
.
frame
(
height
:
50
)
}
.
background
(
ColorSet
.
BottomNav
.
color
)
.
modifier
(
ChangeModeAlertModifier
(
isPresented
:
$
selectedTabModel
.
isShowChangeEmrMode
,
currentMode
:
message
.
mode
,
onConfirm
:
{
var
chatMode
=
message
.
mode
chatMode
.
toggle
()
let
signalRService
=
SignalR
()
signalRService
.
chatMode
(
mode
:
chatMode
,
completion
:
responseChatMode
)
message
.
sendInf
=
true
DispatchQueue
.
main
.
asyncAfter
(
deadline
:
.
now
()
+
3.0
)
{
restartChatMode
()
}
}))
.
modifier
(
LocationAlertModifier
(
isPresented
:
$
selectedTabModel
.
isLocationAlert
))
.
modifier
(
UpdateAlertModifier
(
isPresented
:
$
my
.
isUpDate
,
appStoreURL
:
URL
(
string
:
HttpRequestType
.
AppStore
.
rawValue
)
!
))
.
modifier
(
WarningModeAlertModifier
(
isPresented
:
$
viewModel
.
isModeAlert
,
isSignalrRestert
:
$
viewModel
.
isSignalrRestart
,
onConfirm
:
{
let
signalRService
=
SignalR
()
signalRService
.
startConnection
()
viewModel
.
isSignalrRestart
=
false
}))
//MARK: - チャットTab上の既読マーク
.
onChange
(
of
:
message
.
viewCnt
)
{
newValue
in
viewModel
.
chatBadgeViewModel
.
updateViewCnt
(
to
:
newValue
)
}
//MARK: - アラートTab上の既読マーク
.
onChange
(
of
:
pushHistory
.
viewCnt
)
{
newValue
in
viewModel
.
alertBadgeViewModel
.
updateViewCnt
(
to
:
newValue
)
}
}
//MARK: - Warninngモードレスポンス
func
responseChatMode
(
error
:
Error
?)
{
print
(
debug
:
"responseChatMode"
)
message
.
sendInf
=
false
if
let
e
=
error
{
print
(
debug
:
"Error chat:
\(
e
)
"
)
viewModel
.
isModeAlert
=
true
}
else
{
message
.
changeMode
()
}
}
//MARK: - モード変更レスポンスが無かった場合
func
restartChatMode
()
{
if
message
.
sendInf
{
let
signalRService
=
SignalR
()
signalRService
.
stopConnection
()
Thread
.
sleep
(
forTimeInterval
:
1.0
)
viewModel
.
isSignalrRestart
=
true
}
}
}
Seilassist/Sailassist/Tab/CustomTabBarViewModel.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
import
Foundation
import
UIKit
import
Combine
class
CustomTabBarViewModel
:
ObservableObject
{
@Published
var
chatBadgeViewModel
=
NotificationBadgeViewModel
()
@Published
var
alertBadgeViewModel
=
NotificationBadgeViewModel
()
@Published
var
isMenuTaskViewVisible
=
false
@Published
var
isSignalrRestart
=
false
@Published
var
isModeAlert
=
false
private
let
messageService
=
GetMessage
()
func
handleTabSelection
(
selectedTabModel
:
SelectedTabModel
,
tab
:
Tab
,
message
:
SharingData
.
Message
,
history
:
SharingData
.
PushHistory
,
location
:
SharingData
.
Location
)
{
selectedTabModel
.
activeTab
=
tab
switch
tab
{
case
.
map
:
selectedTabModel
.
isPoppver
.
toggle
()
location
.
focusOwnShip
=
true
if
UIDevice
.
current
.
userInterfaceIdiom
==
.
pad
{
isMenuTaskViewVisible
.
toggle
()
updateMapMenuVisibility
()
}
case
.
chat
:
messageService
.
start
()
messageService
.
readNotification
()
messageService
.
checkUnreadMessages
()
message
.
viewCnt
=
0
chatBadgeViewModel
.
resetBadge
()
resetPopoverAndMenu
(
selectedTabModel
:
selectedTabModel
)
case
.
alert
:
history
.
viewCnt
=
0
alertBadgeViewModel
.
resetBadge
()
resetPopoverAndMenu
(
selectedTabModel
:
selectedTabModel
)
default
:
resetPopoverAndMenu
(
selectedTabModel
:
selectedTabModel
)
}
}
private
func
resetPopoverAndMenu
(
selectedTabModel
:
SelectedTabModel
)
{
selectedTabModel
.
isPoppver
=
false
if
UIDevice
.
current
.
userInterfaceIdiom
==
.
pad
{
isMenuTaskViewVisible
=
false
updateMapMenuVisibility
()
}
}
//MARK: - マップメニュー表示の切り替え
private
func
updateMapMenuVisibility
()
{
let
screenWidth
=
UIScreen
.
main
.
bounds
.
width
let
screenHeight
=
UIScreen
.
main
.
bounds
.
height
let
menuWidth
=
min
(
400
,
screenWidth
*
0.3
)
let
menuHeight
=
screenHeight
*
0.8
if
isMenuTaskViewVisible
{
MenuWindowManager
.
shared
.
showMenuView
(
MenuTaskView
()
.
frame
(
width
:
menuWidth
,
height
:
menuHeight
)
.
background
(
ColorSet
.
BackgroundSecondary
.
color
)
.
cornerRadius
(
15
),
frame
:
CGRect
(
x
:
5
,
y
:
screenHeight
*
0.12
,
width
:
menuWidth
,
height
:
menuHeight
)
)
}
else
{
MenuWindowManager
.
shared
.
hideMenuView
()
}
}
}
class
MenuWindowManager
{
static
let
shared
=
MenuWindowManager
()
private
var
window
:
UIWindow
?
func
showMenuView
<
Content
:
View
>
(
_
content
:
Content
,
frame
:
CGRect
)
{
guard
window
==
nil
else
{
return
}
let
hostingController
=
UIHostingController
(
rootView
:
content
)
if
let
windowScene
=
UIApplication
.
shared
.
connectedScenes
.
first
as?
UIWindowScene
{
var
rFrame
=
frame
rFrame
.
size
.
height
-=
20
let
newWindow
=
UIWindow
(
windowScene
:
windowScene
)
newWindow
.
rootViewController
=
hostingController
newWindow
.
windowLevel
=
.
alert
+
1
newWindow
.
backgroundColor
=
UIColor
.
clear
newWindow
.
makeKeyAndVisible
()
newWindow
.
alpha
=
0.8
newWindow
.
frame
=
rFrame
newWindow
.
layer
.
cornerRadius
=
15
newWindow
.
layer
.
masksToBounds
=
true
self
.
window
=
newWindow
}
}
func
hideMenuView
()
{
guard
let
window
=
window
else
{
return
}
window
.
isHidden
=
true
window
.
rootViewController
=
nil
self
.
window
=
nil
}
}
Seilassist/Sailassist/Tab/MainTabView.swift
deleted
100644 → 0
View file @
d2befa8b
This diff is collapsed.
Click to expand it.
Seilassist/Sailassist/Tab/NotificationBadge.swift
0 → 100644
View file @
8cec0939
import
SwiftUI
class
NotificationBadgeViewModel
:
ObservableObject
{
@Published
private(set)
var
viewCnt
:
Int
=
0
@Published
private(set)
var
hasUnread
:
Bool
=
false
init
(
viewCnt
:
Int
=
0
)
{
self
.
viewCnt
=
viewCnt
}
//MARK: - 未読カウントを更新
func
updateViewCnt
(
to
newValue
:
Int
)
{
viewCnt
=
newValue
hasUnread
=
newValue
>
0
}
//MARK: - バッジをリセット(既読状態にする)
func
resetBadge
()
{
viewCnt
=
0
hasUnread
=
false
}
//MARK: - 現在の未読数を取得
func
getUnreadCount
()
->
Int
{
return
viewCnt
}
}
struct
NotificationBadge
:
View
{
@ObservedObject
var
viewModel
:
NotificationBadgeViewModel
let
font
:
Font
var
body
:
some
View
{
Group
{
if
viewModel
.
getUnreadCount
()
>
0
{
Ellipse
()
.
fill
(
Color
.
red
)
.
frame
(
width
:
12
,
height
:
12
)
.
overlay
(
Text
(
viewModel
.
getUnreadCount
()
<
10
?
"
\(
viewModel
.
getUnreadCount
()
)
"
:
"-"
)
.
font
(
font
)
.
foregroundColor
(
.
white
)
)
}
}
}
}
Seilassist/Sailassist/Tab/View/MainTabView.swift
0 → 100644
View file @
8cec0939
//
// MainTabView.swift
// forShip
//
// Created by Mamoru Sugita on 2023/10/13.
//
import
SwiftUI
enum
Tab
:
String
,
CaseIterable
{
case
map
=
"tab_map"
case
chat
=
"tab_chat"
case
alert
=
"tab_notification"
case
menu
=
"tab_menu"
var
title
:
String
{
switch
self
{
case
.
map
:
return
"Map"
case
.
chat
:
return
"Chat"
case
.
alert
:
return
"Alert"
case
.
menu
:
return
"Menu"
}
}
}
struct
MainTabView
:
View
{
@EnvironmentObject
var
selectedTabModel
:
SelectedTabModel
@EnvironmentObject
private
var
sceneDelegate
:
SceneDelegate
@ObservedObject
var
location
=
SharingData
.
location
@State
private
var
offset
=
CGSize
.
zero
@State
var
isSignout
=
false
var
isTabWindowActive
:
Bool
{
sceneDelegate
.
tabWindow
!=
nil
}
var
isTaskSel
:
Bool
{
selectedTabModel
.
activeTab
==
.
map
}
var
body
:
some
View
{
Group
{
if
UIDevice
.
current
.
userInterfaceIdiom
==
.
phone
{
phoneView
}
else
{
padView
}
}
.
onAppear
{
SharingData
.
location
.
focusOwnShip
=
true
configureTabBarAppearance
()
}
}
private
var
phoneView
:
some
View
{
ZStack
(
alignment
:
.
bottom
)
{
TabView
(
selection
:
$
selectedTabModel
.
activeTab
)
{
mapTab
if
SharingData
.
my
.
isCommunication
{
ChatView
()
.
tag
(
Tab
.
chat
)
}
NotificationView
()
.
tag
(
Tab
.
alert
)
MenuView
(
isSignout
:
$
isSignout
)
.
tag
(
Tab
.
menu
)
}
.
sheet
(
isPresented
:
.
constant
(
isTaskSel
&&
isTabWindowActive
))
{
MenuTaskView
()
.
zIndex
(
0
)
.
presentationDragIndicator
(
.
hidden
)
.
presentationDetents
([
.
height
(
150
),
.
medium
,
.
fraction
(
0.99
)])
.
presentationCornerRadius
(
15
)
.
presentationBackgroundInteraction
(
.
enabled
(
upThrough
:
.
medium
))
.
presentationBackground
(
ColorSet
.
BackgroundSecondary
.
color
)
.
interactiveDismissDisabled
()
}
}
}
private
var
padView
:
some
View
{
ZStack
(
alignment
:
.
bottom
)
{
TabView
(
selection
:
$
selectedTabModel
.
activeTab
)
{
GeometryReader
{
geometry
in
mapTab
.
padding
(
.
bottom
,
geometry
.
safeAreaInsets
.
bottom
+
10
)
}
if
SharingData
.
my
.
isCommunication
{
ChatView
()
.
padding
(
.
bottom
,
50
)
.
tag
(
Tab
.
chat
)
}
NotificationView
()
.
padding
(
.
bottom
,
50
)
.
tag
(
Tab
.
alert
)
MenuView
(
isSignout
:
$
isSignout
)
.
padding
(
.
bottom
,
50
)
.
tag
(
Tab
.
menu
)
}
.
tabViewStyle
(
PageTabViewStyle
(
indexDisplayMode
:
.
never
))
}
}
@ViewBuilder
private
var
mapTab
:
some
View
{
ZStack
{
MapRepresentable
()
MapInformation
()
.
zIndex
(
1
)
}
.
ignoresSafeArea
(
edges
:
.
top
)
.
tag
(
Tab
.
map
)
}
private
func
configureTabBarAppearance
()
{
let
appearance
=
UITabBarAppearance
()
appearance
.
backgroundColor
=
.
clear
UITabBar
.
appearance
()
.
scrollEdgeAppearance
=
appearance
UITabBar
.
appearance
()
.
standardAppearance
=
appearance
}
}
#Preview {
MainTabView
()
.
environmentObject
(
SelectedTabModel
())
.
environmentObject
(
SceneDelegate
())
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment