Compare commits
142 Commits
d8c27ae225
...
ed9c2d9d32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed9c2d9d32 | ||
|
|
27720cfb65 | ||
|
|
11dede49e2 | ||
|
|
5cd0ea9bc2 | ||
|
|
42e375ec4b | ||
|
|
5e85b1d679 | ||
|
|
5e209662d8 | ||
|
|
b522b50dee | ||
|
|
95cae7d0bf | ||
|
|
7e37fc6f57 | ||
|
|
d0a17739a1 | ||
|
|
091f90dee6 | ||
|
|
e100bcb593 | ||
|
|
7825f54a01 | ||
|
|
aa29acad0e | ||
|
|
fb6b623564 | ||
|
|
791ebb0242 | ||
|
|
0a40bdbee8 | ||
|
|
7cdb46dd8a | ||
|
|
02d767d5d8 | ||
|
|
9533b06d1e | ||
|
|
b3331d5512 | ||
|
|
c9c1db39a6 | ||
|
|
280e424385 | ||
|
|
d92dcbc696 | ||
|
|
5a344956e3 | ||
|
|
87c66136ac | ||
|
|
4b9cdeb824 | ||
|
|
75452f0ffd | ||
|
|
9223e26a07 | ||
|
|
a743fecfdd | ||
|
|
d3037d1ba3 | ||
|
|
f1353fc2e6 | ||
|
|
68dab028cc | ||
|
|
4d893a2081 | ||
|
|
d9861b4612 | ||
|
|
a34c050d25 | ||
|
|
c84227008c | ||
|
|
4354530372 | ||
|
|
1b583eabba | ||
|
|
eb83d5e5e3 | ||
|
|
fa4483cd18 | ||
|
|
f855ca55ca | ||
|
|
e7c8c2a12e | ||
|
|
2f8331f2ff | ||
|
|
921c7e008c | ||
|
|
7775e4f0d9 | ||
|
|
19380ccc70 | ||
|
|
64bb5668f4 | ||
|
|
c6da15f42c | ||
|
|
fba3940a94 | ||
|
|
57f24f2481 | ||
|
|
7c1d57676c | ||
|
|
6a7497af7d | ||
|
|
dd51a3fc2e | ||
|
|
7307e5b255 | ||
|
|
dbc4e40904 | ||
|
|
3220f300b5 | ||
|
|
8dae75afd7 | ||
|
|
0ddb5810b1 | ||
|
|
f67fd5bb26 | ||
|
|
a406935e4f | ||
|
|
ee4b7c9a79 | ||
|
|
321d97bf4d | ||
|
|
fdac62fc7a | ||
|
|
27b024f187 | ||
|
|
e035e57fc2 | ||
|
|
c80246adbf | ||
|
|
bea50b0085 | ||
|
|
f51fe327e9 | ||
|
|
8bcbd3aca0 | ||
|
|
982a17bb41 | ||
|
|
8efa89d564 | ||
|
|
a0bb593431 | ||
|
|
fa163ec83d | ||
|
|
619870e1de | ||
|
|
ad9f26c8a0 | ||
|
|
da2e8d0209 | ||
|
|
d2ff2782fe | ||
|
|
30c70ee638 | ||
|
|
b61f432b72 | ||
|
|
96df4c1f1b | ||
|
|
0285f62ecb | ||
|
|
b2c94a44d9 | ||
|
|
bc4b30a965 | ||
|
|
3f24b1f3d4 | ||
|
|
d5437c03b0 | ||
|
|
f88926f8a8 | ||
|
|
ea733b57e6 | ||
|
|
dd7b42d58a | ||
|
|
972e889fab | ||
|
|
3e60a8c340 | ||
|
|
dbf8bc2dd2 | ||
|
|
3f857eb293 | ||
|
|
0aa86c26ca | ||
|
|
a36a440340 | ||
|
|
55fb470b37 | ||
|
|
0375722d4c | ||
|
|
4c8aabdd4c | ||
|
|
9e7c62b794 | ||
|
|
d3ddcecc68 | ||
|
|
6a4f4767e0 | ||
|
|
90b6dfb498 | ||
|
|
001f161fc5 | ||
|
|
4d5ac61dbe | ||
|
|
68fd9ee3ad | ||
|
|
af42fd074f | ||
|
|
31319e4292 | ||
|
|
6cd0e86308 | ||
|
|
54a5c99666 | ||
|
|
cb1b26c548 | ||
|
|
5fcc6a9a60 | ||
|
|
a6ef1d89ce | ||
|
|
9cea4c244a | ||
|
|
43629a27b8 | ||
|
|
daca685ea2 | ||
|
|
438f12024f | ||
|
|
72329d6f60 | ||
|
|
280ce4beda | ||
|
|
320dbb6d57 | ||
|
|
be40fbc226 | ||
|
|
ed48efd58d | ||
|
|
c4a7742514 | ||
|
|
e5810766b1 | ||
|
|
74212405a4 | ||
|
|
2c74bb743b | ||
|
|
0fd49a71f6 | ||
|
|
0902b1fe30 | ||
|
|
8bacf363ff | ||
|
|
1f88bcbddc | ||
|
|
7de2b1c4dd | ||
|
|
6e3a1e1869 | ||
|
|
0134a5286f | ||
|
|
2af2f2ffea | ||
|
|
3a7da9a876 | ||
|
|
54d8845342 | ||
|
|
a4c5a790fe | ||
|
|
3cbac1280e | ||
|
|
34eed366bd | ||
|
|
95c2e992de | ||
|
|
76757215cf | ||
|
|
0a59c6f575 |
1
.gitignore
vendored
@@ -279,5 +279,6 @@ xcuserdata
|
|||||||
|
|
||||||
.kiro/
|
.kiro/
|
||||||
.junie/
|
.junie/
|
||||||
|
docs/
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/macos,xcode,appcode,swift,swiftpackagemanager,swiftpm,fastlane,cocoapods
|
# End of https://www.toptal.com/developers/gitignore/api/macos,xcode,appcode,swift,swiftpackagemanager,swiftpm,fastlane,cocoapods
|
||||||
|
|||||||
18
AGENTS.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
질문에 대한 답변과 설명은 한국어로 한다.
|
||||||
|
|
||||||
|
## Quality Assurance Guidelines
|
||||||
|
|
||||||
|
### Commit Standards
|
||||||
|
1. Write in Korean.
|
||||||
|
2. Use the present tense in the subject line (e.g., "Add feature" not "Added feature").
|
||||||
|
3. Keep the subject line to 50 characters or less.
|
||||||
|
4. Add a blank line between the subject and body.
|
||||||
|
5. Keep the body to 72 characters or less per line.
|
||||||
|
6. Within a paragraph, only break lines when the text exceeds 72 characters.
|
||||||
|
7. Describe changes to public API features and do not include implementation details such as package-private code.
|
||||||
|
8. Do not mention test code in commit messages.
|
||||||
|
9. Do not use any prefix (such as "fix:", "update:", "docs:", "feat:", etc.) in the subject line.
|
||||||
|
10. Do not start the subject line with a lowercase letter unless the first word is a function name or another identifier that is conventionally lowercase and there is a clear, justifiable reason for the exception. Otherwise, always start with an uppercase letter.
|
||||||
|
11. Do not include tool advertisements, branding, or promotional content in commit messages.
|
||||||
|
12. Use separate git commands to stage files before committing.
|
||||||
|
13. Always validate commits using `work/scripts/check-commit-message-rules.sh` and fix until validation passes.
|
||||||
4
Podfile
@@ -8,6 +8,8 @@ target 'SodaLive' do
|
|||||||
# Pods for SodaLive
|
# Pods for SodaLive
|
||||||
pod 'BootpayUI', '4.4.10'
|
pod 'BootpayUI', '4.4.10'
|
||||||
pod 'AgoraRtm', '2.2.4'
|
pod 'AgoraRtm', '2.2.4'
|
||||||
|
pod 'GoogleSignIn'
|
||||||
|
pod 'GoogleSignInSwiftSupport'
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -18,6 +20,8 @@ target 'SodaLive-dev' do
|
|||||||
# Pods for SodaLive-dev
|
# Pods for SodaLive-dev
|
||||||
pod 'BootpayUI', '4.4.10'
|
pod 'BootpayUI', '4.4.10'
|
||||||
pod 'AgoraRtm', '2.2.4'
|
pod 'AgoraRtm', '2.2.4'
|
||||||
|
pod 'GoogleSignIn'
|
||||||
|
pod 'GoogleSignInSwiftSupport'
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
51
Podfile.lock
@@ -5,6 +5,16 @@ PODS:
|
|||||||
- AgoraRtm/RtmBasic (2.2.4)
|
- AgoraRtm/RtmBasic (2.2.4)
|
||||||
- AgoraRtm/RtmKit (2.2.4)
|
- AgoraRtm/RtmKit (2.2.4)
|
||||||
- Alamofire (5.10.2)
|
- Alamofire (5.10.2)
|
||||||
|
- AppAuth (2.0.0):
|
||||||
|
- AppAuth/Core (= 2.0.0)
|
||||||
|
- AppAuth/ExternalUserAgent (= 2.0.0)
|
||||||
|
- AppAuth/Core (2.0.0)
|
||||||
|
- AppAuth/ExternalUserAgent (2.0.0):
|
||||||
|
- AppAuth/Core
|
||||||
|
- AppCheckCore (11.2.0):
|
||||||
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
- Bootpay (4.4.6):
|
- Bootpay (4.4.6):
|
||||||
- CryptoSwift
|
- CryptoSwift
|
||||||
- NVActivityIndicatorView
|
- NVActivityIndicatorView
|
||||||
@@ -17,40 +27,79 @@ PODS:
|
|||||||
- SnapKit
|
- SnapKit
|
||||||
- SwiftyJSON
|
- SwiftyJSON
|
||||||
- CryptoSwift (1.8.4)
|
- CryptoSwift (1.8.4)
|
||||||
|
- GoogleSignIn (9.1.0):
|
||||||
|
- AppAuth (~> 2.0)
|
||||||
|
- AppCheckCore (~> 11.0)
|
||||||
|
- GTMAppAuth (~> 5.0)
|
||||||
|
- GTMSessionFetcher/Core (~> 3.3)
|
||||||
|
- GoogleSignInSwiftSupport (9.1.0):
|
||||||
|
- GoogleSignIn (~> 9.0)
|
||||||
|
- GoogleUtilities/Environment (8.1.0):
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Logger (8.1.0):
|
||||||
|
- GoogleUtilities/Environment
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GoogleUtilities/Privacy (8.1.0)
|
||||||
|
- GoogleUtilities/UserDefaults (8.1.0):
|
||||||
|
- GoogleUtilities/Logger
|
||||||
|
- GoogleUtilities/Privacy
|
||||||
|
- GTMAppAuth (5.0.0):
|
||||||
|
- AppAuth/Core (~> 2.0)
|
||||||
|
- GTMSessionFetcher/Core (< 4.0, >= 3.3)
|
||||||
|
- GTMSessionFetcher/Core (3.5.0)
|
||||||
- NVActivityIndicatorView (5.2.0):
|
- NVActivityIndicatorView (5.2.0):
|
||||||
- NVActivityIndicatorView/Base (= 5.2.0)
|
- NVActivityIndicatorView/Base (= 5.2.0)
|
||||||
- NVActivityIndicatorView/Base (5.2.0)
|
- NVActivityIndicatorView/Base (5.2.0)
|
||||||
- ObjectMapper (4.4.2)
|
- ObjectMapper (4.4.2)
|
||||||
|
- PromisesObjC (2.4.0)
|
||||||
- SnapKit (5.7.1)
|
- SnapKit (5.7.1)
|
||||||
- SwiftyJSON (5.0.2)
|
- SwiftyJSON (5.0.2)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- AgoraRtm (= 2.2.4)
|
- AgoraRtm (= 2.2.4)
|
||||||
- BootpayUI (= 4.4.10)
|
- BootpayUI (= 4.4.10)
|
||||||
|
- GoogleSignIn
|
||||||
|
- GoogleSignInSwiftSupport
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- AgoraRtm
|
- AgoraRtm
|
||||||
- Alamofire
|
- Alamofire
|
||||||
|
- AppAuth
|
||||||
|
- AppCheckCore
|
||||||
- Bootpay
|
- Bootpay
|
||||||
- BootpayUI
|
- BootpayUI
|
||||||
- CryptoSwift
|
- CryptoSwift
|
||||||
|
- GoogleSignIn
|
||||||
|
- GoogleSignInSwiftSupport
|
||||||
|
- GoogleUtilities
|
||||||
|
- GTMAppAuth
|
||||||
|
- GTMSessionFetcher
|
||||||
- NVActivityIndicatorView
|
- NVActivityIndicatorView
|
||||||
- ObjectMapper
|
- ObjectMapper
|
||||||
|
- PromisesObjC
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- SwiftyJSON
|
- SwiftyJSON
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
AgoraRtm: 534144434383d41b3b0ebfae2a961ef0f51b0645
|
AgoraRtm: 534144434383d41b3b0ebfae2a961ef0f51b0645
|
||||||
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496
|
||||||
|
AppAuth: 1c1a8afa7e12f2ec3a294d9882dfa5ab7d3cb063
|
||||||
|
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||||
Bootpay: cd7f0542b096ab0af0b09a6e12a6b87f2cbbb531
|
Bootpay: cd7f0542b096ab0af0b09a6e12a6b87f2cbbb531
|
||||||
BootpayUI: beec5b0bba002b4dbced8c0ecace571ed6a017bc
|
BootpayUI: beec5b0bba002b4dbced8c0ecace571ed6a017bc
|
||||||
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
|
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
|
||||||
|
GoogleSignIn: fcee2257188d5eda57a5e2b6a715550ffff9206d
|
||||||
|
GoogleSignInSwiftSupport: aca902e4e15b234611ecac74ef5c8f61278f774e
|
||||||
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
|
GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238
|
||||||
|
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||||
NVActivityIndicatorView: fe52a6a68664c2df8991d7d9e3d86d8d19453c53
|
NVActivityIndicatorView: fe52a6a68664c2df8991d7d9e3d86d8d19453c53
|
||||||
ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677
|
ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677
|
||||||
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||||
SwiftyJSON: f5b1bf1cd8dd53cd25887ac0eabcfd92301c6a5a
|
SwiftyJSON: f5b1bf1cd8dd53cd25887ac0eabcfd92301c6a5a
|
||||||
|
|
||||||
PODFILE CHECKSUM: 197d8c8b434dbcc335438281fc68e94718f6a8e1
|
PODFILE CHECKSUM: 70c5639090824ff26cfad959985347579609e1e6
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "1f28da3687662a2a9efe60ffc2ca2499be411b5b0a1e07f72559059c40728121",
|
"originHash" : "9f35428c4c178ca4a8bfa4b72544585a9e4d5b119825b423e1d2166cbe03fe37",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "abseil-cpp-binary",
|
"identity" : "abseil-cpp-binary",
|
||||||
@@ -127,6 +127,15 @@
|
|||||||
"version" : "100.0.0"
|
"version" : "100.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "kakao-ios-sdk",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kakao/kakao-ios-sdk",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "5978979157a5a0521c9c56fd0156aec794caa21c",
|
||||||
|
"version" : "2.27.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "kingfisher",
|
"identity" : "kingfisher",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -145,6 +154,15 @@
|
|||||||
"version" : "1.22.5"
|
"version" : "1.22.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "line-sdk-ios-swift",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/line/line-sdk-ios-swift.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "51ef2ebefb05db8f748e80208b3281ca723abcdb",
|
||||||
|
"version" : "5.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "moya",
|
"identity" : "moya",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
21
SodaLive/Resources/Assets.xcassets/btn_follow_big_en.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_follow_big_en.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/btn_follow_big_en.imageset/btn_follow_big_en.png
vendored
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_follow_big_ja.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_follow_big_ja.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/btn_follow_big_ja.imageset/btn_follow_big_ja.png
vendored
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_following_big_en.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_following_big_en.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/btn_following_big_en.imageset/btn_following_big_en.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_following_big_ja.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_following_big_ja.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/btn_following_big_ja.imageset/btn_following_big_ja.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_following_no_alarm_big_en.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_following_no_alarm_big_en.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.5 KiB |
21
SodaLive/Resources/Assets.xcassets/btn_following_no_alarm_big_ja.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_following_no_alarm_big_ja.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -9,7 +9,7 @@
|
|||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "btn_make_live.png",
|
"filename" : "ic_login_line.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/ic_login_line.imageset/ic_login_line.png
vendored
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_login_x.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_login_x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/ic_login_x.imageset/ic_login_x.png
vendored
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_make_live.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_make_live.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/ic_make_live.imageset/ic_make_live.png
vendored
Normal file
|
After Width: | Height: | Size: 896 B |
21
SodaLive/Resources/Assets.xcassets/ic_no_preview.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ic_no_preview.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/ic_no_preview.imageset/ic_no_preview.png
vendored
Normal file
|
After Width: | Height: | Size: 677 B |
21
SodaLive/Resources/Assets.xcassets/img_apply_creator.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img_apply_creator.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/img_apply_creator.imageset/img_apply_creator.png
vendored
Normal file
|
After Width: | Height: | Size: 42 KiB |
21
SodaLive/Resources/Assets.xcassets/img_rank_1.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img_rank_1.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/img_rank_1.imageset/img_rank_1.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
21
SodaLive/Resources/Assets.xcassets/img_rank_2.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img_rank_2.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/img_rank_2.imageset/img_rank_2.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
21
SodaLive/Resources/Assets.xcassets/img_rank_3.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "img_rank_3.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
SodaLive/Resources/Assets.xcassets/img_rank_3.imageset/img_rank_3.png
vendored
Normal file
|
After Width: | Height: | Size: 16 KiB |
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "rank_1.pdf",
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "rank_2.pdf",
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "rank_3.pdf",
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 4.1 MiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,257 +1,296 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>fb608674328645232</string>
|
<string>fb608674328645232</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>voiceon-test</string>
|
<string>voiceon-test</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
<dict>
|
||||||
<key>FacebookAppID</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>608674328645232</string>
|
<string>Editor</string>
|
||||||
<key>FacebookAutoLogAppEventsEnabled</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<true/>
|
<array>
|
||||||
<key>FacebookClientToken</key>
|
<string>kakao20cf19413d63bfdfd30e8e6dff933d33</string>
|
||||||
<string>3775e6ea83236a685d264b6c5a1bbb4d</string>
|
</array>
|
||||||
<key>FacebookDisplayName</key>
|
</dict>
|
||||||
<string>보이스온 - Test1</string>
|
<dict>
|
||||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<false/>
|
<string>Editor</string>
|
||||||
<key>GADApplicationIdentifier</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<string>ca-app-pub-1299501215847962~3447556960</string>
|
<array>
|
||||||
<key>NSAdvertisingAttributionReportEndpoint</key>
|
<string>com.googleusercontent.apps.758414412471-3cf403jb4s405eu17qrfrcbs9ofhq369</string>
|
||||||
<string>https://appsflyer-skadnetwork.com/</string>
|
</array>
|
||||||
<key>NSAppTransportSecurity</key>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<true/>
|
<string>Editor</string>
|
||||||
</dict>
|
<key>CFBundleURLSchemes</key>
|
||||||
<key>SKAdNetworkItems</key>
|
<array>
|
||||||
<array>
|
<!-- Specify URL scheme to use when returning from LINE to your app. -->
|
||||||
<dict>
|
<string>line3rdp.$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
</array>
|
||||||
<string>cstr6suwn9.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
</array>
|
||||||
<dict>
|
<key>FacebookAppID</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>608674328645232</string>
|
||||||
<string>4fzdc2evr5.skadnetwork</string>
|
<key>FacebookAutoLogAppEventsEnabled</key>
|
||||||
</dict>
|
<true/>
|
||||||
<dict>
|
<key>FacebookClientToken</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>3775e6ea83236a685d264b6c5a1bbb4d</string>
|
||||||
<string>4pfyvq9l8r.skadnetwork</string>
|
<key>FacebookDisplayName</key>
|
||||||
</dict>
|
<string>보이스온 - Test1</string>
|
||||||
<dict>
|
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<false/>
|
||||||
<string>2fnua5tdw4.skadnetwork</string>
|
<key>GADApplicationIdentifier</key>
|
||||||
</dict>
|
<string>ca-app-pub-1299501215847962~3447556960</string>
|
||||||
<dict>
|
<key>GIDClientID</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>758414412471-3cf403jb4s405eu17qrfrcbs9ofhq369.apps.googleusercontent.com</string>
|
||||||
<string>ydx93a7ass.skadnetwork</string>
|
<key>NSAdvertisingAttributionReportEndpoint</key>
|
||||||
</dict>
|
<string>https://appsflyer-skadnetwork.com/</string>
|
||||||
<dict>
|
<key>NSAppTransportSecurity</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<dict>
|
||||||
<string>5a6flpkh64.skadnetwork</string>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
</dict>
|
<true/>
|
||||||
<dict>
|
</dict>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<key>SKAdNetworkItems</key>
|
||||||
<string>p78axxw29g.skadnetwork</string>
|
<array>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>cstr6suwn9.skadnetwork</string>
|
||||||
<string>v72qych5uu.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4fzdc2evr5.skadnetwork</string>
|
||||||
<string>ludvb6z3bs.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4pfyvq9l8r.skadnetwork</string>
|
||||||
<string>cp8zw746q7.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>2fnua5tdw4.skadnetwork</string>
|
||||||
<string>3sh42y64q3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ydx93a7ass.skadnetwork</string>
|
||||||
<string>c6k4g5qg8m.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>5a6flpkh64.skadnetwork</string>
|
||||||
<string>s39g8k73mm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>p78axxw29g.skadnetwork</string>
|
||||||
<string>3qy4746246.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v72qych5uu.skadnetwork</string>
|
||||||
<string>f38h382jlk.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ludvb6z3bs.skadnetwork</string>
|
||||||
<string>hs6bdukanm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>cp8zw746q7.skadnetwork</string>
|
||||||
<string>v4nxqhlyqp.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>3sh42y64q3.skadnetwork</string>
|
||||||
<string>wzmmz9fp6w.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>c6k4g5qg8m.skadnetwork</string>
|
||||||
<string>yclnxrl5pm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>s39g8k73mm.skadnetwork</string>
|
||||||
<string>t38b2kh725.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>3qy4746246.skadnetwork</string>
|
||||||
<string>7ug5zh24hu.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>f38h382jlk.skadnetwork</string>
|
||||||
<string>gta9lk7p23.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>hs6bdukanm.skadnetwork</string>
|
||||||
<string>vutu7akeur.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v4nxqhlyqp.skadnetwork</string>
|
||||||
<string>y5ghdn5j9k.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>wzmmz9fp6w.skadnetwork</string>
|
||||||
<string>n6fk4nfna4.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>yclnxrl5pm.skadnetwork</string>
|
||||||
<string>v9wttpbfk9.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>t38b2kh725.skadnetwork</string>
|
||||||
<string>n38lu8286q.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>7ug5zh24hu.skadnetwork</string>
|
||||||
<string>47vhws6wlr.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>gta9lk7p23.skadnetwork</string>
|
||||||
<string>kbd757ywx3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>vutu7akeur.skadnetwork</string>
|
||||||
<string>9t245vhmpl.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>y5ghdn5j9k.skadnetwork</string>
|
||||||
<string>eh6m2bh4zr.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>n6fk4nfna4.skadnetwork</string>
|
||||||
<string>a2p9lx4jpn.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v9wttpbfk9.skadnetwork</string>
|
||||||
<string>22mmun2rn5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>n38lu8286q.skadnetwork</string>
|
||||||
<string>4468km3ulz.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>47vhws6wlr.skadnetwork</string>
|
||||||
<string>2u9pt9hc89.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>kbd757ywx3.skadnetwork</string>
|
||||||
<string>8s468mfl3y.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>9t245vhmpl.skadnetwork</string>
|
||||||
<string>klf5c3l5u5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>eh6m2bh4zr.skadnetwork</string>
|
||||||
<string>ppxm28t8ap.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>a2p9lx4jpn.skadnetwork</string>
|
||||||
<string>ecpz2srf59.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>22mmun2rn5.skadnetwork</string>
|
||||||
<string>uw77j35x4d.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4468km3ulz.skadnetwork</string>
|
||||||
<string>pwa73g5rt2.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>2u9pt9hc89.skadnetwork</string>
|
||||||
<string>mlmmfzh3r3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>8s468mfl3y.skadnetwork</string>
|
||||||
<string>578prtvx9j.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>klf5c3l5u5.skadnetwork</string>
|
||||||
<string>4dzt52r2t5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ppxm28t8ap.skadnetwork</string>
|
||||||
<string>e5fvkxwrpn.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ecpz2srf59.skadnetwork</string>
|
||||||
<string>8c4e2ghe7u.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>uw77j35x4d.skadnetwork</string>
|
||||||
<string>zq492l623r.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>pwa73g5rt2.skadnetwork</string>
|
||||||
<string>3rd42ekr43.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>mlmmfzh3r3.skadnetwork</string>
|
||||||
<string>3qcr597p9d.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
</array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>UIAppFonts</key>
|
<string>578prtvx9j.skadnetwork</string>
|
||||||
<array>
|
</dict>
|
||||||
<string>gmarket_sans_bold.otf</string>
|
<dict>
|
||||||
<string>gmarket_sans_medium.otf</string>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>gmarket_sans_light.otf</string>
|
<string>4dzt52r2t5.skadnetwork</string>
|
||||||
<string>Pretendard-Bold.otf</string>
|
</dict>
|
||||||
<string>Pretendard-Medium.otf</string>
|
<dict>
|
||||||
<string>Pretendard-Light.otf</string>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>Pretendard-Regular.otf</string>
|
<string>e5fvkxwrpn.skadnetwork</string>
|
||||||
</array>
|
</dict>
|
||||||
<key>UIBackgroundModes</key>
|
<dict>
|
||||||
<array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>audio</string>
|
<string>8c4e2ghe7u.skadnetwork</string>
|
||||||
<string>fetch</string>
|
</dict>
|
||||||
<string>remote-notification</string>
|
<dict>
|
||||||
</array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
</dict>
|
<string>zq492l623r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3rd42ekr43.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qcr597p9d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Pretendard-Bold.otf</string>
|
||||||
|
<string>Pretendard-Medium.otf</string>
|
||||||
|
<string>Pretendard-Light.otf</string>
|
||||||
|
<string>Pretendard-Regular.otf</string>
|
||||||
|
<string>NotoSansJP-Bold.ttf</string>
|
||||||
|
<string>NotoSansJP-Medium.ttf</string>
|
||||||
|
<string>NotoSansJP-Light.ttf</string>
|
||||||
|
<string>NotoSansJP-Regular.ttf</string>
|
||||||
|
</array>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
<string>fetch</string>
|
||||||
|
<string>remote-notification</string>
|
||||||
|
</array>
|
||||||
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
<array>
|
||||||
|
<!-- 카카오톡으로 로그인 -->
|
||||||
|
<string>kakaokompassauth</string>
|
||||||
|
<!-- 카카오톡 공유 -->
|
||||||
|
<string>kakaolink</string>
|
||||||
|
<!-- 카카오톡 채널 -->
|
||||||
|
<string>kakaoplus</string>
|
||||||
|
<!-- 라인 -->
|
||||||
|
<string>lineauth2</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
12
SodaLive/Resources/Debug/en.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "VoiceOn-Test";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "Allow access to your photo library to select photos.";
|
||||||
|
"NSMicrophoneUsageDescription" = "Allow access to your microphone to record audio.";
|
||||||
|
"NSUserTrackingUsageDescription" = "Allow tracking to deliver personalized ads.";
|
||||||
|
"NSCameraUsageDescription" = "Allow access to your camera to take photos.";
|
||||||
12
SodaLive/Resources/Debug/ja.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "ボイスオン-テスト";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "写真を選択するために写真ライブラリへのアクセスが必要です。";
|
||||||
|
"NSMicrophoneUsageDescription" = "音声を録音するためにマイクへのアクセスが必要です。";
|
||||||
|
"NSUserTrackingUsageDescription" = "パーソナライズされた広告のためにトラッキングを許可してください。";
|
||||||
|
"NSCameraUsageDescription" = "写真を撮影するためにカメラへのアクセスが必要です。";
|
||||||
12
SodaLive/Resources/Debug/ko.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "보이스온-테스트";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "사진을 선택하려면 사진 라이브러리 접근이 필요합니다.";
|
||||||
|
"NSMicrophoneUsageDescription" = "음성 녹음을 위해 마이크 접근이 필요합니다.";
|
||||||
|
"NSUserTrackingUsageDescription" = "맞춤형 광고 제공을 위해 추적을 허용해 주세요.";
|
||||||
|
"NSCameraUsageDescription" = "사진을 촬영하려면 카메라 접근이 필요합니다.";
|
||||||
BIN
SodaLive/Resources/Font/NotoSansJP-Bold.ttf
Executable file
BIN
SodaLive/Resources/Font/NotoSansJP-Light.ttf
Executable file
BIN
SodaLive/Resources/Font/NotoSansJP-Medium.ttf
Executable file
BIN
SodaLive/Resources/Font/NotoSansJP-Regular.ttf
Executable file
@@ -1,257 +1,296 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>fb612448298237287</string>
|
<string>fb612448298237287</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>voiceon</string>
|
<string>voiceon</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
<dict>
|
||||||
<key>FacebookAppID</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>612448298237287</string>
|
<string>Editor</string>
|
||||||
<key>FacebookAutoLogAppEventsEnabled</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<true/>
|
<array>
|
||||||
<key>FacebookClientToken</key>
|
<string>kakao231cf78acfa8252fca38b9eedf87c5cb</string>
|
||||||
<string>32af760f4a7b7cb7e3b1e7ffd0b0da70</string>
|
</array>
|
||||||
<key>FacebookDisplayName</key>
|
</dict>
|
||||||
<string>보이스온</string>
|
<dict>
|
||||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<false/>
|
<string>Editor</string>
|
||||||
<key>GADApplicationIdentifier</key>
|
<key>CFBundleURLSchemes</key>
|
||||||
<string>ca-app-pub-1299501215847962~8852459715</string>
|
<array>
|
||||||
<key>NSAdvertisingAttributionReportEndpoint</key>
|
<string>com.googleusercontent.apps.983594297130-m6bv7lvc1lsetsvv3rk92etqc98uopqj</string>
|
||||||
<string>https://appsflyer-skadnetwork.com/</string>
|
</array>
|
||||||
<key>NSAppTransportSecurity</key>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<true/>
|
<string>Editor</string>
|
||||||
</dict>
|
<key>CFBundleURLSchemes</key>
|
||||||
<key>SKAdNetworkItems</key>
|
<array>
|
||||||
<array>
|
<!-- Specify URL scheme to use when returning from LINE to your app. -->
|
||||||
<dict>
|
<string>line3rdp.$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
</array>
|
||||||
<string>cstr6suwn9.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
</array>
|
||||||
<dict>
|
<key>FacebookAppID</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>612448298237287</string>
|
||||||
<string>4fzdc2evr5.skadnetwork</string>
|
<key>FacebookAutoLogAppEventsEnabled</key>
|
||||||
</dict>
|
<true/>
|
||||||
<dict>
|
<key>FacebookClientToken</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>32af760f4a7b7cb7e3b1e7ffd0b0da70</string>
|
||||||
<string>4pfyvq9l8r.skadnetwork</string>
|
<key>FacebookDisplayName</key>
|
||||||
</dict>
|
<string>보이스온</string>
|
||||||
<dict>
|
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<false/>
|
||||||
<string>2fnua5tdw4.skadnetwork</string>
|
<key>GADApplicationIdentifier</key>
|
||||||
</dict>
|
<string>ca-app-pub-1299501215847962~8852459715</string>
|
||||||
<dict>
|
<key>GIDClientID</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>983594297130-m6bv7lvc1lsetsvv3rk92etqc98uopqj.apps.googleusercontent.com</string>
|
||||||
<string>ydx93a7ass.skadnetwork</string>
|
<key>NSAdvertisingAttributionReportEndpoint</key>
|
||||||
</dict>
|
<string>https://appsflyer-skadnetwork.com/</string>
|
||||||
<dict>
|
<key>NSAppTransportSecurity</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<dict>
|
||||||
<string>5a6flpkh64.skadnetwork</string>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
</dict>
|
<true/>
|
||||||
<dict>
|
</dict>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<key>SKAdNetworkItems</key>
|
||||||
<string>p78axxw29g.skadnetwork</string>
|
<array>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>cstr6suwn9.skadnetwork</string>
|
||||||
<string>v72qych5uu.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4fzdc2evr5.skadnetwork</string>
|
||||||
<string>ludvb6z3bs.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4pfyvq9l8r.skadnetwork</string>
|
||||||
<string>cp8zw746q7.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>2fnua5tdw4.skadnetwork</string>
|
||||||
<string>3sh42y64q3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ydx93a7ass.skadnetwork</string>
|
||||||
<string>c6k4g5qg8m.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>5a6flpkh64.skadnetwork</string>
|
||||||
<string>s39g8k73mm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>p78axxw29g.skadnetwork</string>
|
||||||
<string>3qy4746246.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v72qych5uu.skadnetwork</string>
|
||||||
<string>f38h382jlk.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ludvb6z3bs.skadnetwork</string>
|
||||||
<string>hs6bdukanm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>cp8zw746q7.skadnetwork</string>
|
||||||
<string>v4nxqhlyqp.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>3sh42y64q3.skadnetwork</string>
|
||||||
<string>wzmmz9fp6w.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>c6k4g5qg8m.skadnetwork</string>
|
||||||
<string>yclnxrl5pm.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>s39g8k73mm.skadnetwork</string>
|
||||||
<string>t38b2kh725.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>3qy4746246.skadnetwork</string>
|
||||||
<string>7ug5zh24hu.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>f38h382jlk.skadnetwork</string>
|
||||||
<string>gta9lk7p23.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>hs6bdukanm.skadnetwork</string>
|
||||||
<string>vutu7akeur.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v4nxqhlyqp.skadnetwork</string>
|
||||||
<string>y5ghdn5j9k.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>wzmmz9fp6w.skadnetwork</string>
|
||||||
<string>n6fk4nfna4.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>yclnxrl5pm.skadnetwork</string>
|
||||||
<string>v9wttpbfk9.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>t38b2kh725.skadnetwork</string>
|
||||||
<string>n38lu8286q.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>7ug5zh24hu.skadnetwork</string>
|
||||||
<string>47vhws6wlr.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>gta9lk7p23.skadnetwork</string>
|
||||||
<string>kbd757ywx3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>vutu7akeur.skadnetwork</string>
|
||||||
<string>9t245vhmpl.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>y5ghdn5j9k.skadnetwork</string>
|
||||||
<string>eh6m2bh4zr.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>n6fk4nfna4.skadnetwork</string>
|
||||||
<string>a2p9lx4jpn.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>v9wttpbfk9.skadnetwork</string>
|
||||||
<string>22mmun2rn5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>n38lu8286q.skadnetwork</string>
|
||||||
<string>4468km3ulz.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>47vhws6wlr.skadnetwork</string>
|
||||||
<string>2u9pt9hc89.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>kbd757ywx3.skadnetwork</string>
|
||||||
<string>8s468mfl3y.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>9t245vhmpl.skadnetwork</string>
|
||||||
<string>klf5c3l5u5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>eh6m2bh4zr.skadnetwork</string>
|
||||||
<string>ppxm28t8ap.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>a2p9lx4jpn.skadnetwork</string>
|
||||||
<string>ecpz2srf59.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>22mmun2rn5.skadnetwork</string>
|
||||||
<string>uw77j35x4d.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>4468km3ulz.skadnetwork</string>
|
||||||
<string>pwa73g5rt2.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>2u9pt9hc89.skadnetwork</string>
|
||||||
<string>mlmmfzh3r3.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>8s468mfl3y.skadnetwork</string>
|
||||||
<string>578prtvx9j.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>klf5c3l5u5.skadnetwork</string>
|
||||||
<string>4dzt52r2t5.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ppxm28t8ap.skadnetwork</string>
|
||||||
<string>e5fvkxwrpn.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>ecpz2srf59.skadnetwork</string>
|
||||||
<string>8c4e2ghe7u.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>uw77j35x4d.skadnetwork</string>
|
||||||
<string>zq492l623r.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>pwa73g5rt2.skadnetwork</string>
|
||||||
<string>3rd42ekr43.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
<dict>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>SKAdNetworkIdentifier</key>
|
<string>mlmmfzh3r3.skadnetwork</string>
|
||||||
<string>3qcr597p9d.skadnetwork</string>
|
</dict>
|
||||||
</dict>
|
<dict>
|
||||||
</array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<key>UIAppFonts</key>
|
<string>578prtvx9j.skadnetwork</string>
|
||||||
<array>
|
</dict>
|
||||||
<string>gmarket_sans_bold.otf</string>
|
<dict>
|
||||||
<string>gmarket_sans_medium.otf</string>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>gmarket_sans_light.otf</string>
|
<string>4dzt52r2t5.skadnetwork</string>
|
||||||
<string>Pretendard-Bold.otf</string>
|
</dict>
|
||||||
<string>Pretendard-Medium.otf</string>
|
<dict>
|
||||||
<string>Pretendard-Light.otf</string>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>Pretendard-Regular.otf</string>
|
<string>e5fvkxwrpn.skadnetwork</string>
|
||||||
</array>
|
</dict>
|
||||||
<key>UIBackgroundModes</key>
|
<dict>
|
||||||
<array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
<string>audio</string>
|
<string>8c4e2ghe7u.skadnetwork</string>
|
||||||
<string>fetch</string>
|
</dict>
|
||||||
<string>remote-notification</string>
|
<dict>
|
||||||
</array>
|
<key>SKAdNetworkIdentifier</key>
|
||||||
</dict>
|
<string>zq492l623r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3rd42ekr43.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qcr597p9d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Pretendard-Bold.otf</string>
|
||||||
|
<string>Pretendard-Medium.otf</string>
|
||||||
|
<string>Pretendard-Light.otf</string>
|
||||||
|
<string>Pretendard-Regular.otf</string>
|
||||||
|
<string>NotoSansJP-Bold.ttf</string>
|
||||||
|
<string>NotoSansJP-Medium.ttf</string>
|
||||||
|
<string>NotoSansJP-Light.ttf</string>
|
||||||
|
<string>NotoSansJP-Regular.ttf</string>
|
||||||
|
</array>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
<string>fetch</string>
|
||||||
|
<string>remote-notification</string>
|
||||||
|
</array>
|
||||||
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
<array>
|
||||||
|
<!-- 카카오톡으로 로그인 -->
|
||||||
|
<string>kakaokompassauth</string>
|
||||||
|
<!-- 카카오톡 공유 -->
|
||||||
|
<string>kakaolink</string>
|
||||||
|
<!-- 카카오톡 채널 -->
|
||||||
|
<string>kakaoplus</string>
|
||||||
|
<!-- Specify URL scheme to use when launching LINE from your app. -->
|
||||||
|
<string>lineauth2</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
9966
SodaLive/Resources/Localizable.xcstrings
Normal file
12
SodaLive/Resources/en.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "VoiceOn";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "Allow access to your photo library to select photos.";
|
||||||
|
"NSMicrophoneUsageDescription" = "Allow access to your microphone to record audio.";
|
||||||
|
"NSUserTrackingUsageDescription" = "Allow tracking to deliver personalized ads.";
|
||||||
|
"NSCameraUsageDescription" = "Allow access to your camera to take photos.";
|
||||||
12
SodaLive/Resources/ja.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "ボイスオン";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "写真を選択するために写真ライブラリへのアクセスが必要です。";
|
||||||
|
"NSMicrophoneUsageDescription" = "音声を録音するためにマイクへのアクセスが必要です。";
|
||||||
|
"NSUserTrackingUsageDescription" = "パーソナライズされた広告のためにトラッキングを許可してください。";
|
||||||
|
"NSCameraUsageDescription" = "写真を撮影するためにカメラへのアクセスが必要です。";
|
||||||
12
SodaLive/Resources/ko.lproj/InfoPlist.strings
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
InfoPlist.strings
|
||||||
|
SodaLive
|
||||||
|
|
||||||
|
Created by klaus on 12/5/25.
|
||||||
|
|
||||||
|
*/
|
||||||
|
"CFBundleDisplayName" = "보이스온";
|
||||||
|
"NSPhotoLibraryUsageDescription" = "사진을 선택하려면 사진 라이브러리 접근이 필요합니다.";
|
||||||
|
"NSMicrophoneUsageDescription" = "음성 녹음을 위해 마이크 접근이 필요합니다.";
|
||||||
|
"NSUserTrackingUsageDescription" = "맞춤형 광고 제공을 위해 추적을 허용해 주세요.";
|
||||||
|
"NSCameraUsageDescription" = "사진을 촬영하려면 카메라 접근이 필요합니다.";
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>aps-environment</key>
|
<key>aps-environment</key>
|
||||||
<string>development</string>
|
<string>development</string>
|
||||||
|
<key>com.apple.developer.applesignin</key>
|
||||||
|
<array>
|
||||||
|
<string>Default</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.developer.associated-domains</key>
|
<key>com.apple.developer.associated-domains</key>
|
||||||
<array>
|
<array>
|
||||||
<string>applinks:voiceon.onelink.me</string>
|
<string>applinks:voiceon.onelink.me</string>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import FBSDKCoreKit
|
|||||||
import FirebaseCore
|
import FirebaseCore
|
||||||
import FirebaseAnalytics
|
import FirebaseAnalytics
|
||||||
import FirebaseMessaging
|
import FirebaseMessaging
|
||||||
|
import LineSDK
|
||||||
|
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
FirebaseApp.configure()
|
FirebaseApp.configure()
|
||||||
|
LoginManager.shared.setup(channelID: LINE_CHANNEL_ID, universalLinkURL: nil)
|
||||||
Notifly.initialize(projectId: NOTIFLY_PROJECT_ID, username: NOTIFLY_USERNAME, password: NOTIFLY_PASSWORD)
|
Notifly.initialize(projectId: NOTIFLY_PROJECT_ID, username: NOTIFLY_USERNAME, password: NOTIFLY_PASSWORD)
|
||||||
Messaging.messaging().delegate = self
|
Messaging.messaging().delegate = self
|
||||||
setupAppsFlyer()
|
setupAppsFlyer()
|
||||||
@@ -75,6 +77,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
|
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||||
|
_ = LoginManager.shared.application(application, open: userActivity.webpageURL)
|
||||||
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
|
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class AppState: ObservableObject {
|
|||||||
@Published var purchasedContentId = 0
|
@Published var purchasedContentId = 0
|
||||||
@Published var purchasedContentOrderType = OrderType.KEEP
|
@Published var purchasedContentOrderType = OrderType.KEEP
|
||||||
|
|
||||||
@Published var isChangeAdultContentVisible = false
|
@Published var isRestartApp = false
|
||||||
@Published var startTab: HomeViewModel.CurrentTab = .home
|
@Published var startTab: HomeViewModel.CurrentTab = .home
|
||||||
|
|
||||||
@Published var marketingUtmSource = ""
|
@Published var marketingUtmSource = ""
|
||||||
@@ -74,4 +74,13 @@ class AppState: ObservableObject {
|
|||||||
self.appStep = .main
|
self.appStep = .main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 언어 적용 직후 앱을 소프트 재시작(스플래시 -> 메인)하여 전역 UI를 새로고침
|
||||||
|
func softRestart() {
|
||||||
|
isRestartApp = true
|
||||||
|
setAppStep(step: .splash)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
|
self.setAppStep(step: .main)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ enum AppStep {
|
|||||||
|
|
||||||
case settings
|
case settings
|
||||||
|
|
||||||
|
case languageSettings
|
||||||
|
|
||||||
case notices
|
case notices
|
||||||
|
|
||||||
case noticeDetail(notice: NoticeItem)
|
case noticeDetail(notice: NoticeItem)
|
||||||
@@ -128,7 +130,7 @@ enum AppStep {
|
|||||||
|
|
||||||
case seriesDetail(seriesId: Int)
|
case seriesDetail(seriesId: Int)
|
||||||
|
|
||||||
case seriesAll(creatorId: Int)
|
case seriesAll(creatorId: Int? = nil, creatorNickname: String? = nil, isOriginal: Bool = false, isCompleted: Bool = false)
|
||||||
|
|
||||||
case seriesContentAll(seriesId: Int, seriesTitle: String)
|
case seriesContentAll(seriesId: Int, seriesTitle: String)
|
||||||
|
|
||||||
@@ -169,4 +171,8 @@ enum AppStep {
|
|||||||
case newCharacterAll
|
case newCharacterAll
|
||||||
|
|
||||||
case originalWorkDetail(originalId: Int)
|
case originalWorkDetail(originalId: Int)
|
||||||
|
|
||||||
|
case contentAll(isFree: Bool = false, isPointOnly: Bool = false)
|
||||||
|
|
||||||
|
case seriesMain
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import Kingfisher
|
|||||||
|
|
||||||
import FBSDKCoreKit
|
import FBSDKCoreKit
|
||||||
import AppsFlyerLib
|
import AppsFlyerLib
|
||||||
|
import GoogleSignIn
|
||||||
|
import KakaoSDKCommon
|
||||||
|
import KakaoSDKAuth
|
||||||
|
import LineSDK
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct SodaLiveApp: App {
|
struct SodaLiveApp: App {
|
||||||
@@ -19,9 +23,13 @@ struct SodaLiveApp: App {
|
|||||||
@ObservedObject var viewModel = AppViewModel()
|
@ObservedObject var viewModel = AppViewModel()
|
||||||
|
|
||||||
@StateObject var canPgPaymentViewModel = CanPgPaymentViewModel()
|
@StateObject var canPgPaymentViewModel = CanPgPaymentViewModel()
|
||||||
|
@StateObject private var languageEnvironment = LanguageContainer.environment
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
configureImageCache()
|
configureImageCache()
|
||||||
|
// 앱 시작 직후, 초기 네트워크 요청도 올바른 언어 헤더를 갖도록 동기 초기화
|
||||||
|
LanguageHeaderProvider.initialize()
|
||||||
|
KakaoSDK.initSDK(appKey: KAKAO_APP_KEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func configureImageCache() {
|
private func configureImageCache() {
|
||||||
@@ -40,6 +48,10 @@ struct SodaLiveApp: App {
|
|||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView(canPgPaymentViewModel: canPgPaymentViewModel)
|
ContentView(canPgPaymentViewModel: canPgPaymentViewModel)
|
||||||
|
.environment(\.locale, languageEnvironment.locale)
|
||||||
|
.task {
|
||||||
|
await LanguageContainer.service.bootstrap()
|
||||||
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification)) { _ in
|
||||||
CreatorCommunityMediaPlayerManager.shared.pauseContent()
|
CreatorCommunityMediaPlayerManager.shared.pauseContent()
|
||||||
// 백그라운드 전환 시 메모리 캐시 정리
|
// 백그라운드 전환 시 메모리 캐시 정리
|
||||||
@@ -61,14 +73,21 @@ struct SodaLiveApp: App {
|
|||||||
.onOpenURL { url in
|
.onOpenURL { url in
|
||||||
DEBUG_LOG("I have received a URL through a custom scheme! \(url.absoluteString)")
|
DEBUG_LOG("I have received a URL through a custom scheme! \(url.absoluteString)")
|
||||||
|
|
||||||
|
if KakaoSDKAuth.AuthApi.isKakaoTalkLoginUrl(url) {
|
||||||
|
_ = AuthController.handleOpenUrl(url: url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if let comps = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
if let comps = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||||
url.scheme?.lowercased() == APPSCHEME.lowercased(),
|
url.scheme?.lowercased() == APPSCHEME.lowercased(),
|
||||||
comps.host?.lowercased() == "payverse",
|
comps.host?.lowercased() == "payverse",
|
||||||
comps.path.lowercased() == "/result" {
|
comps.path.lowercased() == "/result" {
|
||||||
canPgPaymentViewModel.handleVerifyOpenURL(url)
|
canPgPaymentViewModel.handleVerifyOpenURL(url)
|
||||||
} else {
|
} else {
|
||||||
|
_ = LoginManager.shared.application(UIApplication.shared, open: url, options: [:])
|
||||||
ApplicationDelegate.shared.application(UIApplication.shared, open: url, options: [:])
|
ApplicationDelegate.shared.application(UIApplication.shared, open: url, options: [:])
|
||||||
AppsFlyerLib.shared().handleOpen(url)
|
AppsFlyerLib.shared().handleOpen(url)
|
||||||
|
GIDSignIn.sharedInstance.handle(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct ApplyMethodView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("오디션 지원방식")
|
Text("오디션 지원방식")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
.padding(.top, 33.3)
|
.padding(.top, 33.3)
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ struct ApplyMethodView: View {
|
|||||||
Image("ic_upload")
|
Image("ic_upload")
|
||||||
|
|
||||||
Text("파일 업로드")
|
Text("파일 업로드")
|
||||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
@@ -61,7 +61,7 @@ struct ApplyMethodView: View {
|
|||||||
Image("ic_mic_color_button")
|
Image("ic_mic_color_button")
|
||||||
|
|
||||||
Text("바로 녹음")
|
Text("바로 녹음")
|
||||||
.font(.custom(Font.medium.rawValue, size: 14.7))
|
.appFont(size: 14.7, weight: .medium)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
@@ -82,7 +82,7 @@ struct ApplyMethodView: View {
|
|||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("※ 파일은 mp3, aac만 업로드 가능")
|
Text("※ 파일은 mp3, aac만 업로드 가능")
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color.gray77)
|
.foregroundColor(Color.gray77)
|
||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ struct AuditionApplicantItemView: View {
|
|||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text(item.nickname.count > 9 ? "\(item.nickname.prefix(9))..." : item.nickname)
|
Text(item.nickname.count > 9 ? "\(item.nickname.prefix(9))..." : item.nickname)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
AppState.shared.setAppStep(step: .creatorDetail(userId: item.memberId))
|
AppState.shared.setAppStep(step: .creatorDetail(userId: item.memberId))
|
||||||
@@ -53,7 +53,7 @@ struct AuditionApplicantItemView: View {
|
|||||||
|
|
||||||
if soundManager.applicantId == item.applicantId {
|
if soundManager.applicantId == item.applicantId {
|
||||||
Text("\(secondsToMinutesSeconds(Int(soundManager.currentTime)))/\(secondsToMinutesSeconds(Int(soundManager.duration)))")
|
Text("\(secondsToMinutesSeconds(Int(soundManager.currentTime)))/\(secondsToMinutesSeconds(Int(soundManager.duration)))")
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color.gray77)
|
.foregroundColor(Color.gray77)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ struct AuditionApplicantItemView: View {
|
|||||||
Image("ic_heart_vote")
|
Image("ic_heart_vote")
|
||||||
|
|
||||||
Text("\(item.voteCount)")
|
Text("\(item.voteCount)")
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.foregroundColor(Color.gray77)
|
.foregroundColor(Color.gray77)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("오디션 녹음")
|
Text("오디션 녹음")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -46,7 +46,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text(soundManager.timeString)
|
Text(soundManager.timeString)
|
||||||
.font(.custom(Font.bold.rawValue, size: 33.3))
|
.appFont(size: 33.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.top, 80)
|
.padding(.top, 80)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("삭제")
|
Text("삭제")
|
||||||
.font(.custom(Font.medium.rawValue, size: 15.3))
|
.appFont(size: 15.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb.opacity(0))
|
.foregroundColor(Color.graybb.opacity(0))
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -100,7 +100,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("삭제")
|
Text("삭제")
|
||||||
.font(.custom(Font.medium.rawValue, size: 15.3))
|
.appFont(size: 15.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
soundManager.stopAudio()
|
soundManager.stopAudio()
|
||||||
@@ -114,7 +114,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
|
|
||||||
HStack(spacing: 13.3) {
|
HStack(spacing: 13.3) {
|
||||||
Text("다시 녹음")
|
Text("다시 녹음")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
.frame(width: (proxy.size.width - 40) / 3, height: 50)
|
.frame(width: (proxy.size.width - 40) / 3, height: 50)
|
||||||
.background(Color.button.opacity(0.2))
|
.background(Color.button.opacity(0.2))
|
||||||
@@ -130,7 +130,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("녹음완료")
|
Text("녹음완료")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.frame(width: (proxy.size.width - 40) * 2 / 3, height: 50)
|
.frame(width: (proxy.size.width - 40) * 2 / 3, height: 50)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
@@ -178,7 +178,7 @@ struct AuditionApplicantRecordingView: View {
|
|||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.padding(.horizontal, 6.7)
|
.padding(.horizontal, 6.7)
|
||||||
.frame(width: screenSize().width - 66.7, alignment: .center)
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ struct AuditionApplyView: View {
|
|||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("오디션 지원")
|
Text("오디션 지원")
|
||||||
.font(.custom(Font.medium.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -46,7 +46,7 @@ struct AuditionApplyView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("녹음파일")
|
Text("녹음파일")
|
||||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
.appFont(size: 16.7, weight: .bold)
|
||||||
.foregroundColor(.grayee)
|
.foregroundColor(.grayee)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ struct AuditionApplyView: View {
|
|||||||
Image("ic_note_square")
|
Image("ic_note_square")
|
||||||
|
|
||||||
Text(filename)
|
Text(filename)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(.grayd2)
|
.foregroundColor(.grayd2)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -67,7 +67,7 @@ struct AuditionApplyView: View {
|
|||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
Text("연락처")
|
Text("연락처")
|
||||||
.font(.custom(Font.bold.rawValue, size: 16.7))
|
.appFont(size: 16.7, weight: .bold)
|
||||||
.foregroundColor(.grayee)
|
.foregroundColor(.grayee)
|
||||||
.padding(.top, 15)
|
.padding(.top, 15)
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ struct AuditionApplyView: View {
|
|||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.keyboardType(.decimalPad)
|
.keyboardType(.decimalPad)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(.grayee)
|
.foregroundColor(.grayee)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
.padding(.vertical, 17)
|
.padding(.vertical, 17)
|
||||||
@@ -90,7 +90,7 @@ struct AuditionApplyView: View {
|
|||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
Text("보이스온 오디오 드라마 오디션 합격시 개인 연락을 위한 개인 정보(연락처) 수집 및 활용에 동의합니다.\n오디션 지원자는 개인정보 수집 및 활용 동의에 거부할 권리가 있으며 비동의시 오디션 지원은 취소 됩니다.")
|
Text("보이스온 오디오 드라마 오디션 합격시 개인 연락을 위한 개인 정보(연락처) 수집 및 활용에 동의합니다.\n오디션 지원자는 개인정보 수집 및 활용 동의에 거부할 권리가 있으며 비동의시 오디션 지원은 취소 됩니다.")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.lineSpacing(3)
|
.lineSpacing(3)
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ struct AuditionApplyView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("오디션 지원하기")
|
Text("오디션 지원하기")
|
||||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -137,7 +137,7 @@ struct AuditionApplyView: View {
|
|||||||
Text(errorMessage)
|
Text(errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: screenSize().width - 66.7, alignment: .center)
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct AuditionItemView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
Text(item.title)
|
Text(item.title)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ struct AuditionView: View {
|
|||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
Text("오디션")
|
Text("오디션")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,13 +44,13 @@ struct AuditionView: View {
|
|||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("보이스온 오디션 이용방법")
|
Text("보이스온 오디션 이용방법")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("자세히>")
|
Text("자세히>")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
@@ -74,17 +74,17 @@ struct AuditionView: View {
|
|||||||
VStack(alignment: .leading, spacing: 25) {
|
VStack(alignment: .leading, spacing: 25) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("오디션")
|
Text("오디션")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
|
|
||||||
Text(" ON")
|
Text(" ON")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.mainRed)
|
.foregroundColor(Color.mainRed)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("총 \(viewModel.inProgressCount)개")
|
Text("총 \(viewModel.inProgressCount)개")
|
||||||
.font(.custom(Font.medium.rawValue, size: 11.3))
|
.appFont(size: 11.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,17 +112,17 @@ struct AuditionView: View {
|
|||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("오디션")
|
Text("오디션")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
|
|
||||||
Text(" OFF")
|
Text(" OFF")
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
.appFont(size: 18.3, weight: .bold)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("총 \(viewModel.completedCount)개")
|
Text("총 \(viewModel.completedCount)개")
|
||||||
.font(.custom(Font.medium.rawValue, size: 11.3))
|
.appFont(size: 11.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
}
|
}
|
||||||
.padding(.top, 30)
|
.padding(.top, 30)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ struct AuditionDetailView: View {
|
|||||||
.cornerRadius(6.7)
|
.cornerRadius(6.7)
|
||||||
|
|
||||||
Text("오디션 정보")
|
Text("오디션 정보")
|
||||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
.appFont(size: 14.7, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.padding(.top, 15)
|
.padding(.top, 15)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ struct AuditionDetailView: View {
|
|||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
Text("오디션 캐릭터")
|
Text("오디션 캐릭터")
|
||||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
.appFont(size: 14.7, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.padding(.top, 15)
|
.padding(.top, 15)
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ struct AuditionDetailView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: screenSize().width - 66.7, alignment: .center)
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct AuditionDetailRoleItemView: View {
|
|||||||
)
|
)
|
||||||
|
|
||||||
Text(item.isComplete ? "모집완료" : "모집중")
|
Text(item.isComplete ? "모집완료" : "모집중")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.padding(.horizontal, 9)
|
.padding(.horizontal, 9)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -43,7 +43,7 @@ struct AuditionDetailRoleItemView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
Text(item.name)
|
Text(item.name)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
HStack(spacing: 14) {
|
HStack(spacing: 14) {
|
||||||
if let url = URL(string: roleDetail.originalWorkUrl), UIApplication.shared.canOpenURL(url) {
|
if let url = URL(string: roleDetail.originalWorkUrl), UIApplication.shared.canOpenURL(url) {
|
||||||
Text("원작 보러가기")
|
Text("원작 보러가기")
|
||||||
.font(.custom(Font.bold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -60,7 +60,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
|
|
||||||
if let url = URL(string: roleDetail.auditionScriptUrl), UIApplication.shared.canOpenURL(url) {
|
if let url = URL(string: roleDetail.auditionScriptUrl), UIApplication.shared.canOpenURL(url) {
|
||||||
Text("오디션 대본 확인")
|
Text("오디션 대본 확인")
|
||||||
.font(.custom(Font.bold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -75,7 +75,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 13.3) {
|
VStack(alignment: .leading, spacing: 13.3) {
|
||||||
Text("오디션 캐릭터 정보")
|
Text("오디션 캐릭터 정보")
|
||||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
.appFont(size: 14.7, weight: .bold)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
|
|
||||||
ExpandableTextView(text: roleDetail.information)
|
ExpandableTextView(text: roleDetail.information)
|
||||||
@@ -84,28 +84,28 @@ struct AuditionRoleDetailView: View {
|
|||||||
|
|
||||||
if viewModel.applicantList.isEmpty {
|
if viewModel.applicantList.isEmpty {
|
||||||
Text("지원자가 없습니다.")
|
Text("지원자가 없습니다.")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13))
|
.appFont(size: 13, weight: .medium)
|
||||||
.foregroundColor(Color.grayee)
|
.foregroundColor(Color.grayee)
|
||||||
.padding(.top, 15)
|
.padding(.top, 15)
|
||||||
} else {
|
} else {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("참여자")
|
Text("참여자")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
|
|
||||||
Text("\(viewModel.totalCount)")
|
Text("\(viewModel.totalCount)")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.button)
|
.foregroundColor(Color.button)
|
||||||
.padding(.leading, 2.3)
|
.padding(.leading, 2.3)
|
||||||
|
|
||||||
Text("명")
|
Text("명")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(Color.graybb)
|
.foregroundColor(Color.graybb)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("최신순")
|
Text("최신순")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
viewModel.sortType == .NEWEST ? Color.button : Color.graybb
|
viewModel.sortType == .NEWEST ? Color.button : Color.graybb
|
||||||
)
|
)
|
||||||
@@ -114,7 +114,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("좋아요순")
|
Text("좋아요순")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.appFont(size: 13.3, weight: .medium)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
viewModel.sortType == .LIKES ? Color.button : Color.graybb
|
viewModel.sortType == .LIKES ? Color.button : Color.graybb
|
||||||
)
|
)
|
||||||
@@ -156,7 +156,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: screenSize().width - 66.7, alignment: .center)
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
@@ -171,7 +171,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
Text(soundManager.errorMessage)
|
Text(soundManager.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: screenSize().width - 66.7, alignment: .center)
|
.frame(width: screenSize().width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
@@ -190,7 +190,7 @@ struct AuditionRoleDetailView: View {
|
|||||||
|
|
||||||
if let roleDetail = viewModel.auditionRoleDetail {
|
if let roleDetail = viewModel.auditionRoleDetail {
|
||||||
Text(roleDetail.isAlreadyApplicant ? "오디션 재지원" : "오디션 지원")
|
Text(roleDetail.isAlreadyApplicant ? "오디션 재지원" : "오디션 지원")
|
||||||
.font(.custom(Font.bold.rawValue, size: 15.3))
|
.appFont(size: 15.3, weight: .bold)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.padding(14)
|
.padding(14)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
|
|||||||
@@ -79,11 +79,10 @@ private struct AutoSlideCharacterBannerPage: View {
|
|||||||
let width: CGFloat
|
let width: CGFloat
|
||||||
let height: CGFloat
|
let height: CGFloat
|
||||||
let onTap: (CharacterBannerResponse) -> Void
|
let onTap: (CharacterBannerResponse) -> Void
|
||||||
@State private var boundURL: URL?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if let boundURL {
|
if let boundURL = URL(string: item.imageUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? item.imageUrl){
|
||||||
DownsampledKFImage(
|
DownsampledKFImage(
|
||||||
url: boundURL,
|
url: boundURL,
|
||||||
size: CGSize(width: width, height: height)
|
size: CGSize(width: width, height: height)
|
||||||
@@ -96,14 +95,5 @@ private struct AutoSlideCharacterBannerPage: View {
|
|||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture { onTap(item) }
|
.onTapGesture { onTap(item) }
|
||||||
.onAppear {
|
|
||||||
let encoded = item.imageUrl.addingPercentEncoding(
|
|
||||||
withAllowedCharacters: .urlQueryAllowed
|
|
||||||
) ?? item.imageUrl
|
|
||||||
boundURL = URL(string: encoded)
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
boundURL = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ struct Character: Decodable {
|
|||||||
let name: String
|
let name: String
|
||||||
let description: String?
|
let description: String?
|
||||||
let imageUrl: String
|
let imageUrl: String
|
||||||
|
let isNew: Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ enum CharacterApi {
|
|||||||
case getMyCharacterImageList(characterId: Int64, page: Int, size: Int)
|
case getMyCharacterImageList(characterId: Int64, page: Int, size: Int)
|
||||||
case purchaseCharacterImage(imageId: Int)
|
case purchaseCharacterImage(imageId: Int)
|
||||||
case getRecentCharacters(page: Int, size: Int)
|
case getRecentCharacters(page: Int, size: Int)
|
||||||
|
case refreshRecommendCharacters
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CharacterApi: TargetType {
|
extension CharacterApi: TargetType {
|
||||||
@@ -39,6 +40,9 @@ extension CharacterApi: TargetType {
|
|||||||
|
|
||||||
case .getRecentCharacters:
|
case .getRecentCharacters:
|
||||||
return "/api/chat/character/recent"
|
return "/api/chat/character/recent"
|
||||||
|
|
||||||
|
case .refreshRecommendCharacters:
|
||||||
|
return "/api/chat/character/recommend"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +58,7 @@ extension CharacterApi: TargetType {
|
|||||||
|
|
||||||
var task: Moya.Task {
|
var task: Moya.Task {
|
||||||
switch self {
|
switch self {
|
||||||
case .getCharacterHome, .getCharacterDetail:
|
case .getCharacterHome, .getCharacterDetail, .refreshRecommendCharacters:
|
||||||
return .requestPlain
|
return .requestPlain
|
||||||
|
|
||||||
case .getRecentCharacters(let page, let size):
|
case .getRecentCharacters(let page, let size):
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ struct CharacterHomeResponse: Decodable {
|
|||||||
let recentCharacters: [RecentCharacter]
|
let recentCharacters: [RecentCharacter]
|
||||||
let popularCharacters: [Character]
|
let popularCharacters: [Character]
|
||||||
let newCharacters: [Character]
|
let newCharacters: [Character]
|
||||||
let curationSections: [CurationSection]
|
let recommendCharacters: [Character]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,31 +21,50 @@ struct CharacterItemView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
ZStack(alignment: .bottomLeading) {
|
ZStack(alignment: .leading) {
|
||||||
DownsampledKFImage(
|
DownsampledKFImage(
|
||||||
url: URL(string: character.imageUrl),
|
url: URL(string: character.imageUrl),
|
||||||
size: CGSize(width: size, height: size)
|
size: CGSize(width: size, height: size)
|
||||||
)
|
)
|
||||||
.cornerRadius(12)
|
.cornerRadius(12)
|
||||||
|
|
||||||
if isShowRank {
|
VStack {
|
||||||
Text("\(rank)")
|
if character.isNew {
|
||||||
.font(.custom(Font.preBold.rawValue, size: 72))
|
HStack {
|
||||||
.foregroundColor(.white)
|
Spacer()
|
||||||
.lineLimit(1)
|
|
||||||
.frame(height: capHeight)
|
Text("N")
|
||||||
|
.appFont(size: 18, weight: .regular)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(width: 30, height: 30)
|
||||||
|
.background(Color.button)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
.padding(.top, 8)
|
||||||
|
.padding(.trailing, 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if isShowRank {
|
||||||
|
Text("\(rank)")
|
||||||
|
.appFont(size: 72, weight: .bold)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.lineLimit(1)
|
||||||
|
.frame(height: capHeight)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(character.name)
|
Text(character.name)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 18))
|
.appFont(size: 18, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|
||||||
if let desc = character.description, !desc.isEmpty {
|
if let desc = character.description, !desc.isEmpty {
|
||||||
Text(desc)
|
Text(desc)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
@@ -56,7 +75,7 @@ struct CharacterItemView: View {
|
|||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
CharacterItemView(
|
CharacterItemView(
|
||||||
character: Character(characterId: 1, name: "찰리", description: "새로운 친구", imageUrl: "https://picsum.photos/300"),
|
character: Character(characterId: 1, name: "찰리", description: "새로운 친구", imageUrl: "https://picsum.photos/300", isNew: true),
|
||||||
size: 168,
|
size: 168,
|
||||||
rank: 20,
|
rank: 20,
|
||||||
isShowRank: true
|
isShowRank: true
|
||||||
|
|||||||
@@ -16,4 +16,8 @@ class CharacterRepository {
|
|||||||
func getCharacterMain() -> AnyPublisher<Response, MoyaError> {
|
func getCharacterMain() -> AnyPublisher<Response, MoyaError> {
|
||||||
return api.requestPublisher(.getCharacterHome)
|
return api.requestPublisher(.getCharacterHome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshRecommendCharacters() -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.refreshRecommendCharacters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct CharacterSectionView: View {
|
struct CharacterSectionView: View {
|
||||||
let title: String
|
let title: LocalizedStringResource
|
||||||
let items: [Character]
|
let items: [Character]
|
||||||
let isShowRank: Bool
|
let isShowRank: Bool
|
||||||
var trailingTitle: String? = nil
|
var trailingTitle: String? = nil
|
||||||
@@ -19,12 +19,12 @@ struct CharacterSectionView: View {
|
|||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.custom(Font.preBold.rawValue, size: 20))
|
.appFont(size: 24, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
Spacer()
|
Spacer()
|
||||||
if let trailingTitle = trailingTitle {
|
if let trailingTitle = trailingTitle {
|
||||||
Text(trailingTitle)
|
Text(trailingTitle)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "90A4AE"))
|
.foregroundColor(Color(hex: "90A4AE"))
|
||||||
.onTapGesture { onTapTrailing?() }
|
.onTapGesture { onTapTrailing?() }
|
||||||
}
|
}
|
||||||
@@ -54,8 +54,8 @@ struct CharacterSectionView: View {
|
|||||||
CharacterSectionView(
|
CharacterSectionView(
|
||||||
title: "신규 캐릭터",
|
title: "신규 캐릭터",
|
||||||
items: [
|
items: [
|
||||||
Character(characterId: 1, name: "찰리", description: "새로운 친구", imageUrl: "https://picsum.photos/300"),
|
Character(characterId: 1, name: "찰리", description: "새로운 친구", imageUrl: "https://picsum.photos/300", isNew: true),
|
||||||
Character(characterId: 2, name: "데이지", description: "", imageUrl: "https://picsum.photos/300")
|
Character(characterId: 2, name: "데이지", description: "", imageUrl: "https://picsum.photos/300", isNew: false)
|
||||||
],
|
],
|
||||||
isShowRank: true
|
isShowRank: true
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ struct CharacterView: View {
|
|||||||
title: "신규 캐릭터",
|
title: "신규 캐릭터",
|
||||||
items: viewModel.newCharacters,
|
items: viewModel.newCharacters,
|
||||||
isShowRank: false,
|
isShowRank: false,
|
||||||
trailingTitle: "전체보기",
|
trailingTitle: I18n.Common.viewAll,
|
||||||
onTapTrailing: {
|
onTapTrailing: {
|
||||||
onSelectNewCharacterAll()
|
onSelectNewCharacterAll()
|
||||||
},
|
},
|
||||||
@@ -64,20 +64,50 @@ struct CharacterView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 큐레이션 섹션 (여러 섹션)
|
if !viewModel.recommendCharacters.isEmpty {
|
||||||
if !viewModel.curations.isEmpty {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
VStack(alignment: .leading, spacing: 48) {
|
HStack {
|
||||||
ForEach(viewModel.curations.indices, id: \.self) { idx in
|
Text("추천 캐릭터")
|
||||||
let section = viewModel.curations[idx]
|
.appFont(size: 24, weight: .bold)
|
||||||
CharacterSectionView(
|
.foregroundColor(.white)
|
||||||
title: section.title,
|
|
||||||
items: section.characters,
|
Spacer()
|
||||||
isShowRank: false,
|
|
||||||
onTap: { ch in
|
Image("ic_refresh")
|
||||||
onSelectCharacter(ch.characterId)
|
.onTapGesture {
|
||||||
|
viewModel.refreshRecommendCharacters()
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 24)
|
||||||
|
|
||||||
|
let horizontalPadding: CGFloat = 24
|
||||||
|
let gridSpacing: CGFloat = 16
|
||||||
|
let width = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2
|
||||||
|
|
||||||
|
LazyVGrid(
|
||||||
|
columns: Array(
|
||||||
|
repeating: GridItem(
|
||||||
|
.flexible(),
|
||||||
|
spacing: gridSpacing,
|
||||||
|
alignment: .topLeading
|
||||||
|
),
|
||||||
|
count: 2
|
||||||
|
),
|
||||||
|
alignment: .leading,
|
||||||
|
spacing: gridSpacing
|
||||||
|
) {
|
||||||
|
ForEach(viewModel.recommendCharacters.indices, id: \.self) { idx in
|
||||||
|
let character = viewModel.recommendCharacters[idx]
|
||||||
|
CharacterItemView(
|
||||||
|
character: character,
|
||||||
|
size: width,
|
||||||
|
rank: idx + 1,
|
||||||
|
isShowRank: false
|
||||||
|
)
|
||||||
|
.onTapGesture { onSelectCharacter(character.characterId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, horizontalPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +127,7 @@ struct CharacterView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ final class CharacterViewModel: ObservableObject {
|
|||||||
@Published private(set) var recentCharacters: [RecentCharacter] = []
|
@Published private(set) var recentCharacters: [RecentCharacter] = []
|
||||||
@Published private(set) var popularCharacters: [Character] = []
|
@Published private(set) var popularCharacters: [Character] = []
|
||||||
@Published private(set) var newCharacters: [Character] = []
|
@Published private(set) var newCharacters: [Character] = []
|
||||||
@Published private(set) var curations: [CurationSection] = []
|
@Published private(set) var recommendCharacters: [Character] = []
|
||||||
|
|
||||||
@Published var isLoading: Bool = false
|
@Published var isLoading: Bool = false
|
||||||
@Published var errorMessage: String = ""
|
@Published var errorMessage: String = ""
|
||||||
@@ -49,7 +49,46 @@ final class CharacterViewModel: ObservableObject {
|
|||||||
self.recentCharacters = data.recentCharacters
|
self.recentCharacters = data.recentCharacters
|
||||||
self.popularCharacters = data.popularCharacters
|
self.popularCharacters = data.popularCharacters
|
||||||
self.newCharacters = data.newCharacters
|
self.newCharacters = data.newCharacters
|
||||||
self.curations = data.curationSections.filter { !$0.characters.isEmpty }
|
self.recommendCharacters = data.recommendCharacters
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
self.isLoading = false
|
||||||
|
} catch {
|
||||||
|
self.isLoading = false
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshRecommendCharacters() {
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
repository.refreshRecommendCharacters()
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { response in
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponse<[Character]>.self, from: responseData)
|
||||||
|
|
||||||
|
if let data = decoded.data, decoded.success {
|
||||||
|
self.recommendCharacters = data
|
||||||
} else {
|
} else {
|
||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// CurationSection.swift
|
|
||||||
// SodaLive
|
|
||||||
//
|
|
||||||
// Created by klaus on 8/29/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
struct CurationSection: Decodable {
|
|
||||||
let characterCurationId: Int
|
|
||||||
let title: String
|
|
||||||
let characters: [Character]
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,7 @@ struct CharacterDetailResponse: Decodable {
|
|||||||
let others: [OtherCharacter]
|
let others: [OtherCharacter]
|
||||||
let latestComment: CharacterCommentResponse?
|
let latestComment: CharacterCommentResponse?
|
||||||
let totalComments: Int
|
let totalComments: Int
|
||||||
|
let translated: TranslatedAiCharacterDetail?
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CharacterType: String, Decodable {
|
enum CharacterType: String, Decodable {
|
||||||
@@ -44,3 +45,22 @@ struct CharacterBackgroundResponse: Decodable {
|
|||||||
let topic: String
|
let topic: String
|
||||||
let description: String
|
let description: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TranslatedAiCharacterDetail: Decodable {
|
||||||
|
let name: String?
|
||||||
|
let description: String?
|
||||||
|
let gender: String?
|
||||||
|
let personality: TranslatedAiCharacterPersonality?
|
||||||
|
let background: TranslatedAiCharacterBackground?
|
||||||
|
let tags: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TranslatedAiCharacterPersonality: Decodable {
|
||||||
|
let trait: String?
|
||||||
|
let description: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TranslatedAiCharacterBackground: Decodable {
|
||||||
|
let topic: String?
|
||||||
|
let description: String?
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ struct CharacterDetailView: View {
|
|||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .detail: return "상세"
|
case .detail: return I18n.Tab.detail
|
||||||
case .gallery: return "갤러리"
|
case .gallery: return I18n.Tab.gallery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ struct CharacterDetailView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
DetailNavigationBar(title: "캐릭터 정보") {
|
DetailNavigationBar(title: String(localized: "캐릭터 정보")) {
|
||||||
if presentationMode.wrappedValue.isPresented {
|
if presentationMode.wrappedValue.isPresented {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
} else {
|
} else {
|
||||||
@@ -59,7 +59,7 @@ struct CharacterDetailView: View {
|
|||||||
|
|
||||||
// 세계관 및 작품 소개
|
// 세계관 및 작품 소개
|
||||||
if let backgrounds = viewModel.characterDetail?.backgrounds {
|
if let backgrounds = viewModel.characterDetail?.backgrounds {
|
||||||
worldViewSection(backgrounds: backgrounds)
|
worldViewSection(backgrounds: viewModel.characterDetail?.translated?.background?.description ?? backgrounds.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 원작 섹션
|
// 원작 섹션
|
||||||
@@ -70,7 +70,7 @@ struct CharacterDetailView: View {
|
|||||||
|
|
||||||
// 성격 및 특징 섹션
|
// 성격 및 특징 섹션
|
||||||
if let personalities = viewModel.characterDetail?.personalities {
|
if let personalities = viewModel.characterDetail?.personalities {
|
||||||
personalitySection(personalities: personalities)
|
personalitySection(personalities: viewModel.characterDetail?.translated?.personality?.description ?? personalities.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 장르의 다른 캐릭터 섹션
|
// 장르의 다른 캐릭터 섹션
|
||||||
@@ -78,7 +78,7 @@ struct CharacterDetailView: View {
|
|||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("장르의 다른 캐릭터")
|
Text("장르의 다른 캐릭터")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 26))
|
.appFont(size: 26, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -93,7 +93,8 @@ struct CharacterDetailView: View {
|
|||||||
characterId: otherCharacter.characterId,
|
characterId: otherCharacter.characterId,
|
||||||
name: otherCharacter.name,
|
name: otherCharacter.name,
|
||||||
description: otherCharacter.tags,
|
description: otherCharacter.tags,
|
||||||
imageUrl: otherCharacter.imageUrl
|
imageUrl: otherCharacter.imageUrl,
|
||||||
|
isNew: false
|
||||||
),
|
),
|
||||||
size: screenSize().width * 0.42,
|
size: screenSize().width * 0.42,
|
||||||
rank: 0,
|
rank: 0,
|
||||||
@@ -140,7 +141,7 @@ struct CharacterDetailView: View {
|
|||||||
.frame(alignment: .center)
|
.frame(alignment: .center)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal, 33.3)
|
.padding(.horizontal, 33.3)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -203,8 +204,8 @@ extension CharacterDetailView {
|
|||||||
{
|
{
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
if let gender = viewModel.characterDetail?.gender {
|
if let gender = viewModel.characterDetail?.gender {
|
||||||
Text(gender)
|
Text(viewModel.characterDetail?.translated?.gender ?? gender)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(
|
.foregroundColor(
|
||||||
gender == "남성" ?
|
gender == "남성" ?
|
||||||
Color.button :
|
Color.button :
|
||||||
@@ -227,7 +228,7 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
if let age = viewModel.characterDetail?.age {
|
if let age = viewModel.characterDetail?.age {
|
||||||
Text("\(age)세")
|
Text("\(age)세")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -243,7 +244,7 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
if let mbti = viewModel.characterDetail?.mbti {
|
if let mbti = viewModel.characterDetail?.mbti {
|
||||||
Text(mbti)
|
Text(mbti)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -261,8 +262,8 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
// 이름과 상태
|
// 이름과 상태
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Text(viewModel.characterDetail?.name ?? "")
|
Text(viewModel.characterDetail?.translated?.name ?? viewModel.characterDetail?.name ?? "")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 26))
|
.appFont(size: 26, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
@@ -270,7 +271,7 @@ extension CharacterDetailView {
|
|||||||
if let characterType = viewModel.characterDetail?.characterType {
|
if let characterType = viewModel.characterDetail?.characterType {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Text(characterType.rawValue)
|
Text(characterType.rawValue)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.horizontal, 5)
|
.padding(.horizontal, 5)
|
||||||
.padding(.vertical, 1)
|
.padding(.vertical, 1)
|
||||||
@@ -281,12 +282,12 @@ extension CharacterDetailView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 설명
|
// 설명
|
||||||
Text(viewModel.characterDetail?.description ?? "")
|
Text(viewModel.characterDetail?.translated?.description ?? viewModel.characterDetail?.description ?? "")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 18))
|
.appFont(size: 18, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
|
|
||||||
Text(viewModel.characterDetail?.tags ?? "")
|
Text(viewModel.characterDetail?.translated?.tags ?? viewModel.characterDetail?.tags ?? "")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "3BB9F1"))
|
.foregroundColor(Color(hex: "3BB9F1"))
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
}
|
}
|
||||||
@@ -296,17 +297,17 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
// MARK: - World View Section
|
// MARK: - World View Section
|
||||||
extension CharacterDetailView {
|
extension CharacterDetailView {
|
||||||
private func worldViewSection(backgrounds: CharacterBackgroundResponse) -> some View {
|
private func worldViewSection(backgrounds: String) -> some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("[세계관 및 작품 소개]")
|
Text("[세계관 및 작품 소개]")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 18))
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterExpandableTextView(text: backgrounds.description)
|
CharacterExpandableTextView(text: backgrounds)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 24)
|
.padding(.horizontal, 24)
|
||||||
}
|
}
|
||||||
@@ -318,7 +319,7 @@ extension CharacterDetailView {
|
|||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("원작")
|
Text("원작")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
@@ -327,7 +328,7 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -339,7 +340,7 @@ extension CharacterDetailView {
|
|||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("원작 보러가기")
|
Text("원작 보러가기")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundColor(Color(hex: "3BB9F1"))
|
.foregroundColor(Color(hex: "3BB9F1"))
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
@@ -356,23 +357,23 @@ extension CharacterDetailView {
|
|||||||
|
|
||||||
// MARK: - Personality Section
|
// MARK: - Personality Section
|
||||||
extension CharacterDetailView {
|
extension CharacterDetailView {
|
||||||
private func personalitySection(personalities: CharacterPersonalityResponse) -> some View {
|
private func personalitySection(personalities: String) -> some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("[성격 및 특징]")
|
Text("[성격 및 특징]")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 18))
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterExpandableTextView(text: personalities.description)
|
CharacterExpandableTextView(text: personalities)
|
||||||
|
|
||||||
// 캐릭터톡 대화 가이드
|
// 캐릭터톡 대화 가이드
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("⚠️ 캐릭터톡 대화 가이드")
|
Text("⚠️ 캐릭터톡 대화 가이드")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -381,7 +382,7 @@ extension CharacterDetailView {
|
|||||||
Text("""
|
Text("""
|
||||||
보이스온의 오픈월드 캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다. 세계관 속 연관 캐릭터가 되어 대화를 하거나 완전히 새로운 인물이 되어 캐릭터와 당신만의 스토리를 만들어 갈 수 있습니다.
|
보이스온의 오픈월드 캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다. 세계관 속 연관 캐릭터가 되어 대화를 하거나 완전히 새로운 인물이 되어 캐릭터와 당신만의 스토리를 만들어 갈 수 있습니다.
|
||||||
""")
|
""")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "AEAEB2"))
|
.foregroundColor(Color(hex: "AEAEB2"))
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|
||||||
@@ -389,7 +390,7 @@ extension CharacterDetailView {
|
|||||||
오픈월드 캐릭터톡은 캐릭터를 정교하게 설계하였지만, 대화가 어색하거나 불완전할 수도 있습니다.
|
오픈월드 캐릭터톡은 캐릭터를 정교하게 설계하였지만, 대화가 어색하거나 불완전할 수도 있습니다.
|
||||||
대화 도중 캐릭터의 대화가 이상하거나 새로운 캐릭터로 대화를 나누고 싶다면 대화를 초기화 하고 새롭게 캐릭터와 대화를 나눠보세요.
|
대화 도중 캐릭터의 대화가 이상하거나 새로운 캐릭터로 대화를 나누고 싶다면 대화를 초기화 하고 새롭게 캐릭터와 대화를 나눠보세요.
|
||||||
""")
|
""")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "AEAEB2"))
|
.foregroundColor(Color(hex: "AEAEB2"))
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
}
|
}
|
||||||
@@ -411,7 +412,7 @@ extension CharacterDetailView {
|
|||||||
extension CharacterDetailView {
|
extension CharacterDetailView {
|
||||||
private var chatButton: some View {
|
private var chatButton: some View {
|
||||||
Text("대화하기")
|
Text("대화하기")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 18))
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.frame(height: 54)
|
.frame(height: 54)
|
||||||
@@ -442,7 +443,7 @@ struct CharacterExpandableTextView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
.lineLimit(isExpanded ? nil : 3)
|
.lineLimit(isExpanded ? nil : 3)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
@@ -463,12 +464,12 @@ struct CharacterExpandableTextView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Image(systemName: "chevron.down")
|
Image(systemName: "chevron.down")
|
||||||
.font(.system(size: 16))
|
.appFont(size: 16)
|
||||||
.foregroundColor(Color(hex: "607D8B"))
|
.foregroundColor(Color(hex: "607D8B"))
|
||||||
.rotationEffect(.degrees(isExpanded ? 180 : 0))
|
.rotationEffect(.degrees(isExpanded ? 180 : 0))
|
||||||
|
|
||||||
Text(isExpanded ? "간략히" : "더보기")
|
Text(isExpanded ? "간략히" : "더보기")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "607D8B"))
|
.foregroundColor(Color(hex: "607D8B"))
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@@ -54,14 +54,14 @@ final class CharacterDetailViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self?.errorMessage = message
|
self?.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
ERROR_LOG(String(describing: error))
|
ERROR_LOG(String(describing: error))
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,14 +94,14 @@ final class CharacterDetailViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self?.errorMessage = message
|
self?.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
ERROR_LOG(String(describing: error))
|
ERROR_LOG(String(describing: error))
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ struct CharacterDetailGalleryView: View {
|
|||||||
.frame(alignment: .center)
|
.frame(alignment: .center)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal, 33.3)
|
.padding(.horizontal, 33.3)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -78,11 +78,11 @@ struct CharacterDetailGalleryView: View {
|
|||||||
.overlay {
|
.overlay {
|
||||||
if viewModel.isShowPurchaseDialog {
|
if viewModel.isShowPurchaseDialog {
|
||||||
SodaDialog(
|
SodaDialog(
|
||||||
title: "구매 확인",
|
title: I18n.CharacterDetailGallery.purchaseConfirmTitle,
|
||||||
desc: "선택한 이미지를 구매하시겠습니까?",
|
desc: I18n.CharacterDetailGallery.purchaseConfirmDescription,
|
||||||
confirmButtonTitle: viewModel.selectedItemPrice,
|
confirmButtonTitle: viewModel.selectedItemPrice,
|
||||||
confirmButtonAction: viewModel.onPurchaseConfirm,
|
confirmButtonAction: viewModel.onPurchaseConfirm,
|
||||||
cancelButtonTitle: "취소",
|
cancelButtonTitle: I18n.Common.cancel,
|
||||||
cancelButtonAction: viewModel.onPurchaseCancel
|
cancelButtonAction: viewModel.onPurchaseCancel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -95,22 +95,22 @@ struct CharacterDetailGalleryView: View {
|
|||||||
// 상단 정보 (계산된 % 보유중, 정보 아이콘, 개수)
|
// 상단 정보 (계산된 % 보유중, 정보 아이콘, 개수)
|
||||||
HStack {
|
HStack {
|
||||||
Text("\(viewModel.ownershipPercentage)% 보유중")
|
Text("\(viewModel.ownershipPercentage)% 보유중")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 18))
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Text("\(viewModel.ownedCount)")
|
Text("\(viewModel.ownedCount)")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "#FDD453"))
|
.foregroundColor(Color(hex: "#FDD453"))
|
||||||
|
|
||||||
Text("/")
|
Text("/")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Text("\(viewModel.totalCount)개")
|
Text("\(viewModel.totalCount)개")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ struct CharacterDetailGalleryView: View {
|
|||||||
.frame(width: 16)
|
.frame(width: 16)
|
||||||
|
|
||||||
Text("\(item.imagePriceCan)")
|
Text("\(item.imagePriceCan)")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "#263238"))
|
.foregroundColor(Color(hex: "#263238"))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ final class CharacterDetailGalleryViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var selectedItemPrice: String {
|
var selectedItemPrice: String {
|
||||||
return "\(selectedItem?.imagePriceCan ?? 0)캔으로 구매"
|
return I18n.CharacterDetailGallery.purchaseWithCans(selectedItem?.imagePriceCan ?? 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
@@ -112,7 +112,7 @@ final class CharacterDetailGalleryViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self.errorMessage = message
|
self.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
@@ -124,7 +124,7 @@ final class CharacterDetailGalleryViewModel: ObservableObject {
|
|||||||
self.selectedImageIndex = 0
|
self.selectedImageIndex = 0
|
||||||
self.selectedItem = nil
|
self.selectedItem = nil
|
||||||
ERROR_LOG(String(describing: error))
|
ERROR_LOG(String(describing: error))
|
||||||
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self.errorMessage = I18n.Common.commonError
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,14 +172,14 @@ final class CharacterDetailGalleryViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self?.errorMessage = message
|
self?.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
ERROR_LOG(String(describing: error))
|
ERROR_LOG(String(describing: error))
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,14 +219,14 @@ final class CharacterDetailGalleryViewModel: ObservableObject {
|
|||||||
if let message = decoded.message {
|
if let message = decoded.message {
|
||||||
self?.errorMessage = message
|
self?.errorMessage = message
|
||||||
} else {
|
} else {
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
}
|
}
|
||||||
|
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
ERROR_LOG(String(describing: error))
|
ERROR_LOG(String(describing: error))
|
||||||
self?.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
self?.errorMessage = I18n.Common.commonError
|
||||||
self?.isShowPopup = true
|
self?.isShowPopup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,19 +18,19 @@ struct NewCharacterListView: View {
|
|||||||
BaseView(isLoading: $viewModel.isLoading) {
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
// Toolbar
|
// Toolbar
|
||||||
DetailNavigationBar(title: "신규 캐릭터 전체보기")
|
DetailNavigationBar(title: String(localized: "신규 캐릭터 전체보기"))
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
// 전체 n개
|
// 전체 n개
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("전체 ")
|
Text("전체")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
Text("\(viewModel.totalCount)")
|
Text(" \(viewModel.totalCount)")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "ff5c49"))
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
Text("개")
|
Text("개")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "e2e2e2"))
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ struct NewCharacterListView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ struct RecentCharacterItemView: View {
|
|||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text(character.name)
|
Text(character.name)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 18))
|
.appFont(size: 18, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(maxWidth: 76)
|
.frame(maxWidth: 76)
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ struct RecentCharacterSectionView: View {
|
|||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
Text("최근 대화한 캐릭터 ")
|
Text("최근 대화한 캐릭터 ")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 20))
|
.appFont(size: 20, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Text("\(titleCount)")
|
Text("\(titleCount)")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 20))
|
.appFont(size: 20, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "FDCA2F"))
|
.foregroundColor(Color(hex: "FDCA2F"))
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|||||||
@@ -14,14 +14,18 @@ struct ChatTabView: View {
|
|||||||
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
|
@AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token)
|
||||||
@AppStorage("auth") private var auth: Bool = UserDefaults.bool(forKey: UserDefaultsKey.auth)
|
@AppStorage("auth") private var auth: Bool = UserDefaults.bool(forKey: UserDefaultsKey.auth)
|
||||||
|
|
||||||
|
@StateObject var mypageViewModel = MyPageViewModel()
|
||||||
|
|
||||||
private enum InnerTab: Int, CaseIterable {
|
private enum InnerTab: Int, CaseIterable {
|
||||||
case character = 0
|
case character = 0
|
||||||
|
case original = 1
|
||||||
case talk = 2
|
case talk = 2
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .character: return "캐릭터"
|
case .character: return I18n.Tab.character
|
||||||
case .talk: return "톡"
|
case .original: return I18n.Tab.work
|
||||||
|
case .talk: return I18n.Tab.talk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +107,13 @@ struct ChatTabView: View {
|
|||||||
onTap: { if selectedTab != .character { selectedTab = .character } }
|
onTap: { if selectedTab != .character { selectedTab = .character } }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ChatInnerTab(
|
||||||
|
title: InnerTab.original.title,
|
||||||
|
isSelected: selectedTab == .original,
|
||||||
|
onTap: { if selectedTab != .original { selectedTab = .original } }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
ChatInnerTab(
|
ChatInnerTab(
|
||||||
title: InnerTab.talk.title,
|
title: InnerTab.talk.title,
|
||||||
isSelected: selectedTab == .talk,
|
isSelected: selectedTab == .talk,
|
||||||
@@ -115,6 +126,8 @@ struct ChatTabView: View {
|
|||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
case .character:
|
case .character:
|
||||||
CharacterView(onSelectCharacter: handleCharacterSelection, onSelectNewCharacterAll: handleCharacterSelection)
|
CharacterView(onSelectCharacter: handleCharacterSelection, onSelectNewCharacterAll: handleCharacterSelection)
|
||||||
|
case .original:
|
||||||
|
OriginalTabView()
|
||||||
case .talk:
|
case .talk:
|
||||||
TalkView()
|
TalkView()
|
||||||
}
|
}
|
||||||
@@ -142,12 +155,15 @@ struct ChatTabView: View {
|
|||||||
AppState.shared.isShowErrorPopup = true
|
AppState.shared.isShowErrorPopup = true
|
||||||
isShowAuthView = false
|
isShowAuthView = false
|
||||||
}
|
}
|
||||||
.onDone { _ in
|
.onDone {
|
||||||
auth = true
|
DEBUG_LOG("onDone: \($0)")
|
||||||
isShowAuthView = false
|
mypageViewModel.authVerify($0) {
|
||||||
if let action = pendingAction {
|
auth = true
|
||||||
pendingAction = nil
|
isShowAuthView = false
|
||||||
action()
|
if let action = pendingAction {
|
||||||
|
pendingAction = nil
|
||||||
|
action()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onClose {
|
.onClose {
|
||||||
@@ -188,12 +204,7 @@ struct ChatInnerTab: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text(title)
|
Text(title)
|
||||||
.font(
|
.appFont(size: 18, weight: isSelected ? .bold : .regular)
|
||||||
.custom(
|
|
||||||
isSelected ? Font.preBold.rawValue : Font.preRegular.rawValue,
|
|
||||||
size: 18
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.foregroundColor(Color(hex: isSelected ? "3bb9f1" : "b0bec5"))
|
.foregroundColor(Color(hex: isSelected ? "3bb9f1" : "b0bec5"))
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,15 @@ struct OriginalWorkDetailHeaderView: View {
|
|||||||
.cornerRadius(16)
|
.cornerRadius(16)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(item.title)
|
// 번역 데이터가 있으면 번역값을 우선 사용
|
||||||
.font(.custom(Font.preBold.rawValue, size: 26))
|
Text(item.translated?.title ?? item.title)
|
||||||
|
.appFont(size: 26, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.top, 40)
|
.padding(.top, 40)
|
||||||
|
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Text(item.contentType)
|
Text(item.translated?.contentType ?? item.contentType)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -43,8 +44,8 @@ struct OriginalWorkDetailHeaderView: View {
|
|||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(item.category)
|
Text(item.translated?.category ?? item.category)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.button)
|
.foregroundColor(.button)
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -58,7 +59,7 @@ struct OriginalWorkDetailHeaderView: View {
|
|||||||
|
|
||||||
if item.isAdult {
|
if item.isAdult {
|
||||||
Text("19+")
|
Text("19+")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "ff5c49"))
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
.padding(.horizontal, 7)
|
.padding(.horizontal, 7)
|
||||||
.padding(.vertical, 3)
|
.padding(.vertical, 3)
|
||||||
@@ -74,11 +75,11 @@ struct OriginalWorkDetailHeaderView: View {
|
|||||||
.padding(.top, 14)
|
.padding(.top, 14)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
item.tags
|
(item.translated?.tags ?? item.tags)
|
||||||
.map { $0.hasPrefix("#") ? $0 : "#\($0)" }
|
.map { $0.hasPrefix("#") ? $0 : "#\($0)" }
|
||||||
.joined(separator: " ")
|
.joined(separator: " ")
|
||||||
)
|
)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "3bb9f1"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.top, 14)
|
.padding(.top, 14)
|
||||||
@@ -101,7 +102,8 @@ struct OriginalWorkDetailHeaderView: View {
|
|||||||
studio: nil,
|
studio: nil,
|
||||||
originalLinks: [],
|
originalLinks: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
characters: []
|
characters: [],
|
||||||
|
translated: nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,4 +19,13 @@ struct OriginalWorkDetailResponse: Decodable {
|
|||||||
let originalLinks: [String]
|
let originalLinks: [String]
|
||||||
let tags: [String]
|
let tags: [String]
|
||||||
let characters: [Character]
|
let characters: [Character]
|
||||||
|
let translated: TranslatedOriginalWork?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TranslatedOriginalWork: Decodable {
|
||||||
|
let title: String
|
||||||
|
let contentType: String
|
||||||
|
let category: String
|
||||||
|
let description: String
|
||||||
|
let tags: [String]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ struct OriginalWorkDetailView: View {
|
|||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
SeriesDetailTabView(
|
SeriesDetailTabView(
|
||||||
title: "캐릭터",
|
title: I18n.Tab.character,
|
||||||
width: screenSize().width / 2,
|
width: screenSize().width / 2,
|
||||||
isSelected: viewModel.currentTab == .character
|
isSelected: viewModel.currentTab == .character
|
||||||
) {
|
) {
|
||||||
@@ -63,7 +63,7 @@ struct OriginalWorkDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SeriesDetailTabView(
|
SeriesDetailTabView(
|
||||||
title: "작품정보",
|
title: I18n.Tab.workInfo,
|
||||||
width: screenSize().width / 2,
|
width: screenSize().width / 2,
|
||||||
isSelected: viewModel.currentTab == .info
|
isSelected: viewModel.currentTab == .info
|
||||||
) {
|
) {
|
||||||
@@ -157,11 +157,11 @@ struct OriginalWorkInfoView: View {
|
|||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text("작품 소개")
|
Text("작품 소개")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Text(response.description)
|
Text(response.translated?.description ?? response.description)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
.lineLimit(isExpandDesc ? Int.max : 3)
|
.lineLimit(isExpandDesc ? Int.max : 3)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
@@ -176,7 +176,7 @@ struct OriginalWorkInfoView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text("원작 보러 가기")
|
Text("원작 보러 가기")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
@@ -185,7 +185,7 @@ struct OriginalWorkInfoView: View {
|
|||||||
let link = response.originalLinks[$0]
|
let link = response.originalLinks[$0]
|
||||||
|
|
||||||
Text(link)
|
Text(link)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if let url = URL(string: link) {
|
if let url = URL(string: link) {
|
||||||
@@ -203,26 +203,26 @@ struct OriginalWorkInfoView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Text("상세 정보")
|
Text("상세 정보")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 16) {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
if let _ = response.writer {
|
if let _ = response.writer {
|
||||||
Text("작가")
|
Text("작가")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = response.studio {
|
if let _ = response.studio {
|
||||||
Text("제작사")
|
Text("제작사")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let _ = response.originalWork {
|
if let _ = response.originalWork {
|
||||||
Text("원작")
|
Text("원작")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "B0BEC5"))
|
.foregroundColor(Color(hex: "B0BEC5"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,19 +230,19 @@ struct OriginalWorkInfoView: View {
|
|||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
if let writer = response.writer {
|
if let writer = response.writer {
|
||||||
Text(writer)
|
Text(writer)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let studio = response.studio {
|
if let studio = response.studio {
|
||||||
Text(studio)
|
Text(studio)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let originalWork = response.originalWork {
|
if let originalWork = response.originalWork {
|
||||||
Text(originalWork)
|
Text(originalWork)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.underline(response.originalLink != nil ? true : false)
|
.underline(response.originalLink != nil ? true : false)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ struct OriginalTabItemView: View {
|
|||||||
).cornerRadius(16)
|
).cornerRadius(16)
|
||||||
|
|
||||||
Text(item.title)
|
Text(item.title)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 18))
|
.appFont(size: 18, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|
||||||
Text(item.contentType)
|
Text(item.contentType)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ struct OriginalTabView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -46,13 +46,13 @@ struct ChatRoomView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
Text(viewModel.characterName)
|
Text(viewModel.characterName)
|
||||||
.font(.custom(Font.preBold.rawValue, size: 12))
|
.appFont(size: 12, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|
||||||
Text(viewModel.characterType.rawValue)
|
Text(viewModel.characterType.rawValue)
|
||||||
.font(.custom(Font.preBold.rawValue, size: 10))
|
.appFont(size: 10, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.padding(.horizontal, 4)
|
.padding(.horizontal, 4)
|
||||||
.padding(.vertical, 2)
|
.padding(.vertical, 2)
|
||||||
@@ -73,7 +73,7 @@ struct ChatRoomView: View {
|
|||||||
.frame(width: 20, height: 20)
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
Text("\(can)")
|
Text("\(can)")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
@@ -103,7 +103,7 @@ struct ChatRoomView: View {
|
|||||||
? "보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.\n※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다."
|
? "보이스온 AI캐릭터톡은 대화의 자유도가 높아 대화에 참여하는 당신은 누구든 될 수 있습니다.\n세계관 속 캐릭터로 대화를 하거나 새로운 인물로 캐릭터와 당신만의 스토리를 만들어보세요.\n※ AI캐릭터톡은 오픈베타 서비스 중이며, 캐릭터의 대화가 어색하거나 불완전할 수 있습니다."
|
||||||
: "AI Clone은 크리에이터의 정보를 기반으로 대화하지만, 모든 정보를 완벽하게 반영하거나 실제 대화와 일치하지 않을 수 있습니다."
|
: "AI Clone은 크리에이터의 정보를 기반으로 대화하지만, 모든 정보를 완벽하게 반영하거나 실제 대화와 일치하지 않을 수 있습니다."
|
||||||
)
|
)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
Image(systemName: "chevron.up")
|
Image(systemName: "chevron.up")
|
||||||
@@ -187,12 +187,12 @@ struct ChatRoomView: View {
|
|||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
if viewModel.messageText.isEmpty {
|
if viewModel.messageText.isEmpty {
|
||||||
Text("메시지를 입력하세요.")
|
Text("메시지를 입력하세요.")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(Color(hex: "78909C"))
|
.foregroundColor(Color(hex: "78909C"))
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField("", text: $viewModel.messageText)
|
TextField("", text: $viewModel.messageText)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 14))
|
.appFont(size: 14, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.onSubmit {
|
.onSubmit {
|
||||||
viewModel.sendMessage()
|
viewModel.sendMessage()
|
||||||
@@ -227,13 +227,13 @@ struct ChatRoomView: View {
|
|||||||
|
|
||||||
if let message = viewModel.selectedMessage, viewModel.selectedMessageIndex >= 0 {
|
if let message = viewModel.selectedMessage, viewModel.selectedMessageIndex >= 0 {
|
||||||
SodaDialog(
|
SodaDialog(
|
||||||
title: "잠금된 메시지",
|
title: I18n.ChatRoom.lockedMessageTitle,
|
||||||
desc: "이 메시지를 \(message.price ?? 5)캔으로 잠금해제 하시겠습니까?",
|
desc: I18n.ChatRoom.unlockMessageDescription(message.price ?? 5),
|
||||||
confirmButtonTitle: "잠금해제",
|
confirmButtonTitle: I18n.ChatRoom.unlock,
|
||||||
confirmButtonAction: {
|
confirmButtonAction: {
|
||||||
viewModel.purchaseChatMessage()
|
viewModel.purchaseChatMessage()
|
||||||
},
|
},
|
||||||
cancelButtonTitle: "취소"
|
cancelButtonTitle: I18n.Common.cancel
|
||||||
) {
|
) {
|
||||||
viewModel.selectedMessage = nil
|
viewModel.selectedMessage = nil
|
||||||
viewModel.selectedMessageIndex = -1
|
viewModel.selectedMessageIndex = -1
|
||||||
@@ -265,14 +265,14 @@ struct ChatRoomView: View {
|
|||||||
Color.black.opacity(0.7)
|
Color.black.opacity(0.7)
|
||||||
|
|
||||||
SodaDialog(
|
SodaDialog(
|
||||||
title: "대화 초기화",
|
title: I18n.ChatRoom.resetTitle,
|
||||||
desc: "지금까지의 대화가 모두 초기화 되고 새롭게 대화를 시작합니다.",
|
desc: I18n.ChatRoom.resetDescription,
|
||||||
confirmButtonTitle: "30캔으로 초기화",
|
confirmButtonTitle: I18n.ChatRoom.resetWithCans(30),
|
||||||
confirmButtonAction: {
|
confirmButtonAction: {
|
||||||
viewModel.resetChatRoom()
|
viewModel.resetChatRoom()
|
||||||
viewModel.isShowingChatResetConfirmDialog = false
|
viewModel.isShowingChatResetConfirmDialog = false
|
||||||
},
|
},
|
||||||
cancelButtonTitle: "취소",
|
cancelButtonTitle: I18n.Common.cancel,
|
||||||
cancelButtonAction: {
|
cancelButtonAction: {
|
||||||
viewModel.isShowingChatResetConfirmDialog = false
|
viewModel.isShowingChatResetConfirmDialog = false
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ struct ChatRoomView: View {
|
|||||||
Text(viewModel.errorMessage)
|
Text(viewModel.errorMessage)
|
||||||
.padding(.vertical, 13.3)
|
.padding(.vertical, 13.3)
|
||||||
.frame(width: geo.size.width - 66.7, alignment: .center)
|
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
.appFont(size: 12, weight: .medium)
|
||||||
.background(Color.button)
|
.background(Color.button)
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ struct AiMessageItemView: View {
|
|||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Text(characterName)
|
Text(characterName)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ struct AiMessageItemView: View {
|
|||||||
.frame(width: 24, height: 24)
|
.frame(width: 24, height: 24)
|
||||||
|
|
||||||
Text("\(message.price ?? 5)")
|
Text("\(message.price ?? 5)")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 16))
|
.appFont(size: 16, weight: .bold)
|
||||||
.foregroundColor(Color(hex: "263238"))
|
.foregroundColor(Color(hex: "263238"))
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
@@ -131,7 +131,7 @@ struct AiMessageItemView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text("눌러서 잠금해제")
|
Text("눌러서 잠금해제")
|
||||||
.font(.custom(Font.preBold.rawValue, size: 18))
|
.appFont(size: 18, weight: .bold)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
.frame(width: maxWidth, height: imageHeight)
|
.frame(width: maxWidth, height: imageHeight)
|
||||||
@@ -162,7 +162,7 @@ struct AiMessageItemView: View {
|
|||||||
// 시간 표시
|
// 시간 표시
|
||||||
VStack {
|
VStack {
|
||||||
Text(formatTime(from: message.createdAt))
|
Text(formatTime(from: message.createdAt))
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 10))
|
.appFont(size: 10, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ struct AiMessageItemView: View {
|
|||||||
// 첫 번째 컴포넌트는 항상 일반 텍스트
|
// 첫 번째 컴포넌트는 항상 일반 텍스트
|
||||||
if !component.isEmpty {
|
if !component.isEmpty {
|
||||||
result = result + Text(component)
|
result = result + Text(component)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -202,13 +202,13 @@ struct AiMessageItemView: View {
|
|||||||
// 소괄호 뒤의 텍스트 (일반 스타일)
|
// 소괄호 뒤의 텍스트 (일반 스타일)
|
||||||
if !afterClose.isEmpty {
|
if !afterClose.isEmpty {
|
||||||
result = result + Text(afterClose)
|
result = result + Text(afterClose)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 닫는 괄호가 없으면 일반 텍스트로 처리
|
// 닫는 괄호가 없으면 일반 텍스트로 처리
|
||||||
result = result + Text("(\(component)")
|
result = result + Text("(\(component)")
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 16))
|
.appFont(size: 16, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// MessageInputView.swift
|
|
||||||
// SodaLive
|
|
||||||
//
|
|
||||||
// Created by klaus on 9/3/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct MessageInputView: View {
|
|
||||||
var body: some View {
|
|
||||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
MessageInputView()
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ struct TypingIndicatorItemView: View {
|
|||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Text(characterName)
|
Text(characterName)
|
||||||
.font(.custom(Font.preRegular.rawValue, size: 12))
|
.appFont(size: 12, weight: .regular)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||