Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
115a30a7b6 | ||
![]() |
c78b804678 | ||
![]() |
1a01244d85 | ||
![]() |
fea557560c | ||
![]() |
c440e8abd9 | ||
![]() |
41d5bad46f | ||
![]() |
6c8e19aed5 | ||
![]() |
282ee73de1 | ||
![]() |
91c43e679f | ||
![]() |
02835c4b2e | ||
![]() |
a4d15be57a | ||
![]() |
219128ea8d | ||
![]() |
47ae2ec8e1 | ||
![]() |
962197d319 | ||
![]() |
c75f94722b | ||
![]() |
c00931761c | ||
![]() |
8255065bba | ||
![]() |
1e1b97e2d4 | ||
![]() |
f653667df2 | ||
![]() |
658cff20eb | ||
![]() |
fd356451ae | ||
![]() |
91cd3fe995 | ||
![]() |
5f7924880e | ||
![]() |
00a8f7e8ff | ||
![]() |
d1b5ab31aa | ||
![]() |
225efc34e4 | ||
![]() |
4e607ed624 | ||
![]() |
c7522aa7c9 | ||
![]() |
b632a65a6f | ||
![]() |
d3c5a5bfb9 | ||
![]() |
2449eb14d0 | ||
![]() |
4c9e78f960 | ||
![]() |
6e9aaa0c8a | ||
![]() |
0294bbf223 | ||
![]() |
36aa167e1d | ||
![]() |
8c20ce9c62 | ||
![]() |
f44ef505cf | ||
![]() |
9a72e21fda | ||
![]() |
1c4503bda1 | ||
![]() |
b1773b117d | ||
![]() |
a16c38f4ab | ||
![]() |
475882570a | ||
![]() |
5b0cb44645 | ||
![]() |
b2f0975ad1 | ||
![]() |
48b1093dac | ||
![]() |
3bcd2b7dba | ||
![]() |
9863fc66de | ||
![]() |
58868f613a | ||
![]() |
1f992a11dc | ||
![]() |
948b1fd2b3 | ||
![]() |
5d95c0f1c9 | ||
![]() |
b31933715d | ||
![]() |
707b6f804c | ||
![]() |
05f5a4fe82 | ||
![]() |
f08d72745e | ||
![]() |
4611524f8f | ||
![]() |
a41c423991 | ||
![]() |
d76b1c7a59 | ||
![]() |
6fa183b89a | ||
![]() |
371d6d538a | ||
![]() |
2aa3f944c8 | ||
![]() |
e68961bd0d | ||
![]() |
88fcbc98f4 | ||
![]() |
3916a49e60 | ||
![]() |
594bcc27ca | ||
![]() |
bcb934a155 | ||
![]() |
b31dcf901b | ||
![]() |
2c8485a1cd | ||
![]() |
f0d2bda024 | ||
![]() |
828c507742 | ||
![]() |
65b43e4c0d |
21
SodaLive/Resources/Assets.xcassets/btn_audio_content_preview_play.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "btn_audio_content_preview_play.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 845 B After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 4.6 KiB |
@@ -5,11 +5,11 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "loading_4.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "ic_alarm.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_alarm.imageset/ic_alarm.png
vendored
Normal file
After Width: | Height: | Size: 781 B |
21
SodaLive/Resources/Assets.xcassets/ic_alarm_selected.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_alarm_selected.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_alarm_selected.imageset/ic_alarm_selected.png
vendored
Normal file
After Width: | Height: | Size: 804 B |
21
SodaLive/Resources/Assets.xcassets/ic_blog_purple.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_blog_purple.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_blog_purple.imageset/ic_blog_purple.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_heart_777.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_heart_777.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_heart_777.imageset/ic_heart_777.png
vendored
Normal file
After Width: | Height: | Size: 830 B |
21
SodaLive/Resources/Assets.xcassets/ic_instagram_purple.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_instagram_purple.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_instagram_purple.imageset/ic_instagram_purple.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_kick_out.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_kick_out.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_kick_out.imageset/ic_kick_out.png
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_message_send.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_message_send.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_message_send.imageset/ic_message_send.png
vendored
Normal file
After Width: | Height: | Size: 787 B |
@@ -9,7 +9,7 @@
|
|||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "ic_headphones_purple.png",
|
"filename" : "ic_message_square_777.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_message_square_777 1.imageset/ic_message_square_777.png
vendored
Normal file
After Width: | Height: | Size: 471 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_mic_paint.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_mic_paint.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_mic_paint.imageset/ic_mic_paint.png
vendored
Normal file
After Width: | Height: | Size: 694 B |
@@ -5,11 +5,11 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "loading_2.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "ic_mute.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_mute.imageset/ic_mute.png
vendored
Normal file
After Width: | Height: | Size: 7.6 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_place_holder.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_place_holder.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_place_holder.imageset/ic_place_holder.png
vendored
Normal file
After Width: | Height: | Size: 70 KiB |
@@ -5,11 +5,11 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "loading_3.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "ic_record.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_record.imageset/ic_record.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_record_pause.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_record_pause.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_record_pause.imageset/ic_record_pause.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_record_play.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_record_play.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_record_play.imageset/ic_record_play.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
21
SodaLive/Resources/Assets.xcassets/ic_record_stop.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_record_stop.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_record_stop.imageset/ic_record_stop.png
vendored
Normal file
After Width: | Height: | Size: 1.9 KiB |
@@ -5,11 +5,11 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "loading_1.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "ic_save.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_save.imageset/ic_save.png
vendored
Normal file
After Width: | Height: | Size: 443 B |
21
SodaLive/Resources/Assets.xcassets/ic_website_purple.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_website_purple.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_website_purple.imageset/ic_website_purple.png
vendored
Normal file
After Width: | Height: | Size: 926 B |
21
SodaLive/Resources/Assets.xcassets/ic_youtube_play_purple.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "ic_youtube_play_purple.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/ic_youtube_play_purple.imageset/ic_youtube_play_purple.png
vendored
Normal file
After Width: | Height: | Size: 858 B |
21
SodaLive/Resources/Assets.xcassets/img_compleate_book.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img_compleate_book.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/img_compleate_book.imageset/img_compleate_book.png
vendored
Normal file
After Width: | Height: | Size: 21 KiB |
21
SodaLive/Resources/Assets.xcassets/img_noti_mute.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img_noti_mute.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/img_noti_mute.imageset/img_noti_mute.png
vendored
Normal file
After Width: | Height: | Size: 23 KiB |
21
SodaLive/Resources/Assets.xcassets/img_thumb_default.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "img_thumb_default.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
SodaLive/Resources/Assets.xcassets/img_thumb_default.imageset/img_thumb_default.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 5.1 KiB |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "loading_5.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -1,25 +1,226 @@
|
|||||||
<?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>FirebaseAppDelegateProxyEnabled</key>
|
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>UIAppFonts</key>
|
<key>UIAppFonts</key>
|
||||||
<array>
|
<array>
|
||||||
<string>gmarket_sans_bold.otf</string>
|
<string>gmarket_sans_bold.otf</string>
|
||||||
<string>gmarket_sans_medium.otf</string>
|
<string>gmarket_sans_medium.otf</string>
|
||||||
<string>gmarket_sans_light.otf</string>
|
<string>gmarket_sans_light.otf</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>audio</string>
|
<string>audio</string>
|
||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
<string>remote-notification</string>
|
<string>remote-notification</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
<key>GADApplicationIdentifier</key>
|
||||||
|
<string>ca-app-pub-1299501215847962~3447556960</string>
|
||||||
|
<key>SKAdNetworkItems</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>cstr6suwn9.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4fzdc2evr5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4pfyvq9l8r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>2fnua5tdw4.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ydx93a7ass.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>5a6flpkh64.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>p78axxw29g.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v72qych5uu.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ludvb6z3bs.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>cp8zw746q7.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3sh42y64q3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>c6k4g5qg8m.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>s39g8k73mm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qy4746246.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>f38h382jlk.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>hs6bdukanm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v4nxqhlyqp.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>wzmmz9fp6w.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>yclnxrl5pm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>t38b2kh725.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>7ug5zh24hu.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>gta9lk7p23.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>vutu7akeur.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>y5ghdn5j9k.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>n6fk4nfna4.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v9wttpbfk9.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>n38lu8286q.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>47vhws6wlr.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>kbd757ywx3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>9t245vhmpl.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>eh6m2bh4zr.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>a2p9lx4jpn.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>22mmun2rn5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4468km3ulz.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>2u9pt9hc89.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>8s468mfl3y.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>klf5c3l5u5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ppxm28t8ap.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ecpz2srf59.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>uw77j35x4d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>pwa73g5rt2.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>mlmmfzh3r3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>578prtvx9j.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4dzt52r2t5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>e5fvkxwrpn.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>8c4e2ghe7u.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>zq492l623r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3rd42ekr43.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qcr597p9d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@@ -21,5 +21,206 @@
|
|||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
<string>remote-notification</string>
|
<string>remote-notification</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>GADApplicationIdentifier</key>
|
||||||
|
<string>ca-app-pub-1299501215847962~8852459715</string>
|
||||||
|
<key>SKAdNetworkItems</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>cstr6suwn9.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4fzdc2evr5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4pfyvq9l8r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>2fnua5tdw4.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ydx93a7ass.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>5a6flpkh64.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>p78axxw29g.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v72qych5uu.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ludvb6z3bs.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>cp8zw746q7.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3sh42y64q3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>c6k4g5qg8m.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>s39g8k73mm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qy4746246.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>f38h382jlk.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>hs6bdukanm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v4nxqhlyqp.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>wzmmz9fp6w.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>yclnxrl5pm.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>t38b2kh725.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>7ug5zh24hu.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>gta9lk7p23.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>vutu7akeur.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>y5ghdn5j9k.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>n6fk4nfna4.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>v9wttpbfk9.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>n38lu8286q.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>47vhws6wlr.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>kbd757ywx3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>9t245vhmpl.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>eh6m2bh4zr.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>a2p9lx4jpn.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>22mmun2rn5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4468km3ulz.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>2u9pt9hc89.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>8s468mfl3y.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>klf5c3l5u5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ppxm28t8ap.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>ecpz2srf59.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>uw77j35x4d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>pwa73g5rt2.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>mlmmfzh3r3.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>578prtvx9j.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>4dzt52r2t5.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>e5fvkxwrpn.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>8c4e2ghe7u.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>zq492l623r.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3rd42ekr43.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SKAdNetworkIdentifier</key>
|
||||||
|
<string>3qcr597p9d.skadnetwork</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@@ -16,7 +16,6 @@ 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()
|
||||||
|
|
||||||
Messaging.messaging().delegate = self
|
Messaging.messaging().delegate = self
|
||||||
|
|
||||||
// For iOS 10 display notification (sent via APNS)
|
// For iOS 10 display notification (sent via APNS)
|
||||||
@@ -95,16 +94,26 @@ extension AppDelegate : UNUserNotificationCenterDelegate {
|
|||||||
Messaging.messaging().appDidReceiveMessage(userInfo)
|
Messaging.messaging().appDidReceiveMessage(userInfo)
|
||||||
|
|
||||||
let roomIdString = userInfo["room_id"] as? String
|
let roomIdString = userInfo["room_id"] as? String
|
||||||
let audioContentIdString = userInfo["audio_content_id"] as? String
|
let contentIdString = userInfo["content_id"] as? String
|
||||||
|
let channelIdString = userInfo["channel_id"] as? String
|
||||||
|
let messageIdString = userInfo["message_id"] as? String
|
||||||
|
|
||||||
if let roomIdString = roomIdString, let roomId = Int(roomIdString), roomId > 0 {
|
if let roomIdString = roomIdString, let roomId = Int(roomIdString), roomId > 0 {
|
||||||
AppState.shared.pushRoomId = roomId
|
AppState.shared.pushRoomId = roomId
|
||||||
}
|
}
|
||||||
|
|
||||||
if let audioContentIdString = audioContentIdString, let audioContentId = Int(audioContentIdString), audioContentId > 0 {
|
if let contentIdString = contentIdString, let audioContentId = Int(contentIdString), audioContentId > 0 {
|
||||||
AppState.shared.pushAudioContentId = audioContentId
|
AppState.shared.pushAudioContentId = audioContentId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let channelIdString = channelIdString, let channelId = Int(channelIdString), channelId > 0 {
|
||||||
|
AppState.shared.pushChannelId = channelId
|
||||||
|
}
|
||||||
|
|
||||||
|
if let messageIdString = messageIdString, let messageId = Int(messageIdString), messageId > 0 {
|
||||||
|
AppState.shared.pushMessageId = messageId
|
||||||
|
}
|
||||||
|
|
||||||
completionHandler()
|
completionHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,7 @@ class AppState: ObservableObject {
|
|||||||
|
|
||||||
@Published var pushRoomId = 0
|
@Published var pushRoomId = 0
|
||||||
@Published var pushChannelId = 0
|
@Published var pushChannelId = 0
|
||||||
|
@Published var pushMessageId = 0
|
||||||
@Published var pushAudioContentId = 0
|
@Published var pushAudioContentId = 0
|
||||||
@Published var roomId = 0 {
|
@Published var roomId = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
|
@@ -105,4 +105,12 @@ enum AppStep {
|
|||||||
case profileUpdate(refresh: () -> Void)
|
case profileUpdate(refresh: () -> Void)
|
||||||
|
|
||||||
case followingList
|
case followingList
|
||||||
|
|
||||||
|
case orderListAll
|
||||||
|
|
||||||
|
case newContentAll
|
||||||
|
|
||||||
|
case curationAll(title: String, curationId: Int)
|
||||||
|
|
||||||
|
case contentRankingAll
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,8 @@ struct SodaLiveApp: App {
|
|||||||
let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems
|
let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems
|
||||||
let roomId = queryItems?.filter({$0.name == "room_id"}).first?.value
|
let roomId = queryItems?.filter({$0.name == "room_id"}).first?.value
|
||||||
let channelId = queryItems?.filter({$0.name == "channel_id"}).first?.value
|
let channelId = queryItems?.filter({$0.name == "channel_id"}).first?.value
|
||||||
let audioContentId = queryItems?.filter({$0.name == "audio_content_id"}).first?.value
|
let messageId = queryItems?.filter({$0.name == "message_id"}).first?.value
|
||||||
|
let audioContentId = queryItems?.filter({$0.name == "content_id"}).first?.value
|
||||||
|
|
||||||
if let roomId = roomId {
|
if let roomId = roomId {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
@@ -40,6 +41,12 @@ struct SodaLiveApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let messageId = messageId {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
|
AppState.shared.pushMessageId = Int(messageId) ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let audioContentId = audioContentId {
|
if let audioContentId = audioContentId {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
AppState.shared.pushAudioContentId = Int(audioContentId) ?? 0
|
AppState.shared.pushAudioContentId = Int(audioContentId) ?? 0
|
||||||
|
@@ -13,7 +13,7 @@ struct AddAllPlaybackTrackingRequest: Encodable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PlaybackTrackingData: Encodable {
|
struct PlaybackTrackingData: Encodable {
|
||||||
let audioContentId: Int
|
let contentId: Int
|
||||||
let playDateTime: String
|
let playDateTime: String
|
||||||
let isPreview: Bool
|
let isPreview: Bool
|
||||||
}
|
}
|
||||||
|
54
SodaLive/Sources/Content/All/ContentNewAllItemView.swift
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// ContentNewAllItemView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct ContentNewAllItemView: View {
|
||||||
|
|
||||||
|
let item: GetAudioContentMainItem
|
||||||
|
|
||||||
|
@State var width: CGFloat = 0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
KFImage(URL(string: item.coverImageUrl))
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: width, height: width, alignment: .top)
|
||||||
|
.cornerRadius(2.7)
|
||||||
|
|
||||||
|
Text(item.title)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
|
.frame(width: width, alignment: .leading)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.lineLimit(2)
|
||||||
|
|
||||||
|
HStack(spacing: 5.3) {
|
||||||
|
KFImage(URL(string: item.creatorProfileImageUrl))
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: 21.3, height: 21.3)
|
||||||
|
.clipShape(Circle())
|
||||||
|
.onTapGesture { AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId)) }
|
||||||
|
|
||||||
|
Text(item.creatorNickname)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
}
|
||||||
|
.frame(width: width)
|
||||||
|
.onTapGesture { AppState.shared.setAppStep(step: .contentDetail(contentId: item.contentId)) }
|
||||||
|
.onAppear {
|
||||||
|
width = (screenSize().width - 40) / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
SodaLive/Sources/Content/All/ContentNewAllView.swift
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// ContentNewAllView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentNewAllView: View {
|
||||||
|
|
||||||
|
@StateObject var viewModel = ContentNewAllViewModel()
|
||||||
|
|
||||||
|
let columns = [
|
||||||
|
GridItem(.flexible(), alignment: .top),
|
||||||
|
GridItem(.flexible(), alignment: .top)
|
||||||
|
]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
DetailNavigationBar(title: "새로운 콘텐츠")
|
||||||
|
|
||||||
|
Text("※ 최근 2주간 등록된 새로운 콘텐츠 입니다.")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 14.7))
|
||||||
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
.frame(width: screenSize().width, alignment: .leading)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
|
ContentMainNewContentThemeView(
|
||||||
|
themes: viewModel.themeList,
|
||||||
|
selectTheme: {
|
||||||
|
viewModel.selectedTheme = $0
|
||||||
|
},
|
||||||
|
selectedTheme: $viewModel.selectedTheme
|
||||||
|
).padding(.top, 13.3)
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("전체")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
|
||||||
|
Text("\(viewModel.totalCount)")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
|
.padding(.leading, 8)
|
||||||
|
|
||||||
|
Text("개")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
.padding(.leading, 2)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 13.3)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
|
||||||
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
|
LazyVGrid(columns: columns, spacing: 13.3) {
|
||||||
|
ForEach(0..<viewModel.newContentList.count, id: \.self) { index in
|
||||||
|
ContentNewAllItemView(item: viewModel.newContentList[index])
|
||||||
|
.onAppear {
|
||||||
|
if index == viewModel.newContentList.count - 1 {
|
||||||
|
viewModel.getNewContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
viewModel.getThemeList()
|
||||||
|
viewModel.getNewContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentNewAllView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentNewAllView()
|
||||||
|
}
|
||||||
|
}
|
130
SodaLive/Sources/Content/All/ContentNewAllViewModel.swift
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// ContentNewAllViewModel.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class ContentNewAllViewModel: ObservableObject {
|
||||||
|
|
||||||
|
private let repository = ContentRepository()
|
||||||
|
private var subscription = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@Published var errorMessage = ""
|
||||||
|
@Published var isShowPopup = false
|
||||||
|
@Published var isLoading = false
|
||||||
|
|
||||||
|
@Published var themeList = [String]()
|
||||||
|
@Published var newContentList = [GetAudioContentMainItem]()
|
||||||
|
|
||||||
|
@Published var selectedTheme = "전체" {
|
||||||
|
didSet {
|
||||||
|
page = 1
|
||||||
|
isLast = false
|
||||||
|
getNewContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Published var totalCount = 0
|
||||||
|
|
||||||
|
var page = 1
|
||||||
|
var isLast = false
|
||||||
|
private let pageSize = 10
|
||||||
|
|
||||||
|
func getNewContentList() {
|
||||||
|
if (!isLast && !isLoading) {
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
repository.getNewContentAllOfTheme(
|
||||||
|
theme: selectedTheme == "전체" ? "" : selectedTheme,
|
||||||
|
page: page,
|
||||||
|
size: pageSize
|
||||||
|
)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
self.isLoading = false
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponse<GetNewContentAllResponse>.self, from: responseData)
|
||||||
|
self.isLoading = false
|
||||||
|
|
||||||
|
if let data = decoded.data, decoded.success {
|
||||||
|
if page == 1 {
|
||||||
|
newContentList.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.totalCount = data.totalCount
|
||||||
|
|
||||||
|
if !data.items.isEmpty {
|
||||||
|
page += 1
|
||||||
|
self.newContentList.append(contentsOf: data.items)
|
||||||
|
} else {
|
||||||
|
isLast = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
} else {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getThemeList() {
|
||||||
|
repository.getNewContentThemeList()
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponse<[String]>.self, from: responseData)
|
||||||
|
|
||||||
|
if let data = decoded.data, decoded.success {
|
||||||
|
self.themeList.append("전체")
|
||||||
|
self.themeList.append(contentsOf: data)
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
|
}
|
153
SodaLive/Sources/Content/All/ContentRankingAllView.swift
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
//
|
||||||
|
// ContentRankingAllView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/10/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct ContentRankingAllView: View {
|
||||||
|
|
||||||
|
@StateObject var viewModel = ContentRankingAllViewModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
DetailNavigationBar(title: "인기 콘텐츠")
|
||||||
|
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
Text("\(viewModel.dateString)")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Text("※ 인기 콘텐츠의 순위는 매주 업데이트됩니다.")
|
||||||
|
.font(.custom(Font.light.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
|
}
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
.frame(width: screenSize().width - 26.7)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
|
VStack(spacing: 20) {
|
||||||
|
ForEach(0..<viewModel.contentRankingItemList.count, id: \.self) { index in
|
||||||
|
let item = viewModel.contentRankingItemList[index]
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
KFImage(URL(string: item.coverImageUrl))
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: 66.7, height: 66.7, alignment: .top)
|
||||||
|
.clipped()
|
||||||
|
.cornerRadius(5.3)
|
||||||
|
|
||||||
|
Text("\(index + 1)")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||||
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Text(item.themeStr)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 8))
|
||||||
|
.foregroundColor(Color(hex: "3bac6a"))
|
||||||
|
.padding(2.6)
|
||||||
|
.background(Color(hex: "28312b"))
|
||||||
|
.cornerRadius(2.6)
|
||||||
|
|
||||||
|
Text(item.duration)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 8))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.padding(2.6)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.cornerRadius(2.6)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(item.creatorNickname)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 10.7))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
|
||||||
|
Text(item.title)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
|
.lineLimit(2)
|
||||||
|
.padding(.top, 2.7)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if item.price > 0 {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Image("ic_can")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 17, height: 17)
|
||||||
|
|
||||||
|
Text("\(item.price)")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.foregroundColor(Color(hex: "909090"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("무료")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.foregroundColor(Color(hex: "ffffff"))
|
||||||
|
.padding(.horizontal, 5.3)
|
||||||
|
.padding(.vertical, 2.7)
|
||||||
|
.background(Color(hex: "cf5c37"))
|
||||||
|
.cornerRadius(2.6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: 66.7)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture {
|
||||||
|
AppState
|
||||||
|
.shared
|
||||||
|
.setAppStep(step: .contentDetail(contentId: item.contentId))
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
if index == viewModel.contentRankingItemList.count - 1 {
|
||||||
|
viewModel.getContentRanking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(13.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if viewModel.isLoading {
|
||||||
|
LoadingView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) {
|
||||||
|
GeometryReader { geo in
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Text(viewModel.errorMessage)
|
||||||
|
.padding(.vertical, 13.3)
|
||||||
|
.padding(.horizontal, 6.7)
|
||||||
|
.frame(width: geo.size.width - 66.7, alignment: .center)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.background(Color(hex: "9970ff"))
|
||||||
|
.foregroundColor(Color.white)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.cornerRadius(20)
|
||||||
|
.padding(.top, 66.7)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
viewModel.getContentRanking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentRankingAllView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentRankingAllView()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// ContentRankingAllViewModel.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/10/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class ContentRankingAllViewModel: ObservableObject {
|
||||||
|
|
||||||
|
private let repository = ContentRepository()
|
||||||
|
private var subscription = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@Published var errorMessage = ""
|
||||||
|
@Published var isShowPopup = false
|
||||||
|
@Published var isLoading = false
|
||||||
|
|
||||||
|
@Published var dateString = ""
|
||||||
|
@Published var contentRankingItemList = [GetAudioContentRankingItem]()
|
||||||
|
|
||||||
|
var page = 1
|
||||||
|
var isLast = false
|
||||||
|
private let pageSize = 10
|
||||||
|
|
||||||
|
func getContentRanking() {
|
||||||
|
if (!isLast && !isLoading && page <= 5) {
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
repository.getContentRanking(page: page, size: pageSize)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
self.isLoading = false
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponse<GetAudioContentRanking>.self, from: responseData)
|
||||||
|
self.isLoading = false
|
||||||
|
|
||||||
|
if let data = decoded.data, decoded.success {
|
||||||
|
if page == 1 {
|
||||||
|
contentRankingItemList.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
dateString = "\(data.startDate)~\(data.endDate)"
|
||||||
|
|
||||||
|
if !data.items.isEmpty {
|
||||||
|
page += 1
|
||||||
|
self.contentRankingItemList.append(contentsOf: data.items)
|
||||||
|
} else {
|
||||||
|
isLast = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
SodaLive/Sources/Content/All/GetNewContentAllResponse.swift
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// GetNewContentAllResponse.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct GetNewContentAllResponse: Decodable {
|
||||||
|
let totalCount: Int
|
||||||
|
let items: [GetAudioContentMainItem]
|
||||||
|
}
|
@@ -25,6 +25,11 @@ enum ContentApi {
|
|||||||
case getMain
|
case getMain
|
||||||
case getNewContentOfTheme(theme: String)
|
case getNewContentOfTheme(theme: String)
|
||||||
case donation(request: AudioContentDonationRequest)
|
case donation(request: AudioContentDonationRequest)
|
||||||
|
case modifyComment(request: ModifyCommentRequest)
|
||||||
|
case getNewContentThemeList
|
||||||
|
case getNewContentAllOfTheme(theme: String, page: Int, size: Int)
|
||||||
|
case getAudioContentListByCurationId(curationId: Int, page: Int, size: Int, sort: ContentCurationViewModel.Sort)
|
||||||
|
case getContentRanking(page: Int, size: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ContentApi: TargetType {
|
extension ContentApi: TargetType {
|
||||||
@@ -81,15 +86,32 @@ extension ContentApi: TargetType {
|
|||||||
|
|
||||||
case .donation:
|
case .donation:
|
||||||
return "/audio-content/donation"
|
return "/audio-content/donation"
|
||||||
|
|
||||||
|
case .modifyComment:
|
||||||
|
return "/audio-content/comment"
|
||||||
|
|
||||||
|
case .getNewContentThemeList:
|
||||||
|
return "/audio-content/main/theme"
|
||||||
|
|
||||||
|
case .getNewContentAllOfTheme:
|
||||||
|
return "/audio-content/main/new/all"
|
||||||
|
|
||||||
|
case .getAudioContentListByCurationId(let curationId, _, _, _):
|
||||||
|
return "/audio-content/curation/\(curationId)"
|
||||||
|
|
||||||
|
case .getContentRanking:
|
||||||
|
return "/audio-content/ranking"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var method: Moya.Method {
|
var method: Moya.Method {
|
||||||
switch self {
|
switch self {
|
||||||
case .getAudioContentList, .getAudioContentDetail, .getOrderList, .getAudioContentThemeList, .getAudioContentCommentList, .getAudioContentCommentReplyList, .getMain, .getNewContentOfTheme:
|
case .getAudioContentList, .getAudioContentDetail, .getOrderList, .getAudioContentThemeList,
|
||||||
|
.getAudioContentCommentList, .getAudioContentCommentReplyList, .getMain, .getNewContentOfTheme,
|
||||||
|
.getNewContentThemeList, .getNewContentAllOfTheme, .getAudioContentListByCurationId, .getContentRanking:
|
||||||
return .get
|
return .get
|
||||||
|
|
||||||
case .likeContent, .modifyAudioContent:
|
case .likeContent, .modifyAudioContent, .modifyComment:
|
||||||
return .put
|
return .put
|
||||||
|
|
||||||
case .registerComment, .orderAudioContent, .addAllPlaybackTracking, .uploadAudioContent, .donation:
|
case .registerComment, .orderAudioContent, .addAllPlaybackTracking, .uploadAudioContent, .donation:
|
||||||
@@ -173,6 +195,38 @@ extension ContentApi: TargetType {
|
|||||||
|
|
||||||
case .donation(let request):
|
case .donation(let request):
|
||||||
return .requestJSONEncodable(request)
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
|
case .modifyComment(let request):
|
||||||
|
return .requestJSONEncodable(request)
|
||||||
|
|
||||||
|
case .getNewContentThemeList:
|
||||||
|
return .requestPlain
|
||||||
|
|
||||||
|
case .getNewContentAllOfTheme(let theme, let page, let size):
|
||||||
|
let parameters = [
|
||||||
|
"theme": theme,
|
||||||
|
"page": page - 1,
|
||||||
|
"size": size
|
||||||
|
] as [String : Any]
|
||||||
|
|
||||||
|
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
|
||||||
|
|
||||||
|
case .getAudioContentListByCurationId(_, let page, let size, let sort):
|
||||||
|
let parameters = [
|
||||||
|
"page": page - 1,
|
||||||
|
"size": size,
|
||||||
|
"sort-type": sort
|
||||||
|
] as [String : Any]
|
||||||
|
|
||||||
|
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
|
||||||
|
|
||||||
|
case .getContentRanking(let page, let size):
|
||||||
|
let parameters = [
|
||||||
|
"page": page - 1,
|
||||||
|
"size": size,
|
||||||
|
] as [String : Any]
|
||||||
|
|
||||||
|
return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ struct ContentListItemView: View {
|
|||||||
|
|
||||||
HStack(spacing: 13.3) {
|
HStack(spacing: 13.3) {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
Image("ic_heart")
|
Image("ic_heart_777")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 13.3, height: 13.3)
|
.frame(width: 13.3, height: 13.3)
|
||||||
|
|
||||||
|
@@ -74,6 +74,26 @@ final class ContentRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func donation(contentId: Int, can: Int, comment: String) -> AnyPublisher<Response, MoyaError> {
|
func donation(contentId: Int, can: Int, comment: String) -> AnyPublisher<Response, MoyaError> {
|
||||||
return api.requestPublisher(.donation(request: AudioContentDonationRequest(audioContentId: contentId, donationCan: can, comment: comment)))
|
return api.requestPublisher(.donation(request: AudioContentDonationRequest(contentId: contentId, donationCan: can, comment: comment)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyComment(request: ModifyCommentRequest) -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.modifyComment(request: request))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNewContentThemeList() -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.getNewContentThemeList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNewContentAllOfTheme(theme: String, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.getNewContentAllOfTheme(theme: theme, page: page, size: size))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAudioContentListByCurationId(curationId: Int, page: Int, size: Int, sort: ContentCurationViewModel.Sort) -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.getAudioContentListByCurationId(curationId: curationId, page: page, size: size, sort: sort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContentRanking(page: Int, size: Int) -> AnyPublisher<Response, MoyaError> {
|
||||||
|
return api.requestPublisher(.getContentRanking(page: page, size: size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -221,7 +221,7 @@ struct ContentCreateView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
TextField("가격을 입력하세요(10캔 이상)", text: $viewModel.priceString)
|
TextField("가격을 입력하세요(5캔 이상)", text: $viewModel.priceString)
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.font(.custom(Font.bold.rawValue, size: 14.7))
|
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||||
@@ -253,12 +253,12 @@ struct ContentCreateView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.top, 13.3)
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
Text("※ 대여가격은 소장가격의 70%로 자동 반영")
|
Text("※ 대여가격은 소장가격의 60%로 자동 반영")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
Text("※ 콘텐츠의 최소금액은 10캔 입니다")
|
Text("※ 콘텐츠의 최소금액은 5캔 입니다")
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "777777"))
|
.foregroundColor(Color(hex: "777777"))
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
@@ -321,6 +321,61 @@ struct ContentCreateView: View {
|
|||||||
.padding(.top, 26.7)
|
.padding(.top, 26.7)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
Text("미리듣기 시간 설정")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
Text("미리듣기 시간을 직접 설정하지 않으면 콘텐츠 앞부분 30초가 자동으로 설정됩니다. 미리듣기의 시간제한은 없습니다.")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
HStack(spacing: 13.3) {
|
||||||
|
VStack(spacing: 5.3) {
|
||||||
|
Text("시작 시간")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
TextField("00:00:00", text: $viewModel.previewStartTime)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 14.6))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.padding(.vertical, 16.7)
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.cornerRadius(6.7)
|
||||||
|
.keyboardType(.default)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(spacing: 5.3) {
|
||||||
|
Text("종료 시간")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
TextField("00:00:30", text: $viewModel.previewEndTime)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 14.6))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.padding(.vertical, 16.7)
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.cornerRadius(6.7)
|
||||||
|
.keyboardType(.default)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.top, 3.3)
|
||||||
|
}
|
||||||
|
.padding(.top, 26.7)
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
HStack(alignment: .top, spacing: 0) {
|
HStack(alignment: .top, spacing: 0) {
|
||||||
Text("등록")
|
Text("등록")
|
||||||
|
@@ -58,6 +58,9 @@ final class ContentCreateViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Published var previewStartTime: String = ""
|
||||||
|
@Published var previewEndTime: String = ""
|
||||||
|
|
||||||
var placeholder = "내용을 입력하세요"
|
var placeholder = "내용을 입력하세요"
|
||||||
|
|
||||||
func uploadAudioContent() {
|
func uploadAudioContent() {
|
||||||
@@ -71,7 +74,9 @@ final class ContentCreateViewModel: ObservableObject {
|
|||||||
price: price,
|
price: price,
|
||||||
themeId: theme!.id,
|
themeId: theme!.id,
|
||||||
isAdult: isAdult,
|
isAdult: isAdult,
|
||||||
isCommentAvailable: isAvailableComment
|
isCommentAvailable: isAvailableComment,
|
||||||
|
previewStartTime: previewStartTime.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 ? previewStartTime : nil,
|
||||||
|
previewEndTime: previewEndTime.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 ? previewEndTime : nil
|
||||||
)
|
)
|
||||||
|
|
||||||
var multipartData = [MultipartFormData]()
|
var multipartData = [MultipartFormData]()
|
||||||
@@ -205,12 +210,68 @@ final class ContentCreateViewModel: ObservableObject {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isFree && price < 10 {
|
if !isFree && price < 5 {
|
||||||
errorMessage = "콘텐츠의 최소금액은 10캔 입니다."
|
errorMessage = "콘텐츠의 최소금액은 5캔 입니다."
|
||||||
isShowPopup = true
|
isShowPopup = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if previewStartTime.count > 0 && previewEndTime.count > 0 {
|
||||||
|
let startTimeArray = previewStartTime.split(separator: ":")
|
||||||
|
if startTimeArray.count != 3 {
|
||||||
|
errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다"
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for time in startTimeArray {
|
||||||
|
if time.count != 2 {
|
||||||
|
errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다"
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let endTimeArray = previewStartTime.split(separator: ":")
|
||||||
|
if endTimeArray.count != 3 {
|
||||||
|
errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다"
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for time in endTimeArray {
|
||||||
|
if time.count != 2 {
|
||||||
|
errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다"
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeDifference = timeDifference(startTime: previewStartTime, endTime: previewEndTime)
|
||||||
|
if timeDifference < 30.0 {
|
||||||
|
errorMessage = "미리 듣기의 최소 시간은 30초 입니다"
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if previewStartTime.count > 0 || previewEndTime.count > 0 {
|
||||||
|
errorMessage = "미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다."
|
||||||
|
isShowPopup = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func timeDifference(startTime: String, endTime: String) -> Double {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "HH:mm:ss"
|
||||||
|
|
||||||
|
if let date1 = dateFormatter.date(from: startTime), let date2 = dateFormatter.date(from: endTime) {
|
||||||
|
return date2.timeIntervalSince(date1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,4 +15,6 @@ struct CreateAudioContentRequest: Encodable {
|
|||||||
let themeId: Int
|
let themeId: Int
|
||||||
let isAdult: Bool
|
let isAdult: Bool
|
||||||
let isCommentAvailable: Bool
|
let isCommentAvailable: Bool
|
||||||
|
let previewStartTime: String?
|
||||||
|
let previewEndTime: String?
|
||||||
}
|
}
|
||||||
|
110
SodaLive/Sources/Content/Curation/ContentCurationView.swift
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// ContentCurationView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentCurationView: View {
|
||||||
|
|
||||||
|
@StateObject var viewModel = ContentCurationViewModel()
|
||||||
|
|
||||||
|
let title: String
|
||||||
|
let curationId: Int
|
||||||
|
|
||||||
|
let columns = [
|
||||||
|
GridItem(.flexible(), alignment: .top),
|
||||||
|
GridItem(.flexible(), alignment: .top)
|
||||||
|
]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
BaseView(isLoading: $viewModel.isLoading) {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
DetailNavigationBar(title: title)
|
||||||
|
|
||||||
|
HStack(spacing: 13.3) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("최신순")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(
|
||||||
|
Color(hex: "e2e2e2")
|
||||||
|
.opacity(viewModel.sort == .NEWEST ? 1 : 0.5)
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
if viewModel.sort != .NEWEST {
|
||||||
|
viewModel.sort = .NEWEST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text("높은 가격순")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(
|
||||||
|
Color(hex: "e2e2e2")
|
||||||
|
.opacity(viewModel.sort == .PRICE_HIGH ? 1 : 0.5)
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
if viewModel.sort != .PRICE_HIGH {
|
||||||
|
viewModel.sort = .PRICE_HIGH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text("낮은 가격순")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(
|
||||||
|
Color(hex: "e2e2e2")
|
||||||
|
.opacity(viewModel.sort == .PRICE_LOW ? 1 : 0.5)
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
if viewModel.sort != .PRICE_LOW {
|
||||||
|
viewModel.sort = .PRICE_LOW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 13.3)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.background(Color(hex: "161616"))
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("전체")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
|
||||||
|
Text("\(viewModel.totalCount)")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "ff5c49"))
|
||||||
|
.padding(.leading, 8)
|
||||||
|
|
||||||
|
Text("개")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "e2e2e2"))
|
||||||
|
.padding(.leading, 2)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 13.3)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
|
||||||
|
ScrollView(.vertical, showsIndicators: false) {
|
||||||
|
LazyVGrid(columns: columns, spacing: 13.3) {
|
||||||
|
ForEach(0..<viewModel.contentList.count, id: \.self) { index in
|
||||||
|
ContentNewAllItemView(item: viewModel.contentList[index])
|
||||||
|
.onAppear {
|
||||||
|
if index == viewModel.contentList.count - 1 {
|
||||||
|
viewModel.getContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
viewModel.curationId = curationId
|
||||||
|
viewModel.getContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// ContentCurationViewModel.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class ContentCurationViewModel: ObservableObject {
|
||||||
|
|
||||||
|
enum Sort: String {
|
||||||
|
case NEWEST, PRICE_HIGH, PRICE_LOW
|
||||||
|
}
|
||||||
|
|
||||||
|
private let repository = ContentRepository()
|
||||||
|
private var subscription = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@Published var errorMessage = ""
|
||||||
|
@Published var isShowPopup = false
|
||||||
|
@Published var isLoading = false
|
||||||
|
|
||||||
|
@Published var totalCount = 0
|
||||||
|
@Published var sort = Sort.NEWEST {
|
||||||
|
didSet {
|
||||||
|
page = 1
|
||||||
|
isLast = false
|
||||||
|
getContentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var contentList: [GetAudioContentMainItem] = []
|
||||||
|
|
||||||
|
var curationId = 0
|
||||||
|
var page = 1
|
||||||
|
var isLast = false
|
||||||
|
private let pageSize = 10
|
||||||
|
|
||||||
|
func getContentList() {
|
||||||
|
if (!isLast && !isLoading) {
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
repository.getAudioContentListByCurationId(
|
||||||
|
curationId: curationId,
|
||||||
|
page: page,
|
||||||
|
size: pageSize,
|
||||||
|
sort: sort
|
||||||
|
)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponse<GetCurationContentResponse>.self, from: responseData)
|
||||||
|
|
||||||
|
if let data = decoded.data, decoded.success {
|
||||||
|
if page == 1 {
|
||||||
|
self.contentList.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !data.items.isEmpty {
|
||||||
|
page += 1
|
||||||
|
self.totalCount = data.totalCount
|
||||||
|
self.contentList.append(contentsOf: data.items)
|
||||||
|
} else {
|
||||||
|
isLast = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isLoading = false
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// GetCurationContentResponse.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/27.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct GetCurationContentResponse: Decodable {
|
||||||
|
let totalCount: Int
|
||||||
|
let items: [GetAudioContentMainItem]
|
||||||
|
}
|
@@ -9,81 +9,168 @@ import SwiftUI
|
|||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
|
||||||
struct AudioContentCommentItemView: View {
|
struct AudioContentCommentItemView: View {
|
||||||
|
let contentCreatorId: Int
|
||||||
let comment: GetAudioContentCommentListItem
|
let audioContentId: Int
|
||||||
|
let commentItem: GetAudioContentCommentListItem
|
||||||
let isReplyComment: Bool
|
let isReplyComment: Bool
|
||||||
|
let isShowPopupMenuButton: Bool
|
||||||
|
|
||||||
|
let modifyComment: (Int, String) -> Void
|
||||||
|
let onClickDelete: (Int) -> Void
|
||||||
|
|
||||||
|
@State var isShowPopupMenu: Bool = false
|
||||||
|
@State var isModeModify: Bool = false
|
||||||
|
@State var comment: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
ZStack(alignment: .topTrailing) {
|
||||||
HStack(spacing: 6.7) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
KFImage(URL(string: comment.profileUrl))
|
HStack(spacing: 6.7) {
|
||||||
.resizable()
|
KFImage(URL(string: commentItem.profileUrl))
|
||||||
.frame(width: 40, height: 40)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
|
||||||
Text(comment.nickname)
|
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
|
||||||
|
|
||||||
Text(comment.date)
|
|
||||||
.font(.custom(Font.medium.rawValue, size: 10.3))
|
|
||||||
.foregroundColor(Color(hex: "525252"))
|
|
||||||
.padding(.top, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
if comment.donationCan > 0 {
|
|
||||||
HStack(spacing: 3) {
|
|
||||||
Image("ic_can")
|
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 13.3, height: 13.3)
|
.frame(width: 40, height: 40)
|
||||||
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text("\(comment.donationCan)")
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
.font(.custom(Font.bold.rawValue, size: 12))
|
Text(commentItem.nickname)
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 6.7)
|
|
||||||
.padding(.vertical, 2.7)
|
|
||||||
.background(
|
|
||||||
comment.donationCan >= 100000 ? Color(hex: "973a3a") :
|
|
||||||
comment.donationCan >= 50000 ? Color(hex: "d85e37") :
|
|
||||||
comment.donationCan >= 10000 ? Color(hex: "d38c38") :
|
|
||||||
comment.donationCan >= 5000 ? Color(hex: "59548f") :
|
|
||||||
comment.donationCan >= 1000 ? Color(hex: "4d6aa4") :
|
|
||||||
comment.donationCan >= 500 ? Color(hex: "2d7390") :
|
|
||||||
Color(hex: "548f7d")
|
|
||||||
)
|
|
||||||
.cornerRadius(10.7)
|
|
||||||
.padding(.leading, 46.7)
|
|
||||||
.padding(.bottom, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
|
||||||
VStack(alignment: .leading, spacing: 13.3) {
|
|
||||||
Text(comment.comment)
|
|
||||||
.font(.custom(Font.medium.rawValue, size: 12))
|
|
||||||
.foregroundColor(Color(hex: "777777"))
|
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
|
||||||
.padding(.top, comment.donationCan > 0 ? 0 : 13.3)
|
|
||||||
|
|
||||||
if !isReplyComment {
|
|
||||||
Text(comment.replyCount > 0 ? "답글 \(comment.replyCount)개" : "답글 쓰기")
|
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "9970ff"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Text(commentItem.date)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 10.3))
|
||||||
|
.foregroundColor(Color(hex: "525252"))
|
||||||
|
.padding(.top, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if isShowPopupMenuButton && (contentCreatorId == UserDefaults.int(forKey: .userId) || commentItem.writerId == UserDefaults.int(forKey: .userId)) {
|
||||||
|
Image("ic_seemore_vertical")
|
||||||
|
.onTapGesture { isShowPopupMenu.toggle() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
if commentItem.donationCan > 0 {
|
||||||
|
HStack(spacing: 3) {
|
||||||
|
Image("ic_can")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 13.3, height: 13.3)
|
||||||
|
|
||||||
|
Text("\(commentItem.donationCan)")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 12))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 6.7)
|
||||||
|
.padding(.vertical, 2.7)
|
||||||
|
.background(
|
||||||
|
commentItem.donationCan >= 100000 ? Color(hex: "973a3a") :
|
||||||
|
commentItem.donationCan >= 50000 ? Color(hex: "d85e37") :
|
||||||
|
commentItem.donationCan >= 10000 ? Color(hex: "d38c38") :
|
||||||
|
commentItem.donationCan >= 5000 ? Color(hex: "59548f") :
|
||||||
|
commentItem.donationCan >= 1000 ? Color(hex: "4d6aa4") :
|
||||||
|
commentItem.donationCan >= 500 ? Color(hex: "2d7390") :
|
||||||
|
Color(hex: "548f7d")
|
||||||
|
)
|
||||||
|
.cornerRadius(10.7)
|
||||||
|
.padding(.leading, 46.7)
|
||||||
|
.padding(.bottom, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
if isModeModify {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
TextField("댓글을 입력해 보세요.", text: $comment)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
.accentColor(Color(hex: "3bb9f1"))
|
||||||
|
.keyboardType(.default)
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image("btn_message_send")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
|
.padding(6.7)
|
||||||
|
.onTapGesture {
|
||||||
|
hideKeyboard()
|
||||||
|
if commentItem.comment != comment {
|
||||||
|
modifyComment(commentItem.id, comment)
|
||||||
|
}
|
||||||
|
isModeModify = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(Color(hex: "232323"))
|
||||||
|
.cornerRadius(10)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.strokeBorder(lineWidth: 1)
|
||||||
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
VStack(alignment: .leading, spacing: 13.3) {
|
||||||
|
Text(commentItem.comment)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 12))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.padding(.top, commentItem.donationCan > 0 ? 0 : 13.3)
|
||||||
|
|
||||||
|
if !isReplyComment {
|
||||||
|
NavigationLink(
|
||||||
|
destination: AudioContentListReplyView(
|
||||||
|
creatorId: contentCreatorId,
|
||||||
|
audioContentId: audioContentId,
|
||||||
|
parentComment: commentItem
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(commentItem.replyCount > 0 ? "답글 \(commentItem.replyCount)개" : "답글 쓰기")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "9970ff"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading, 46.7)
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(Color(hex: "595959"))
|
||||||
|
.frame(height: 0.5)
|
||||||
|
.padding(.top, 16.7)
|
||||||
}
|
}
|
||||||
.padding(.leading, 46.7)
|
|
||||||
|
|
||||||
Rectangle()
|
if isShowPopupMenu {
|
||||||
.foregroundColor(Color(hex: "595959"))
|
VStack(spacing: 10) {
|
||||||
.frame(height: 0.5)
|
if commentItem.writerId == UserDefaults.int(forKey: .userId) {
|
||||||
.padding(.top, 16.7)
|
Text("수정")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 14))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.onTapGesture {
|
||||||
|
isModeModify = true
|
||||||
|
isShowPopupMenu = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentCreatorId == UserDefaults.int(forKey: .userId) ||
|
||||||
|
commentItem.writerId == UserDefaults.int(forKey: .userId)
|
||||||
|
{
|
||||||
|
Text("삭제")
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 14))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
.onTapGesture {
|
||||||
|
onClickDelete(commentItem.id)
|
||||||
|
isShowPopupMenu = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(10)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.onAppear { comment = commentItem.comment }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,10 +11,15 @@ import Kingfisher
|
|||||||
struct AudioContentCommentListView: View {
|
struct AudioContentCommentListView: View {
|
||||||
|
|
||||||
@Binding var isPresented: Bool
|
@Binding var isPresented: Bool
|
||||||
|
|
||||||
|
let creatorId: Int
|
||||||
let audioContentId: Int
|
let audioContentId: Int
|
||||||
|
|
||||||
@StateObject var viewModel = AudioContentCommentListViewModel()
|
@StateObject var viewModel = AudioContentCommentListViewModel()
|
||||||
|
|
||||||
|
@State private var commentId: Int = 0
|
||||||
|
@State private var isShowDeletePopup: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -59,7 +64,7 @@ struct AudioContentCommentListView: View {
|
|||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
.accentColor(Color(hex: "9970ff"))
|
.accentColor(Color(hex: "3bb9f1"))
|
||||||
.keyboardType(.default)
|
.keyboardType(.default)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
@@ -79,7 +84,7 @@ struct AudioContentCommentListView: View {
|
|||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 10)
|
RoundedRectangle(cornerRadius: 10)
|
||||||
.strokeBorder(lineWidth: 1)
|
.strokeBorder(lineWidth: 1)
|
||||||
.foregroundColor(Color(hex: "9970ff"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -97,13 +102,22 @@ struct AudioContentCommentListView: View {
|
|||||||
LazyVStack(spacing: 13.3) {
|
LazyVStack(spacing: 13.3) {
|
||||||
ForEach(0..<viewModel.commentList.count, id: \.self) { index in
|
ForEach(0..<viewModel.commentList.count, id: \.self) { index in
|
||||||
let comment = viewModel.commentList[index]
|
let comment = viewModel.commentList[index]
|
||||||
NavigationLink {
|
VStack {
|
||||||
AudioContentListReplyView(
|
AudioContentCommentItemView(
|
||||||
|
contentCreatorId: creatorId,
|
||||||
audioContentId: audioContentId,
|
audioContentId: audioContentId,
|
||||||
parentComment: comment
|
commentItem: comment,
|
||||||
|
isReplyComment: false,
|
||||||
|
isShowPopupMenuButton: true,
|
||||||
|
modifyComment: { commentId, comment in
|
||||||
|
hideKeyboard()
|
||||||
|
viewModel.modifyComment(commentId: commentId, audioContentId: audioContentId, comment: comment)
|
||||||
|
},
|
||||||
|
onClickDelete: {
|
||||||
|
commentId = $0
|
||||||
|
isShowDeletePopup = true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} label: {
|
|
||||||
AudioContentCommentItemView(comment: comment, isReplyComment: false)
|
|
||||||
.padding(.horizontal, 26.7)
|
.padding(.horizontal, 26.7)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if index == viewModel.commentList.count - 1 {
|
if index == viewModel.commentList.count - 1 {
|
||||||
@@ -116,6 +130,24 @@ struct AudioContentCommentListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isShowDeletePopup && commentId > 0 {
|
||||||
|
SodaDialog(
|
||||||
|
title: "댓글 삭제",
|
||||||
|
desc: "삭제하시겠습니까?",
|
||||||
|
confirmButtonTitle: "삭제",
|
||||||
|
confirmButtonAction: {
|
||||||
|
viewModel.modifyComment(commentId: commentId, audioContentId: audioContentId, isActive: false)
|
||||||
|
commentId = 0
|
||||||
|
isShowDeletePopup = false
|
||||||
|
},
|
||||||
|
cancelButtonTitle: "취소",
|
||||||
|
cancelButtonAction: {
|
||||||
|
commentId = 0
|
||||||
|
isShowDeletePopup = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if viewModel.isLoading {
|
if viewModel.isLoading {
|
||||||
LoadingView()
|
LoadingView()
|
||||||
}
|
}
|
||||||
|
@@ -121,4 +121,77 @@ class AudioContentCommentListViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
.store(in: &subscription)
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func modifyComment(
|
||||||
|
commentId: Int,
|
||||||
|
audioContentId: Int,
|
||||||
|
comment: String? = nil,
|
||||||
|
isActive: Bool? = nil
|
||||||
|
) {
|
||||||
|
if comment == nil && isActive == nil {
|
||||||
|
errorMessage = "변경사항이 없습니다."
|
||||||
|
isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let comment = comment, comment.trimmingCharacters(in: .whitespaces).isEmpty {
|
||||||
|
errorMessage = "내용을 입력하세요."
|
||||||
|
isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
var request = ModifyCommentRequest(commentId: commentId)
|
||||||
|
|
||||||
|
if let comment = comment {
|
||||||
|
request.comment = comment
|
||||||
|
}
|
||||||
|
|
||||||
|
if let isActive = isActive {
|
||||||
|
request.isActive = isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.modifyComment(request: request)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
self.isLoading = false
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||||
|
|
||||||
|
if decoded.success {
|
||||||
|
self.comment = ""
|
||||||
|
self.page = 1
|
||||||
|
self.isLast = false
|
||||||
|
|
||||||
|
self.commentList.removeAll()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
|
self.getCommentList()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.isLoading = false
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,12 +10,16 @@ import Kingfisher
|
|||||||
|
|
||||||
struct AudioContentListReplyView: View {
|
struct AudioContentListReplyView: View {
|
||||||
|
|
||||||
|
let creatorId: Int
|
||||||
let audioContentId: Int
|
let audioContentId: Int
|
||||||
let parentComment: GetAudioContentCommentListItem
|
let parentComment: GetAudioContentCommentListItem
|
||||||
|
|
||||||
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
||||||
@StateObject var viewModel = AudioContentListReplyViewModel()
|
@StateObject var viewModel = AudioContentListReplyViewModel()
|
||||||
|
|
||||||
|
@State private var commentId: Int = 0
|
||||||
|
@State private var isShowDeletePopup: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@@ -53,7 +57,7 @@ struct AudioContentListReplyView: View {
|
|||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
.accentColor(Color(hex: "9970ff"))
|
.accentColor(Color(hex: "3bb9f1"))
|
||||||
.keyboardType(.default)
|
.keyboardType(.default)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
@@ -73,7 +77,7 @@ struct AudioContentListReplyView: View {
|
|||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 10)
|
RoundedRectangle(cornerRadius: 10)
|
||||||
.strokeBorder(lineWidth: 1)
|
.strokeBorder(lineWidth: 1)
|
||||||
.foregroundColor(Color(hex: "9970ff"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -87,7 +91,15 @@ struct AudioContentListReplyView: View {
|
|||||||
.padding(.bottom, 13.3)
|
.padding(.bottom, 13.3)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
AudioContentCommentItemView(comment: parentComment, isReplyComment: true)
|
AudioContentCommentItemView(
|
||||||
|
contentCreatorId: creatorId,
|
||||||
|
audioContentId: audioContentId,
|
||||||
|
commentItem: parentComment,
|
||||||
|
isReplyComment: true,
|
||||||
|
isShowPopupMenuButton: false,
|
||||||
|
modifyComment: { _, _ in },
|
||||||
|
onClickDelete: { _ in }
|
||||||
|
)
|
||||||
.padding(.horizontal, 26.7)
|
.padding(.horizontal, 26.7)
|
||||||
.padding(.bottom, 13.3)
|
.padding(.bottom, 13.3)
|
||||||
|
|
||||||
@@ -95,7 +107,21 @@ struct AudioContentListReplyView: View {
|
|||||||
LazyVStack(spacing: 13.3) {
|
LazyVStack(spacing: 13.3) {
|
||||||
ForEach(0..<viewModel.commentList.count, id: \.self) { index in
|
ForEach(0..<viewModel.commentList.count, id: \.self) { index in
|
||||||
let comment = viewModel.commentList[index]
|
let comment = viewModel.commentList[index]
|
||||||
AudioContentCommentItemView(comment: comment, isReplyComment: true)
|
AudioContentCommentItemView(
|
||||||
|
contentCreatorId: creatorId,
|
||||||
|
audioContentId: audioContentId,
|
||||||
|
commentItem: comment,
|
||||||
|
isReplyComment: true,
|
||||||
|
isShowPopupMenuButton: true,
|
||||||
|
modifyComment: { commentId, comment in
|
||||||
|
hideKeyboard()
|
||||||
|
viewModel.modifyComment(commentId: commentId, audioContentId: audioContentId, comment: comment)
|
||||||
|
},
|
||||||
|
onClickDelete: {
|
||||||
|
commentId = $0
|
||||||
|
isShowDeletePopup = true
|
||||||
|
}
|
||||||
|
)
|
||||||
.padding(.horizontal, 40)
|
.padding(.horizontal, 40)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if index == viewModel.commentList.count - 1 {
|
if index == viewModel.commentList.count - 1 {
|
||||||
@@ -108,6 +134,28 @@ struct AudioContentListReplyView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarBackButtonHidden()
|
.navigationBarBackButtonHidden()
|
||||||
|
|
||||||
|
if isShowDeletePopup && commentId > 0 {
|
||||||
|
SodaDialog(
|
||||||
|
title: "댓글 삭제",
|
||||||
|
desc: "삭제하시겠습니까?",
|
||||||
|
confirmButtonTitle: "삭제",
|
||||||
|
confirmButtonAction: {
|
||||||
|
viewModel.modifyComment(commentId: commentId, audioContentId: audioContentId, isActive: false)
|
||||||
|
commentId = 0
|
||||||
|
isShowDeletePopup = false
|
||||||
|
},
|
||||||
|
cancelButtonTitle: "취소",
|
||||||
|
cancelButtonAction: {
|
||||||
|
commentId = 0
|
||||||
|
isShowDeletePopup = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if viewModel.isLoading {
|
||||||
|
LoadingView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
viewModel.audioContentId = audioContentId
|
viewModel.audioContentId = audioContentId
|
||||||
|
@@ -121,4 +121,77 @@ final class AudioContentListReplyViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
.store(in: &subscription)
|
.store(in: &subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func modifyComment(
|
||||||
|
commentId: Int,
|
||||||
|
audioContentId: Int,
|
||||||
|
comment: String? = nil,
|
||||||
|
isActive: Bool? = nil
|
||||||
|
) {
|
||||||
|
if comment == nil && isActive == nil {
|
||||||
|
errorMessage = "변경사항이 없습니다."
|
||||||
|
isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let comment = comment, comment.trimmingCharacters(in: .whitespaces).isEmpty {
|
||||||
|
errorMessage = "내용을 입력하세요."
|
||||||
|
isShowPopup = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
var request = ModifyCommentRequest(commentId: commentId)
|
||||||
|
|
||||||
|
if let comment = comment {
|
||||||
|
request.comment = comment
|
||||||
|
}
|
||||||
|
|
||||||
|
if let isActive = isActive {
|
||||||
|
request.isActive = isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.modifyComment(request: request)
|
||||||
|
.sink { result in
|
||||||
|
switch result {
|
||||||
|
case .finished:
|
||||||
|
DEBUG_LOG("finish")
|
||||||
|
case .failure(let error):
|
||||||
|
ERROR_LOG(error.localizedDescription)
|
||||||
|
}
|
||||||
|
} receiveValue: { [unowned self] response in
|
||||||
|
self.isLoading = false
|
||||||
|
let responseData = response.data
|
||||||
|
|
||||||
|
do {
|
||||||
|
let jsonDecoder = JSONDecoder()
|
||||||
|
let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData)
|
||||||
|
|
||||||
|
if decoded.success {
|
||||||
|
self.comment = ""
|
||||||
|
self.page = 1
|
||||||
|
self.isLast = false
|
||||||
|
|
||||||
|
self.commentList.removeAll()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
|
self.getCommentList()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let message = decoded.message {
|
||||||
|
self.errorMessage = message
|
||||||
|
} else {
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
self.isLoading = false
|
||||||
|
self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &subscription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ struct ContentDetailCommentView: View {
|
|||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13.3))
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
.accentColor(Color(hex: "9970ff"))
|
.accentColor(Color(hex: "3bb9f1"))
|
||||||
.keyboardType(.default)
|
.keyboardType(.default)
|
||||||
.padding(.horizontal, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ struct ContentDetailCommentView: View {
|
|||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 10)
|
RoundedRectangle(cornerRadius: 10)
|
||||||
.strokeBorder(lineWidth: 1)
|
.strokeBorder(lineWidth: 1)
|
||||||
.foregroundColor(Color(hex: "9970ff"))
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// ModifyCommentRequest.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/09/08.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct ModifyCommentRequest: Encodable {
|
||||||
|
let commentId: Int
|
||||||
|
var comment: String? = nil
|
||||||
|
var isActive: Bool? = nil
|
||||||
|
}
|
@@ -43,7 +43,7 @@ struct ContentDetailOtherContentView: View {
|
|||||||
}
|
}
|
||||||
.padding(13.3)
|
.padding(13.3)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(Color(hex: "2b2635"))
|
.background(Color(hex: "13181b"))
|
||||||
.cornerRadius(4.7)
|
.cornerRadius(4.7)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import Sliders
|
|||||||
struct ContentDetailPlayView: View {
|
struct ContentDetailPlayView: View {
|
||||||
|
|
||||||
let audioContent: GetAudioContentDetailResponse
|
let audioContent: GetAudioContentDetailResponse
|
||||||
|
let isAlertPreview: Bool
|
||||||
@Binding var isShowPreviewAlert: Bool
|
@Binding var isShowPreviewAlert: Bool
|
||||||
|
|
||||||
@StateObject var contentPlayManager = ContentPlayManager.shared
|
@StateObject var contentPlayManager = ContentPlayManager.shared
|
||||||
@@ -34,7 +35,7 @@ struct ContentDetailPlayView: View {
|
|||||||
)
|
)
|
||||||
.cornerRadius(10.7, corners: [.topLeft, .topRight])
|
.cornerRadius(10.7, corners: [.topLeft, .topRight])
|
||||||
|
|
||||||
Image(isPlaying() ? "btn_audio_content_pause" : "btn_audio_content_play")
|
Image(isPlaying() ? "btn_audio_content_pause" : isAlertPreview ? "btn_audio_content_preview_play" : "btn_audio_content_play")
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if isPlaying() {
|
if isPlaying() {
|
||||||
contentPlayManager.pauseAudio()
|
contentPlayManager.pauseAudio()
|
||||||
|
@@ -66,61 +66,57 @@ struct ContentDetailView: View {
|
|||||||
viewModel.getAudioContentDetail()
|
viewModel.getAudioContentDetail()
|
||||||
}) {
|
}) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
LazyVStack(spacing: 0) {
|
ContentDetailPlayView(
|
||||||
ContentDetailPlayView(
|
audioContent: audioContent,
|
||||||
audioContent: audioContent,
|
isAlertPreview: audioContent.price > 0 && !audioContent.existOrdered && audioContent.orderType == nil && audioContent.creator.creatorId != UserDefaults.int(forKey: .userId),
|
||||||
isShowPreviewAlert: $viewModel.isShowPreviewAlert
|
isShowPreviewAlert: $viewModel.isShowPreviewAlert
|
||||||
)
|
)
|
||||||
|
|
||||||
ContentDetailInfoView(
|
ContentDetailInfoView(
|
||||||
isExpandDescription: $viewModel.isExpandDescription,
|
isExpandDescription: $viewModel.isExpandDescription,
|
||||||
isShowPreviewAlert: $viewModel.isShowPreviewAlert,
|
isShowPreviewAlert: $viewModel.isShowPreviewAlert,
|
||||||
audioContent: audioContent,
|
audioContent: audioContent,
|
||||||
onClickLike: { viewModel.likeContent() },
|
onClickLike: { viewModel.likeContent() },
|
||||||
onClickShare: {
|
onClickShare: {
|
||||||
viewModel.shareAudioContent(
|
viewModel.shareAudioContent(
|
||||||
contentImage: audioContent.coverImageUrl,
|
contentImage: audioContent.coverImageUrl,
|
||||||
contentTitle: "\(audioContent.title) - \(audioContent.creator.nickname)"
|
contentTitle: "\(audioContent.title) - \(audioContent.creator.nickname)"
|
||||||
)
|
|
||||||
},
|
|
||||||
onClickDonation: { viewModel.isShowDonationPopup = true }
|
|
||||||
)
|
|
||||||
|
|
||||||
if audioContent.price > 0 &&
|
|
||||||
!audioContent.existOrdered &&
|
|
||||||
audioContent.orderType == nil &&
|
|
||||||
audioContent.creator.creatorId != UserDefaults.int(forKey: .userId) {
|
|
||||||
ContentDetailPurchaseButton(price: audioContent.price)
|
|
||||||
.contentShape(Rectangle())
|
|
||||||
.onTapGesture { isShowOrderView = true }
|
|
||||||
}
|
|
||||||
|
|
||||||
if audioContent.isCommentAvailable {
|
|
||||||
ContentDetailCommentView(
|
|
||||||
commentCount: audioContent.commentCount,
|
|
||||||
commentList: audioContent.commentList,
|
|
||||||
registerComment: { comment in
|
|
||||||
self.viewModel.registerComment(comment: comment)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
.padding(10.3)
|
},
|
||||||
.background(Color.white.opacity(0.1))
|
onClickDonation: { viewModel.isShowDonationPopup = true }
|
||||||
.cornerRadius(5.3)
|
)
|
||||||
.padding(.top, 13.3)
|
.padding(.horizontal, 13.3)
|
||||||
|
|
||||||
|
if audioContent.price > 0 &&
|
||||||
|
!audioContent.existOrdered &&
|
||||||
|
audioContent.orderType == nil &&
|
||||||
|
audioContent.creator.creatorId != UserDefaults.int(forKey: .userId) {
|
||||||
|
ContentDetailPurchaseButton(price: audioContent.price)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture {
|
.padding(.horizontal, 13.3)
|
||||||
if audioContent.commentCount > 0 {
|
.onTapGesture { isShowOrderView = true }
|
||||||
isShowCommentListView = true
|
}
|
||||||
}
|
|
||||||
|
if audioContent.isCommentAvailable {
|
||||||
|
ContentDetailCommentView(
|
||||||
|
commentCount: audioContent.commentCount,
|
||||||
|
commentList: audioContent.commentList,
|
||||||
|
registerComment: { comment in
|
||||||
|
self.viewModel.registerComment(comment: comment)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.padding(10.3)
|
||||||
|
.background(Color.white.opacity(0.1))
|
||||||
|
.cornerRadius(5.3)
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.padding(.horizontal, 13.3)
|
||||||
|
.onTapGesture {
|
||||||
|
if audioContent.commentCount > 0 {
|
||||||
|
isShowCommentListView = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 13.3)
|
|
||||||
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color(hex: "232323"))
|
|
||||||
.frame(height: 6.7)
|
|
||||||
.padding(.top, 24)
|
|
||||||
|
|
||||||
ContentDetailOtherContentView(
|
ContentDetailOtherContentView(
|
||||||
title: "크리에이터의 다른 콘텐츠",
|
title: "크리에이터의 다른 콘텐츠",
|
||||||
@@ -282,6 +278,7 @@ struct ContentDetailView: View {
|
|||||||
content: {
|
content: {
|
||||||
AudioContentCommentListView(
|
AudioContentCommentListView(
|
||||||
isPresented: $isShowCommentListView,
|
isPresented: $isShowCommentListView,
|
||||||
|
creatorId: viewModel.audioContent!.creator.creatorId,
|
||||||
audioContentId: viewModel.audioContent!.contentId
|
audioContentId: viewModel.audioContent!.contentId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ struct ContentOrderConfirmDialogView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 16.7, height: 16.7)
|
.frame(width: 16.7, height: 16.7)
|
||||||
|
|
||||||
Text("\(orderType == .RENTAL ? Int(ceil(Double(audioContent.price) * 0.7)) : audioContent.price)")
|
Text("\(orderType == .RENTAL ? Int(ceil(Double(audioContent.price) * 0.6)) : audioContent.price)")
|
||||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
@@ -43,7 +43,7 @@ struct ContentOrderDialogView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 16.7, height: 16.7)
|
.frame(width: 16.7, height: 16.7)
|
||||||
|
|
||||||
Text("\(Int(ceil(Double(price) * 0.7)))")
|
Text("\(Int(ceil(Double(price) * 0.6)))")
|
||||||
.font(.custom(Font.bold.rawValue, size: 13.3))
|
.font(.custom(Font.bold.rawValue, size: 13.3))
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
}
|
}
|
||||||
|
@@ -12,11 +12,12 @@ import Kingfisher
|
|||||||
|
|
||||||
struct LiveRoomDonationDialogView: View {
|
struct LiveRoomDonationDialogView: View {
|
||||||
|
|
||||||
|
@AppStorage("can") private var can: Int = UserDefaults.int(forKey: .can)
|
||||||
|
|
||||||
@State private var donationCan = ""
|
@State private var donationCan = ""
|
||||||
@State private var donationMessage = ""
|
@State private var donationMessage = ""
|
||||||
@State private var isShowErrorPopup = false
|
@State private var isShowErrorPopup = false
|
||||||
@State private var errorMessage = ""
|
@State private var errorMessage = ""
|
||||||
@State private var can = 0
|
|
||||||
|
|
||||||
@Binding var isShowing: Bool
|
@Binding var isShowing: Bool
|
||||||
let isAudioContentDonation: Bool
|
let isAudioContentDonation: Bool
|
||||||
@@ -243,9 +244,6 @@ struct LiveRoomDonationDialogView: View {
|
|||||||
}
|
}
|
||||||
.offset(y: isAudioContentDonation ? 0 : 0 - keyboardHandler.keyboardHeight)
|
.offset(y: isAudioContentDonation ? 0 : 0 - keyboardHandler.keyboardHeight)
|
||||||
}
|
}
|
||||||
.onAppear {
|
|
||||||
self.can = UserDefaults.int(forKey: .can)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func limitText() {
|
func limitText() {
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct AudioContentDonationRequest: Encodable {
|
struct AudioContentDonationRequest: Encodable {
|
||||||
let audioContentId: Int
|
let contentId: Int
|
||||||
let donationCan: Int
|
let donationCan: Int
|
||||||
let comment: String
|
let comment: String
|
||||||
let container: String = "ios"
|
let container: String = "ios"
|
||||||
|
@@ -13,9 +13,26 @@ struct ContentMainCurationItemView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
Text(item.title)
|
HStack(spacing: 0) {
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
Text(item.title)
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image("ic_forward")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.onTapGesture {
|
||||||
|
AppState.shared
|
||||||
|
.setAppStep(
|
||||||
|
step: .curationAll(
|
||||||
|
title: item.title,
|
||||||
|
curationId: item.curationId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Text(item.description)
|
Text(item.description)
|
||||||
.font(.custom(Font.medium.rawValue, size: 13))
|
.font(.custom(Font.medium.rawValue, size: 13))
|
||||||
|
@@ -14,8 +14,8 @@ struct ContentMainCurationView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
LazyVStack(spacing: 40) {
|
LazyVStack(spacing: 40) {
|
||||||
ForEach(0..<items.count, id: \.self) {
|
ForEach(0..<items.count, id: \.self) {
|
||||||
let item = items[$0]
|
ContentMainCurationItemView(item: items[$0])
|
||||||
ContentMainCurationItemView(item: item)
|
.padding(.horizontal, 13.3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,9 @@ struct ContentMainMyStashView: View {
|
|||||||
Text("전체보기")
|
Text("전체보기")
|
||||||
.font(.custom(Font.light.rawValue, size: 11.3))
|
.font(.custom(Font.light.rawValue, size: 11.3))
|
||||||
.foregroundColor(Color(hex: "bbbbbb"))
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
.onTapGesture {}
|
.onTapGesture {
|
||||||
|
AppState.shared.setAppStep(step: .orderListAll)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
@@ -33,7 +33,11 @@ struct ContentMainNewContentThemeView: View {
|
|||||||
.stroke(lineWidth: 0.5)
|
.stroke(lineWidth: 0.5)
|
||||||
.foregroundColor(Color(hex: selectedTheme == theme ? "9970ff" : "eeeeee"))
|
.foregroundColor(Color(hex: selectedTheme == theme ? "9970ff" : "eeeeee"))
|
||||||
)
|
)
|
||||||
.onTapGesture { selectTheme(theme) }
|
.onTapGesture {
|
||||||
|
if selectedTheme != theme {
|
||||||
|
selectTheme(theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,20 @@ struct ContentMainNewContentView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
Text("새로운 콘텐츠")
|
HStack(spacing: 0) {
|
||||||
.font(.custom(Font.bold.rawValue, size: 18.3))
|
Text("새로운 콘텐츠")
|
||||||
.foregroundColor(Color(hex: "eeeeee"))
|
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image("ic_forward")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.onTapGesture {
|
||||||
|
AppState.shared.setAppStep(step: .newContentAll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentMainNewContentThemeView(themes: themes, selectTheme: selectTheme, selectedTheme: $selectedTheme)
|
ContentMainNewContentThemeView(themes: themes, selectTheme: selectTheme, selectedTheme: $selectedTheme)
|
||||||
.padding(.vertical, 16.7)
|
.padding(.vertical, 16.7)
|
||||||
|
143
SodaLive/Sources/Content/Main/ContentMainRankingView.swift
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
//
|
||||||
|
// ContentMainRankingView.swift
|
||||||
|
// SodaLive
|
||||||
|
//
|
||||||
|
// Created by klaus on 2023/10/15.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct ContentMainRankingView: View {
|
||||||
|
|
||||||
|
let item: GetAudioContentRanking
|
||||||
|
|
||||||
|
let rows = [
|
||||||
|
GridItem(.fixed(60), alignment: .leading),
|
||||||
|
GridItem(.fixed(60), alignment: .leading),
|
||||||
|
GridItem(.fixed(60), alignment: .leading)
|
||||||
|
]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("인기 콘텐츠")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 18.3))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image("ic_forward")
|
||||||
|
.onTapGesture {
|
||||||
|
AppState.shared.setAppStep(step: .contentRankingAll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
Text("\(item.startDate) ~ \(item.endDate)")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 14.7))
|
||||||
|
.foregroundColor(Color(hex: "eeeeee"))
|
||||||
|
|
||||||
|
Text("※ 인기 콘텐츠의 순위는 매주 업데이트됩니다.")
|
||||||
|
.font(.custom(Font.light.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "bbbbbb"))
|
||||||
|
}
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
.frame(width: screenSize().width - 26.7)
|
||||||
|
.background(Color(hex: "222222"))
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHGrid(rows: rows, spacing: 13.3) {
|
||||||
|
ForEach(0..<item.items.count, id: \.self) { index in
|
||||||
|
let content = item.items[index]
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
KFImage(URL(string: content.coverImageUrl))
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 60, height: 60)
|
||||||
|
.cornerRadius(2.7)
|
||||||
|
|
||||||
|
Text("\(index + 1)")
|
||||||
|
.font(.custom(Font.bold.rawValue, size: 16.7))
|
||||||
|
.foregroundColor(Color(hex: "3bb9f1"))
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(content.title)
|
||||||
|
.lineLimit(2)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 13.3))
|
||||||
|
.foregroundColor(Color(hex: "d2d2d2"))
|
||||||
|
|
||||||
|
Text(content.creatorNickname)
|
||||||
|
.font(.custom(Font.medium.rawValue, size: 11))
|
||||||
|
.foregroundColor(Color(hex: "777777"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: screenSize().width * 0.66, alignment: .leading)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture {
|
||||||
|
AppState
|
||||||
|
.shared
|
||||||
|
.setAppStep(step: .contentDetail(contentId: content.contentId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: 207)
|
||||||
|
}
|
||||||
|
.padding(.top, 13.3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentMainRankingView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentMainRankingView(
|
||||||
|
item: GetAudioContentRanking(
|
||||||
|
startDate: "2023년 10월 2일",
|
||||||
|
endDate: "10월 9일",
|
||||||
|
items: [
|
||||||
|
GetAudioContentRankingItem(
|
||||||
|
contentId: 10,
|
||||||
|
title: "sdffsfddfs",
|
||||||
|
coverImageUrl: "https://test-cf.sodalive.net/audio_content_cover/27/27-cover-77e3a23f-23f2-467c-867f-3e6b7e08060c-9136-1696424061456",
|
||||||
|
themeStr: "커버곡",
|
||||||
|
price: 10,
|
||||||
|
duration: "00:30:20",
|
||||||
|
creatorId: 1,
|
||||||
|
creatorNickname: "ㄹㄴ어ㅏㅣㅇㄴ런아ㅣ"
|
||||||
|
),
|
||||||
|
GetAudioContentRankingItem(
|
||||||
|
contentId: 10,
|
||||||
|
title: "sdffsfddfs",
|
||||||
|
coverImageUrl: "https://test-cf.sodalive.net/audio_content_cover/27/27-cover-77e3a23f-23f2-467c-867f-3e6b7e08060c-9136-1696424061456",
|
||||||
|
themeStr: "커버곡",
|
||||||
|
price: 10,
|
||||||
|
duration: "00:30:20",
|
||||||
|
creatorId: 1,
|
||||||
|
creatorNickname: "ㄹㄴ어ㅏㅣㅇㄴ런아ㅣ"
|
||||||
|
),
|
||||||
|
GetAudioContentRankingItem(
|
||||||
|
contentId: 10,
|
||||||
|
title: "sdffsfddfs",
|
||||||
|
coverImageUrl: "https://test-cf.sodalive.net/audio_content_cover/27/27-cover-77e3a23f-23f2-467c-867f-3e6b7e08060c-9136-1696424061456",
|
||||||
|
themeStr: "커버곡",
|
||||||
|
price: 10,
|
||||||
|
duration: "00:30:20",
|
||||||
|
creatorId: 1,
|
||||||
|
creatorNickname: "ㄹㄴ어ㅏㅣㅇㄴ런아ㅣ"
|
||||||
|
),
|
||||||
|
GetAudioContentRankingItem(
|
||||||
|
contentId: 10,
|
||||||
|
title: "sdffsfddfs",
|
||||||
|
coverImageUrl: "https://test-cf.sodalive.net/audio_content_cover/27/27-cover-77e3a23f-23f2-467c-867f-3e6b7e08060c-9136-1696424061456",
|
||||||
|
themeStr: "커버곡",
|
||||||
|
price: 10,
|
||||||
|
duration: "00:30:20",
|
||||||
|
creatorId: 1,
|
||||||
|
creatorNickname: "ㄹㄴ어ㅏㅣㅇㄴ런아ㅣ"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|