diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/Contents.json index 9fbbcd81..1a3514d5 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/Contents.json +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "app_logo.png", + "filename" : "app_logo@1x.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo.png deleted file mode 100644 index fdf46b01..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@1x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@1x.png new file mode 100644 index 00000000..574066af Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@1x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@2x.png index 6a300ec2..574066af 100644 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@2x.png and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@3x.png index 5408199f..574066af 100644 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@3x.png and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/app_icon/app_logo.imageset/app_logo@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/Contents.json new file mode 100644 index 00000000..8043f60b --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "flowerPopup_background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "flowerPopup_background@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "flowerPopup_background@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background.png new file mode 100644 index 00000000..e2948d16 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@2x.png new file mode 100644 index 00000000..182da1bf Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@3x.png new file mode 100644 index 00000000..2024944b Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/background/flowerPopup_background.imageset/flowerPopup_background@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/Contents.json new file mode 100644 index 00000000..a67caea8 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "img_buds.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "img_buds@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "img_buds@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds.png new file mode 100644 index 00000000..d1e3841e Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@2x.png new file mode 100644 index 00000000..a844d6bc Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@3x.png new file mode 100644 index 00000000..3fe20478 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_small/img_buds.imageset/img_buds@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/Contents.json new file mode 100644 index 00000000..a54769ae --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon_challenge_progress.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_challenge_progress@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_challenge_progress@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress.png new file mode 100644 index 00000000..e83097b1 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@2x.png new file mode 100644 index 00000000..a0d727c1 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@3x.png new file mode 100644 index 00000000..8ba2d8d4 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_state_icon/icon_challenge_progress.imageset/icon_challenge_progress@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Contents.json index b94ba360..5b25b3fe 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Contents.json +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "Property 1=my_bling_sunflower.png", + "filename" : "flower_success_mate_bling_sunflower.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "Property 1=my_bling_sunflower@2x.png", + "filename" : "flower_success_mate_bling_sunflower@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Property 1=my_bling_sunflower@3x.png", + "filename" : "flower_success_mate_bling_sunflower@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower.png deleted file mode 100644 index e39ebb42..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@2x.png deleted file mode 100644 index d1a3bbd8..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@2x.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@3x.png deleted file mode 100644 index 433786ca..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/Property 1=my_bling_sunflower@3x.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower.png new file mode 100644 index 00000000..f766d27c Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@2x.png new file mode 100644 index 00000000..3706666e Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@3x.png new file mode 100644 index 00000000..8d940ce0 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_bling_sunflower.imageset/flower_success_mate_bling_sunflower@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Contents.json index 34d97290..ab96dae5 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Contents.json +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "Property 1=mate_sunflower.png", + "filename" : "flower_success_mate_sunflower.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "Property 1=mate_sunflower@2x.png", + "filename" : "flower_success_mate_sunflower@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Property 1=mate_sunflower@3x.png", + "filename" : "flower_success_mate_sunflower@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower.png deleted file mode 100644 index 2b856105..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@2x.png deleted file mode 100644 index 9b329c3a..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@2x.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@3x.png deleted file mode 100644 index f2a73549..00000000 Binary files a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/Property 1=mate_sunflower@3x.png and /dev/null differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower.png new file mode 100644 index 00000000..54e0c092 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@2x.png new file mode 100644 index 00000000..338f3a16 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@3x.png new file mode 100644 index 00000000..ecdc6d83 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/flower_success/flower_success_mate_sunflower.imageset/flower_success_mate_sunflower@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/Contents.json new file mode 100644 index 00000000..57aa0552 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon_title_arrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_title_arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_title_arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow.png new file mode 100644 index 00000000..fef5e47e Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@2x.png new file mode 100644 index 00000000..a37cb892 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@3x.png new file mode 100644 index 00000000..17e0274c Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_title_arrow.imageset/icon_title_arrow@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/Contents.json new file mode 100644 index 00000000..3278d658 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "icon_edit.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/icon_edit.pdf b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/icon_edit.pdf new file mode 100644 index 00000000..d57a8155 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/myinfo/icon_edit.imageset/icon_edit.pdf differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/Contents.json new file mode 100644 index 00000000..0cca70e2 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "bubble_challenge_fail.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bubble_challenge_fail@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "bubble_challenge_fail@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail.png new file mode 100644 index 00000000..a1890c01 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@2x.png new file mode 100644 index 00000000..a91fc533 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@3x.png new file mode 100644 index 00000000..7b083e4a Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/bubble_challenge_fail.imageset/bubble_challenge_fail@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/Contents.json new file mode 100644 index 00000000..a402aa36 --- /dev/null +++ b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon_bubble_flowerLanguage.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_bubble_flowerLanguage@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_bubble_flowerLanguage@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage.png new file mode 100644 index 00000000..ed1938f3 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@2x.png new file mode 100644 index 00000000..92d3a496 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@2x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@3x.png new file mode 100644 index 00000000..3f9b71a6 Binary files /dev/null and b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_flowerLanguage.imageset/icon_bubble_flowerLanguage@3x.png differ diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/Contents.json similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/Contents.json rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/Contents.json diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate@2x 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate@2x 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate@2x 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate@2x 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate@3x 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate@3x 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_not_mate.imageset/icon_bubble_not_mate@3x 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_not_mate.imageset/icon_bubble_not_mate@3x 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/Contents.json similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/Contents.json rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/Contents.json diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail@2x.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail@2x.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail@2x.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail@3x.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_my.imageset/img_yellow_tail@3x.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_my.imageset/img_yellow_tail@3x.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/Contents.json similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/Contents.json rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/Contents.json diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail@2x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail@2x.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail@2x.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail@2x.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail@3x.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail@3x.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_tail_partner.imageset/img_pink_tail@3x.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_tail_partner.imageset/img_pink_tail@3x.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/Contents.json b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/Contents.json similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/Contents.json rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/Contents.json diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write@2x 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write@2x 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write@2x 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write@2x 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write@3x 1.png b/Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write@3x 1.png similarity index 100% rename from Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/main_icon/icon_bubble_write.imageset/icon_bubble_write@3x 1.png rename to Core/DesignSystem/Sources/DesignSystem/Resources/Assets.xcassets/speechBubble/icon_bubble_write.imageset/icon_bubble_write@3x 1.png diff --git a/Core/DesignSystem/Sources/DesignSystem/Sources/Assets/Assets.swift b/Core/DesignSystem/Sources/DesignSystem/Sources/Assets/Assets.swift index ed4ef06d..3a6acb0d 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Sources/Assets/Assets.swift +++ b/Core/DesignSystem/Sources/DesignSystem/Sources/Assets/Assets.swift @@ -119,6 +119,7 @@ public enum Assets: String { case icon_buds case icon_bubble_tail_my case icon_bubble_tail_partner + case icon_bubble_flowerLanguage case icon_check case icon_cryingseed case icon_blossome @@ -132,6 +133,12 @@ public enum Assets: String { case history_certificate case history_line case history_waiting + case icon_edit + case img_buds + case bubble_challenge_fail + case flowerPopup_background + case icon_challenge_progress + case icon_title_arrow public var image: UIImage { return .init(named: self.rawValue, in: Bundle.module, with: nil)! diff --git a/Core/DesignSystem/Sources/DesignSystem/Sources/Component/NavigationBar/TTNavigationDetailBar.swift b/Core/DesignSystem/Sources/DesignSystem/Sources/Component/NavigationBar/TTNavigationDetailBar.swift index 1d76fbdf..0dd4dfc6 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Sources/Component/NavigationBar/TTNavigationDetailBar.swift +++ b/Core/DesignSystem/Sources/DesignSystem/Sources/Component/NavigationBar/TTNavigationDetailBar.swift @@ -49,37 +49,29 @@ public final class TTNavigationDetailBar: UIView { // MARK: - Init - override public init(frame: CGRect) { + override init(frame: CGRect) { super.init(frame: frame) self.layout() } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + /// - Parameters: /// - title: 타이틀 설정 /// - leftButtonImage: 좌측 버튼 이미지 설정 /// - rightButtonImage: 우측 버튼 이미지 설정 - /// 사용 예시 - /// ```swift - /// TTNavigationDetailBar(title: "Hello", - /// leftButtonImage: .asset(.icon_more), - /// rightButtonImage: nil) - /// ``` - public convenience init(title: String?, - leftButtonImage: UIImage?, - rightButtonImage: UIImage?) { - self.init() + public func configure(title: String?, + leftButtonImage: UIImage?, + rightButtonImage: UIImage?) { self.titleLabel.text = title self.leftButton.setImage(leftButtonImage, for: .normal) self.rightButton.setImage(rightButtonImage , for: .normal) - - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") } // MARK: - Layout - + private func layout() { [self.titleLabel, self.leftButton, self.rightButton].forEach { self.addSubview($0) diff --git a/Core/DesignSystem/Sources/DesignSystem/Sources/Component/TTPopup.swift b/Core/DesignSystem/Sources/DesignSystem/Sources/Component/TTPopup.swift index bfc544c6..8cbf871d 100644 --- a/Core/DesignSystem/Sources/DesignSystem/Sources/Component/TTPopup.swift +++ b/Core/DesignSystem/Sources/DesignSystem/Sources/Component/TTPopup.swift @@ -9,6 +9,8 @@ import UIKit import Util public final class TTPopup: UIView, UIComponentBased { + + private var buttonTitles: [String] = [] /// 팝업 표시시 딤 처리 lazy var dimView: UIView = { @@ -25,32 +27,12 @@ public final class TTPopup: UIView, UIComponentBased { return v }() - - lazy var stackView: UIStackView = { - let v = UIStackView() - v.axis = .vertical - v.alignment = .center - v.spacing = 37 - v.addArrangedSubviews(self.titleLabel, self.resultView, self.descriptionLabel, self.buttonStackView) - return v - }() - - /// 하단 버튼 2개를 묶어둔 StackView - lazy var buttonStackView: UIStackView = { - let v = UIStackView() - v.axis = .horizontal - v.addArrangedSubviews(self.leftButton, self.rightButton) - v.alignment = .center - v.spacing = 60 - - return v - }() lazy var titleLabel: UILabel = { let v = UILabel() v.font = .h2 v.textColor = .primary - + v.textAlignment = .center return v }() @@ -64,8 +46,27 @@ public final class TTPopup: UIView, UIComponentBased { let v = UILabel() v.font = .omyupretty(size: ._16) v.numberOfLines = 0 - v.textAlignment = .center v.textColor = .grey500 + v.isHidden = true + return v + }() + + lazy var waringLabel: UILabel = { + let v = UILabel() + v.font = .omyupretty(size: ._16) + v.numberOfLines = 0 + v.textColor = .red + v.isHidden = true + return v + }() + + lazy var descriptionWarningStackView: UIStackView = { + let v = UIStackView() + v.axis = .vertical + v.alignment = .center + v.spacing = 8 + v.addArrangedSubviews(self.descriptionLabel, + self.waringLabel) return v }() @@ -84,8 +85,27 @@ public final class TTPopup: UIView, UIComponentBased { return v }() - - private var buttonTitles: [String] = [] + + /// 하단 버튼 2개를 묶어둔 StackView + lazy var buttonStackView: UIStackView = { + let v = UIStackView() + v.axis = .horizontal + v.distribution = .fillEqually + v.addArrangedSubviews(self.leftButton, + self.rightButton) + return v + }() + + lazy var stackView: UIStackView = { + let v = UIStackView() + v.axis = .vertical + v.spacing = 37 + v.addArrangedSubviews(self.titleLabel, + self.resultView, + self.descriptionWarningStackView, + self.buttonStackView) + return v + }() override init(frame: CGRect) { super.init(frame: .zero) @@ -99,15 +119,29 @@ public final class TTPopup: UIView, UIComponentBased { public func configure(title: String, resultView: UIView, - description: String, + description: String = "", + warningText: String = "", buttonTitles: [String]) { self.titleLabel.text = title self.resultView.addSubview(resultView) resultView.snp.makeConstraints { make in make.centerX.centerY.equalToSuperview() } - self.descriptionLabel.text = description - self.descriptionLabel.setLineSpacing(8) + + if !description.isEmpty { + self.descriptionLabel.isHidden = false + self.descriptionLabel.text = description + self.descriptionLabel.setLineSpacing(8) + self.descriptionLabel.textAlignment = .center + } + + if !warningText.isEmpty { + self.waringLabel.isHidden = false + self.waringLabel.text = warningText + self.waringLabel.setLineSpacing(8) + self.waringLabel.textAlignment = .center + } + self.buttonTitles = buttonTitles self.configureButtons(titles: buttonTitles) } diff --git a/Core/Network/Sources/Network/NetworkConfiguration.swift b/Core/Network/Sources/Network/NetworkConfiguration.swift index da8d006f..8bd41b88 100644 --- a/Core/Network/Sources/Network/NetworkConfiguration.swift +++ b/Core/Network/Sources/Network/NetworkConfiguration.swift @@ -23,8 +23,10 @@ public struct NetworkConfiguration { "accept": "application/json", "Content-Type": "application/json" ], - baseURL: String = "http://43.202.68.239:3000", - maxWaitTime: Double = 15.0 + baseURL: String = "https://twotoo-node-zmtrd.run.goorm.site", // 개발 서버 +// baseURL: String = "https://imv0qb1bei.execute-api.ap-northeast-2.amazonaws.com", // 현 상용 서버 +// baseURL: String = "http://43.202.68.239:3000", // 구 상용 서버 + maxWaitTime: Double = 25.0 ) { self.headers = headers self.baseURL = baseURL diff --git a/Core/Network/Tests/NetworkTests/NetworkManagerSpec.swift b/Core/Network/Tests/NetworkTests/NetworkManagerSpec.swift index 8ff00524..66a6d329 100644 --- a/Core/Network/Tests/NetworkTests/NetworkManagerSpec.swift +++ b/Core/Network/Tests/NetworkTests/NetworkManagerSpec.swift @@ -228,12 +228,12 @@ class NetworkManagerTests: QuickSpec { } } - describe("네트워크 요청 결과가 15초 이상 반환되지 않는다.") { + describe("네트워크 요청 결과가 25초 이상 반환되지 않는다.") { beforeEach { stub(condition: isHost("api.example.com")) { _ in let stubData = "{\"success\": true}".data(using: .utf8) let response = HTTPStubsResponse(data: stubData!, statusCode: 200, headers: ["Content-Type": "application/json"]) - response.requestTime = 15.5 + response.requestTime = 25.5 return response } } diff --git a/Core/Worker/Sources/Worker/NetworkWorker/ChangeNicknameNetworkWorker.swift b/Core/Worker/Sources/Worker/NetworkWorker/ChangeNicknameNetworkWorker.swift new file mode 100644 index 00000000..7a4b2ffd --- /dev/null +++ b/Core/Worker/Sources/Worker/NetworkWorker/ChangeNicknameNetworkWorker.swift @@ -0,0 +1,31 @@ +// +// ChangeNicknameNetworkWorker.swift +// +// +// Created by Eddy on 2023/10/12. +// + +import Foundation +import Network + +public struct ChangeNicknameResponse: Decodable { + public var nickname: String +} + +public protocol ChangeNicknameNetworkWorkerProtocol { + func requestChangeNicknameInquiry(nickname: String) async throws -> ChangeNicknameResponse +} + +public final class ChangeNicknameNetworkWorker: ChangeNicknameNetworkWorkerProtocol { + public init() {} + + public func requestChangeNicknameInquiry(nickname: String) async throws -> ChangeNicknameResponse { + return try await NetworkManager.shared.request( + path: "/user/changeNickname", + method: .patch, + parameters: [ + "nickname": nickname + ] + ) + } +} diff --git a/Core/Worker/Sources/Worker/NetworkWorker/HistoryNetworkWorker.swift b/Core/Worker/Sources/Worker/NetworkWorker/HistoryNetworkWorker.swift index 6d40ba6e..268fa1a4 100644 --- a/Core/Worker/Sources/Worker/NetworkWorker/HistoryNetworkWorker.swift +++ b/Core/Worker/Sources/Worker/NetworkWorker/HistoryNetworkWorker.swift @@ -11,6 +11,7 @@ import Network // https://https://twotoo-node-zmtrd.run.goorm.site/challenge/histories public struct HistoryResponse: Decodable { + public var viewState: String public var challengeNo: Int public var name: String public var description: String @@ -46,13 +47,6 @@ public final class HistoryNetworkWorker: HistoryNetworkWorkerProtocol { public init() {} public func requestHistoryInquiry() async throws -> [HistoryResponse] { -// return [ -// .init(challengeNo: 0, name: "fdafdas", description: "fdafdasfas", startDate: "2023-08-01T03:23:27.788Z", endDate: "2023-08-24T03:23:27.788Z", user1CommitCnt: 14, user2CommitCnt: 20, user1Flower: .CAMELLIA, user2Flower: .CHRYSANTHEMUM), -// .init(challengeNo: 1, name: "fdafdas", description: "fdafdasfas", startDate: "2023-08-01T03:23:27.788Z", endDate: "2023-08-24T03:23:27.788Z", user1CommitCnt: 0, user2CommitCnt: 10, user1Flower: .DELPHINIUM, user2Flower: .FIG), -// .init(challengeNo: 2, name: "fdafdas", description: "fdafdasfas", startDate: "2023-08-01T03:23:27.788Z", endDate: "2023-08-24T03:23:27.788Z", user1CommitCnt: 3, user2CommitCnt: 22, user1Flower: .CAMELLIA, user2Flower: .DELPHINIUM), -// .init(challengeNo: 3, name: "fdafdas", description: "fdafdasfas", startDate: "2023-08-01T03:23:27.788Z", endDate: "2023-08-24T03:23:27.788Z", user1CommitCnt: 4, user2CommitCnt: 1, user1Flower: .COTTON, user2Flower: .ROSE) -// ] -// return try await NetworkManager.shared.request( path: "/challenge/histories", method: .get diff --git a/Core/Worker/Sources/Worker/NetworkWorker/SignOutNetworkWorker.swift b/Core/Worker/Sources/Worker/NetworkWorker/SignOutNetworkWorker.swift new file mode 100644 index 00000000..a21ccc42 --- /dev/null +++ b/Core/Worker/Sources/Worker/NetworkWorker/SignOutNetworkWorker.swift @@ -0,0 +1,31 @@ +// +// SignOutNetworkWorker.swift +// +// +// Created by 박건우 on 2023/10/02. +// + +import Foundation +import Network + +// https://twotoo-node-zmtrd.run.goorm.site/user/signOut + +public struct SignOutResponse: Decodable { + +} + +public protocol SignOutNetworkWorkerProtocol { + func requestSignOut() async throws -> SignOutResponse +} + +public final class SignOutNetworkWorker: SignOutNetworkWorkerProtocol { + + public init() {} + + public func requestSignOut() async throws -> SignOutResponse { + return try await NetworkManager.shared.request( + path: "/user/signOut/", + method: .delete + ) + } +} diff --git a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateInteractor.swift b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateInteractor.swift index 6e748f4f..ede5fa47 100644 --- a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateInteractor.swift +++ b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateInteractor.swift @@ -138,14 +138,8 @@ extension ChallengeCertificateInteractor { } func didCropImage(image: UIImage) async { - do { - try await self.worker.saveImage(image: image) - await self.updateCertificateImage(certificateImage: image) - await self.presenter.presentCertificateImage(image: image) - } - catch { - await self.presenter.presentPhotoSaveError(error: error) - } + await self.updateCertificateImage(certificateImage: image) + await self.presenter.presentCertificateImage(image: image) } } diff --git a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificatePresenter.swift b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificatePresenter.swift index 8e0adaf7..f7106cd7 100644 --- a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificatePresenter.swift +++ b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificatePresenter.swift @@ -22,8 +22,6 @@ protocol ChallengeCertificatePresentationLogic { func presentImagePicker() /// 이미지 크롭 화면을 보여준다. func presentImageCropView(with image: ChallengeCertificate.Model.Image) - /// 사진 저장 실패 오류를 보여준다. - func presentPhotoSaveError(error: Error) /// 인증 사진을 보여준다. func presentCertificateImage(image: ChallengeCertificate.Model.Image) /// 인증하기를 활성화하여 보여준다. @@ -87,10 +85,6 @@ extension ChallengeCertificatePresenter: ChallengeCertificatePresentationLogic { self.viewController?.displayImageCropView(viewModel: .init(image: image)) } - func presentPhotoSaveError(error: Error) { - self.viewController?.displayToast(viewModel: .init(message: "사진 저장에 실패하였습니다.")) - } - func presentCertificateImage(image: ChallengeCertificate.Model.Image) { self.viewController?.displayCommitPhoto(viewModel: .init(image: image)) } diff --git a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateWorker.swift b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateWorker.swift index dddcb29a..09f69f73 100644 --- a/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateWorker.swift +++ b/Scene/ChallengeCertificateScene/Sources/ChallengeCertificateScene/ChallengeCertificateWorker.swift @@ -79,7 +79,7 @@ final class ChallengeCertificateWorker: ChallengeCertificateWorkerProtocol { guard let challengeNo = Int(challengeID) else { throw NSError(domain: "not fount challenge", code: -1) } - guard let img = certificateImage.jpegData(compressionQuality: 1.0) else { + guard let img = self.compressImage(certificateImage) else { throw NSError(domain: "not fount img", code: -1) } let date = Date() @@ -94,4 +94,39 @@ final class ChallengeCertificateWorker: ChallengeCertificateWorkerProtocol { fileName: formattedDate ) } + + private func compressImage( + _ image: UIImage, + quality: CGFloat = 1, + maxWidth: CGFloat = 1920, + maxHeight: CGFloat = 1920 + ) -> Data? { + var actualHeight: CGFloat = image.size.height + var actualWidth: CGFloat = image.size.width + var imgRatio: CGFloat = actualWidth / actualHeight + let maxRatio: CGFloat = maxWidth / maxHeight + + if actualHeight > maxHeight || actualWidth > maxWidth { + if imgRatio < maxRatio { + imgRatio = maxHeight / actualHeight + actualWidth = imgRatio * actualWidth + actualHeight = maxHeight + } else if imgRatio > maxRatio { + imgRatio = maxWidth / actualWidth + actualHeight = imgRatio * actualHeight + actualWidth = maxWidth + } else { + actualHeight = maxHeight + actualWidth = maxWidth + } + } + + let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight) + UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0) + image.draw(in: rect) + let resizedImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return resizedImage?.jpegData(compressionQuality: quality) + } } diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmInteractor.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmInteractor.swift index 05b95d17..12a3437c 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmInteractor.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmInteractor.swift @@ -13,6 +13,18 @@ protocol ChallengeConfirmBusinessLogic { func didAppear() async /// 다음 버튼 클릭 func didTapNextButton() async + /// 옵션 버튼 클릭 + func didTapOptionButton() async + /// 옵션 팝업의 챌린지 그만두기 버튼 클릭 + func didTapOptionPopupQuitButton() async + /// 챌린지 그만두기 팝업의 취소 버튼 클릭 + func didTapQuitPopupCancelButton() async + /// 챌린지 그만두기 팝업의 배경 클릭 + func didTapQuitPopupBackground() async + /// 챌린지 그만두기 팝업의 그만두기 버튼 클릭 + func didTapQuitPopupQuitButton() async + /// 뒤로가기 버튼 클릭 + func didTapBackButton() async } protocol ChallengeConfirmDataStore: AnyObject { @@ -120,8 +132,43 @@ extension ChallengeConfirmInteractor { // MARK: - Application Business Logic -// MARK: UseCase () +// MARK: Feature (챌린지 그만두기) extension ChallengeConfirmInteractor { + func didTapOptionButton() async { + await self.presenter.presentOptionPopup() + } + + func didTapOptionPopupQuitButton() async { + await self.presenter.presentQuitPopup() + } + + func didTapQuitPopupCancelButton() async { + await self.presenter.dismissQuitPopup() + } + + func didTapQuitPopupBackground() async { + await self.presenter.dismissQuitPopup() + } + + func didTapQuitPopupQuitButton() async { + do { + try await self.worker.requestChallengeQuit(challengeID: self.challengeID ?? "") + await self.presenter.presentChallengeQuitSuccess() + await self.router.pop() + } + catch { + await self.presenter.presentChallengeQuitError(error: error) + } + } +} + +// MARK: Feature (뒤로가기) + +extension ChallengeConfirmInteractor { + + func didTapBackButton() async { + await self.router.pop() + } } diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmModels.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmModels.swift index 62f690e8..97b70488 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmModels.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmModels.swift @@ -43,5 +43,22 @@ enum ChallengeConfirm { /// 챌린지 규칙 var rule: String? } + /// 그만두기 팝업 + struct QuitPopup { + /// 타이틀 + var title: String + /// 그만두기 아이콘 + var iconImage: UIImage + /// 설명 + var description: String + /// 경고문구 + var warning: String + /// 하단의 왼쪽, 오른쪽 버튼 타이틀 + var buttonTitles: [String] + } + + struct Toast { + var message: String? + } } } diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmPresenter.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmPresenter.swift index dc9063b9..eaed16df 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmPresenter.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmPresenter.swift @@ -14,6 +14,16 @@ protocol ChallengeConfirmPresentationLogic { func presentChallengeConfirmView(status: ChallengeConfirm.Model.ConfirmStatus, model: ChallengeConfirm.Model.ChallengeInfo) /// 챌린지 확인 에러 화면을 보여준다. func presentChallengeConfirmViewError(error: Error) + /// 옵션 팝업을 보여준다. + func presentOptionPopup() + /// 챌린지 그만두기 팝업을 보여준다. + func presentQuitPopup() + /// 챌린지 그만두기 팝업을 제거한다. + func dismissQuitPopup() + /// 챌린지 그만두기 성공을 보여준다. + func presentChallengeQuitSuccess() + /// 챌린지 그만두기 오류를 보여준다. + func presentChallengeQuitError(error: Error) } final class ChallengeConfirmPresenter { @@ -42,4 +52,30 @@ extension ChallengeConfirmPresenter: ChallengeConfirmPresentationLogic { func presentChallengeConfirmViewError(error: Error) { } + + func presentOptionPopup() { + self.viewController?.displayOptionPopup(title: "챌린지 그만두기") + } + + func presentQuitPopup() { + let viewModel = ChallengeConfirm.ViewModel.QuitPopup(title: "챌린지 그만두기", + iconImage: .asset(.icon_delete)!, + description: "기존의 챌린지는 삭제 됩니다", + warning: "* (경고) 그만두기 시 양쪽 모두에게\n삭제 및 종료 됩니다!*", + buttonTitles: ["취소", "그만두기"]) + self.viewController?.displayQuitPopup(viewModel: viewModel) + } + + func dismissQuitPopup() { + self.viewController?.dismissQuitPopup() + } + + func presentChallengeQuitSuccess() { + self.viewController?.displayToast(message: "기존 챌린지를 삭제했어요. 새로운 챌린지를 도전하세요!") + } + + func presentChallengeQuitError(error: Error) { + self.viewController?.displayToast(message: error.localizedDescription) + } + } diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmSceneFactory.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmSceneFactory.swift index b9e85129..4d63f595 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmSceneFactory.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmSceneFactory.swift @@ -49,10 +49,13 @@ public final class ChallengeConfirmSceneFactory { public init() {} public func make(with configuration: ChallengeConfirmConfiguration) -> ChallengeConfirmScene { - + let challengeQuitNetworkWorker = ChallengeQuitNetworkWorker() + let presenter = ChallengeConfirmPresenter() let router = ChallengeConfirmRouter() - let worker = ChallengeConfirmWorker() + let worker = ChallengeConfirmWorker( + challengeQuitNetworkWorker: challengeQuitNetworkWorker + ) let interactor = ChallengeConfirmInteractor( presenter: presenter, router: router, diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmViewController.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmViewController.swift index 91b9ae06..c6a9dddf 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmViewController.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmViewController.swift @@ -13,6 +13,11 @@ protocol ChallengeConfirmDisplayLogic: AnyObject { func displayCreateView(info: ChallengeConfirm.ViewModel.ChallengeInfo) func displayConfirmView(info: ChallengeConfirm.ViewModel.ChallengeInfo) func displayAcceptView(info: ChallengeConfirm.ViewModel.ChallengeInfo) + + func displayOptionPopup(title: String) + func displayQuitPopup(viewModel: ChallengeConfirm.ViewModel.QuitPopup) + func dismissQuitPopup() + func displayToast(message: String) } final class ChallengeConfirmViewController: UIViewController { @@ -21,7 +26,10 @@ final class ChallengeConfirmViewController: UIViewController { // MARK: - UI private lazy var navigationbar: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "", leftButtonImage: .asset(.icon_back), rightButtonImage: nil) + let v = TTNavigationDetailBar() + v.configure(title: nil, + leftButtonImage: .asset(.icon_back), + rightButtonImage: nil) v.delegate = self return v }() @@ -117,6 +125,27 @@ final class ChallengeConfirmViewController: UIViewController { v.image = .asset(.icon_seed) return v }() + + lazy var popupView: TTPopup = { + let v = TTPopup() + v.isHidden = true + v.didTapLeftButton { + Task { [weak self] in + await self?.interactor.didTapQuitPopupCancelButton() + } + } + v.didTapRightButton { + Task { [weak self] in + await self?.interactor.didTapQuitPopupQuitButton() + } + } + v.didTapBackground { + Task { [weak self] in + await self?.interactor.didTapQuitPopupBackground() + } + } + return v + }() private lazy var nextButton: TTPrimaryButtonType = { let v = TTPrimaryButton.create(title: "다음", .large) @@ -149,6 +178,14 @@ final class ChallengeConfirmViewController: UIViewController { } } + @objc private func viewDidAppearWithModalDismissed() { + Task { + Loading.shared.showLoadingView() + await self.interactor.didAppear() + Loading.shared.stopLoadingView() + } + } + // MARK: - Layout private func setUI() { @@ -159,7 +196,11 @@ final class ChallengeConfirmViewController: UIViewController { self.challengeConfirmView.addSubviews(self.challenageTitleStackView, self.challengeRuleLabel) self.challengeTitleView.addSubviews(self.challengeAppLabel, self.challengeImage) self.challengeContentView.addSubviews(self.challengeConfirmView, self.challengeTitleView) - self.view.addSubviews(self.navigationbar, self.headerStackView, self.challengeContentView, self.nextButton) + self.view.addSubviews(self.navigationbar, + self.headerStackView, + self.challengeContentView, + self.nextButton, + self.popupView) self.headerStackView.setCustomSpacing(8, after: self.processLabel) self.headerStackView.setCustomSpacing(12, after: self.headerLabel) @@ -226,7 +267,13 @@ final class ChallengeConfirmViewController: UIViewController { self.nextButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) - make.bottom.equalToSuperview().offset(-54) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) + } + + self.popupView.snp.makeConstraints { make in + make.centerX.centerY.equalToSuperview() + make.width.equalTo(273) + make.height.equalTo(349) } } } @@ -235,12 +282,17 @@ final class ChallengeConfirmViewController: UIViewController { extension ChallengeConfirmViewController: TTNavigationDetailBarDelegate { func didTapDetailLeftButton() { - self.navigationController?.popViewController(animated: true) + Task { + await self.interactor.didTapBackButton() + } } - + func didTapDetailRightButton() { - + Task { + await self.interactor.didTapOptionButton() + } } + } // MARK: - Trigger by Parent Scene @@ -265,7 +317,9 @@ extension ChallengeConfirmViewController: ChallengeConfirmDisplayLogic { self.headerStackView.isHidden = true self.nextButton.isHidden = true - self.title = "챌린지 정보" + self.navigationbar.configure(title: "챌린지 정보", + leftButtonImage: .asset(.icon_back), + rightButtonImage: .asset(.icon_more)) } func displayAcceptView(info: ChallengeConfirm.ViewModel.ChallengeInfo) { @@ -276,4 +330,40 @@ extension ChallengeConfirmViewController: ChallengeConfirmDisplayLogic { self.title = "" self.processLabel.isHidden = true } + + // MARK: - Display Popup + func displayQuitPopup(viewModel: ChallengeConfirm.ViewModel.QuitPopup) { + self.popupView.configure(title: viewModel.title, + resultView: UIImageView(image: viewModel.iconImage), + description: viewModel.description, + warningText: viewModel.warning, + buttonTitles: viewModel.buttonTitles) + self.popupView.isHidden = false + } + + func displayOptionPopup(title: String) { + let alertVC = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let action = UIAlertAction(title: title, style: .destructive) { [weak self] _ in + Task { + await self?.interactor.didTapOptionPopupQuitButton() + } + } + let cancel = UIAlertAction(title: "취소", style: .cancel) { _ in + Task { + await self.interactor.didTapQuitPopupCancelButton() + } + } + alertVC.addAction(action) + alertVC.addAction(cancel) + self.present(alertVC, animated: true) + } + + func dismissQuitPopup() { + self.popupView.isHidden = true + } + + func displayToast(message: String) { + Toast.shared.makeToast(message) + } + } diff --git a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmWorker.swift b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmWorker.swift index 4ca81f27..f188977e 100644 --- a/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmWorker.swift +++ b/Scene/ChallengeConfirmScene/Sources/ChallengeConfirmScene/ChallengeConfirmWorker.swift @@ -8,7 +8,21 @@ import CoreKit -protocol ChallengeConfirmWorkerProtocol {} +protocol ChallengeConfirmWorkerProtocol { + /// 챌린지 그만두기 요청을 한다. + func requestChallengeQuit(challengeID: String) async throws +} final class ChallengeConfirmWorker: ChallengeConfirmWorkerProtocol { + var challengeQuitNetworkWorker: ChallengeQuitNetworkWorkerProtocol + + init( + challengeQuitNetworkWorker: ChallengeQuitNetworkWorkerProtocol + ) { + self.challengeQuitNetworkWorker = challengeQuitNetworkWorker + } + + func requestChallengeQuit(challengeID: String) async throws { + _ = try await self.challengeQuitNetworkWorker.requestChallengeQuit(challengeNo: Int(challengeID) ?? 0) + } } diff --git a/Scene/ChallengeCreateScene/Sources/ChallengeAdditionalInfoInputScene/ChallengeAdditionalInfoInputViewController.swift b/Scene/ChallengeCreateScene/Sources/ChallengeAdditionalInfoInputScene/ChallengeAdditionalInfoInputViewController.swift index e9bc478a..f35d74a5 100644 --- a/Scene/ChallengeCreateScene/Sources/ChallengeAdditionalInfoInputScene/ChallengeAdditionalInfoInputViewController.swift +++ b/Scene/ChallengeCreateScene/Sources/ChallengeAdditionalInfoInputScene/ChallengeAdditionalInfoInputViewController.swift @@ -17,7 +17,8 @@ final class ChallengeAdditionalInfoInputViewController: UIViewController { // MARK: - UI private lazy var navigationbar: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "", leftButtonImage: .asset(.icon_back), rightButtonImage: nil) + let v = TTNavigationDetailBar() + v.configure(title: nil, leftButtonImage: .asset(.icon_back), rightButtonImage: nil) v.delegate = self return v }() @@ -140,7 +141,7 @@ final class ChallengeAdditionalInfoInputViewController: UIViewController { self.nextButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) - make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-20) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } } } @@ -185,26 +186,18 @@ extension ChallengeAdditionalInfoInputViewController: ChallengeAdditionalInfoInp extension ChallengeAdditionalInfoInputViewController: KeyboardDelegate { func willShowKeyboard(keyboardFrame: CGRect, duration: Double) { - UIView.animate(withDuration: 0.3) { - self.challengeRuleTextView.snp.updateConstraints { make in - make.top.equalTo(self.headerStackView.snp.bottom).offset(20) - } - + UIView.animate(withDuration: duration) { self.nextButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(keyboardFrame.height - 20) + make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(keyboardFrame.height + 20) } self.view.layoutIfNeeded() } } func willHideKeyboard(duration: Double) { - UIView.animate(withDuration: 0.3) { - self.challengeRuleTextView.snp.updateConstraints { make in - make.top.equalTo(self.headerStackView.snp.bottom).offset(42) - } - + UIView.animate(withDuration: duration) { self.nextButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } self.view.layoutIfNeeded() } diff --git a/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputInteractor.swift b/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputInteractor.swift index 232f17f4..b5858568 100644 --- a/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputInteractor.swift +++ b/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputInteractor.swift @@ -94,6 +94,8 @@ extension ChallengeEssentialInfoInputInteractor { extension ChallengeEssentialInfoInputInteractor { func didLoad() async { + self.startDateDataSource = Date().dateToString(.yearMonthDay) + self.endDateDataSource = Date().addingTimeInterval((86400 * 22)).dateToString(.yearMonthDay) await self.presenter.presentEnabled(nextButton: .init(isEnabled: false)) } } diff --git a/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputViewController.swift b/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputViewController.swift index 4e615a8a..511d2b21 100644 --- a/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputViewController.swift +++ b/Scene/ChallengeCreateScene/Sources/ChallengeEssentialInfoInputScene/ChallengeEssentialInfoInputViewController.swift @@ -27,7 +27,8 @@ final class ChallengeEssentialInfoInputViewController: UIViewController { // MARK: - UI private lazy var navigationbar: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "", leftButtonImage: .asset(.icon_back), rightButtonImage: nil) + let v = TTNavigationDetailBar() + v.configure(title: nil, leftButtonImage: .asset(.icon_back), rightButtonImage: nil) v.delegate = self return v }() @@ -263,7 +264,7 @@ final class ChallengeEssentialInfoInputViewController: UIViewController { self.nextButton.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) - make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-20) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } } } @@ -318,24 +319,18 @@ extension ChallengeEssentialInfoInputViewController: ChallengeEssentialInfoInput extension ChallengeEssentialInfoInputViewController: KeyboardDelegate { func willShowKeyboard(keyboardFrame: CGRect, duration: Double) { - UIView.animate(withDuration: 0.3) { - self.startDateStackView.snp.updateConstraints { make in - make.top.equalTo(self.challengeRecommendButton.snp.bottom).offset(20) - } + UIView.animate(withDuration: duration) { self.nextButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(keyboardFrame.height - 20) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(keyboardFrame.height + 20) } self.view.layoutIfNeeded() } } func willHideKeyboard(duration: Double) { - UIView.animate(withDuration: 0.3) { - self.startDateStackView.snp.updateConstraints { make in - make.top.equalTo(self.challengeRecommendButton.snp.bottom).offset(43) - } + UIView.animate(withDuration: duration) { self.nextButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } self.view.layoutIfNeeded() } diff --git a/Scene/ChallengeHistoryDetailScene/Package.swift b/Scene/ChallengeHistoryDetailScene/Package.swift index 98e249ef..343f69ff 100644 --- a/Scene/ChallengeHistoryDetailScene/Package.swift +++ b/Scene/ChallengeHistoryDetailScene/Package.swift @@ -14,6 +14,10 @@ let package = Package( ], dependencies: [ .package(path: "./CoreKit"), + .package( + url: "https://github.com/suzuki-0000/SKPhotoBrowser", + .upToNextMajor(from: "7.0.0") + ), .package( url: "https://github.com/Quick/Nimble", .upToNextMajor(from: "12.0.0") @@ -27,7 +31,8 @@ let package = Package( .target( name: "ChallengeHistoryDetailScene", dependencies: [ - .product(name: "CoreKit", package: "CoreKit") + .product(name: "CoreKit", package: "CoreKit"), + .product(name: "SKPhotoBrowser", package: "SKPhotoBrowser") ], resources: [.process("Assets")] ), diff --git a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailInteractor.swift b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailInteractor.swift index a633148e..7096deaf 100644 --- a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailInteractor.swift +++ b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailInteractor.swift @@ -13,6 +13,8 @@ protocol ChallengeHistoryDetailBusinessLogic { func didLoad() async /// 닫기 버튼 클릭 func didTapCloseButton() async + /// 사진 클릭 + func didTapPhoto() async } protocol ChallengeHistoryDetailDataStore: AnyObject { @@ -64,6 +66,15 @@ extension ChallengeHistoryDetailInteractor { } +// MARK: - Feature (사진 상세) + +extension ChallengeHistoryDetailInteractor { + + func didTapPhoto() async { + await self.presenter.presentPhoto(imageUrl: self.detail.certificateImageUrl) + } +} + // MARK: Feature (닫기) extension ChallengeHistoryDetailInteractor { diff --git a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailModels.swift b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailModels.swift index 679f4762..0fb0d9e3 100644 --- a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailModels.swift +++ b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailModels.swift @@ -7,6 +7,7 @@ // import UIKit +import SKPhotoBrowser enum ChallengeHistoryDetail { @@ -60,5 +61,10 @@ enum ChallengeHistoryDetail { /// 칭찬 문구 var complimentComment: String? } + + /// 사진 + struct Photo { + var images: [SKPhoto] + } } } diff --git a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailPresenter.swift b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailPresenter.swift index 532f1742..ed7941ea 100644 --- a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailPresenter.swift +++ b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailPresenter.swift @@ -7,10 +7,12 @@ // import UIKit +import SKPhotoBrowser @MainActor protocol ChallengeHistoryDetailPresentationLogic { func presentChallengeDetail(detail: ChallengeHistoryDetail.Model.ChallengeDetail) + func presentPhoto(imageUrl: String) } @@ -29,6 +31,14 @@ extension ChallengeHistoryDetailPresenter: ChallengeHistoryDetailPresentationLog self.viewController?.displayCompliment(compliment: compliment) } + func presentPhoto(imageUrl: String) { + var images = [SKPhoto]() + let photo = SKPhoto.photoWithImageURL(imageUrl) + photo.shouldCachePhotoURLImage = false + images.append(photo) + self.viewController?.displayPhoto(photo: .init(images: images)) + } + // model -> viewModel private func map(_ model: ChallengeHistoryDetail.Model.ChallengeDetail) -> (ChallengeHistoryDetail.ViewModel.Challenge, diff --git a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailViewController.swift b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailViewController.swift index 19be8482..c24f3254 100644 --- a/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailViewController.swift +++ b/Scene/ChallengeHistoryDetailScene/Sources/ChallengeHistoryDetailScene/ChallengeHistoryDetailViewController.swift @@ -8,10 +8,12 @@ import CoreKit import UIKit +import SKPhotoBrowser protocol ChallengeHistoryDetailDisplayLogic: AnyObject { func displayCertification(certification: ChallengeHistoryDetail.ViewModel.Challenge) func displayCompliment(compliment: ChallengeHistoryDetail.ViewModel.Compliment) + func displayPhoto(photo: ChallengeHistoryDetail.ViewModel.Photo) } final class ChallengeHistoryDetailViewController: UIViewController { @@ -45,6 +47,12 @@ final class ChallengeHistoryDetailViewController: UIViewController { let v = UIImageView() v.layer.cornerRadius = 10 v.clipsToBounds = true + v.isUserInteractionEnabled = true + v.addTapAction { [weak self] in + Task { [weak self] in + await self?.interactor.didTapPhoto() + } + } return v }() /// 챌린지 이름 라벨 @@ -59,6 +67,7 @@ final class ChallengeHistoryDetailViewController: UIViewController { let v = UILabel() v.font = .h4 v.textColor = .primary + v.numberOfLines = 0 return v }() /// 인증 시간 라벨 @@ -245,5 +254,9 @@ extension ChallengeHistoryDetailViewController: ChallengeHistoryDetailDisplayLog } } - + func displayPhoto(photo: ChallengeHistoryDetail.ViewModel.Photo) { + let browser = SKPhotoBrowser(photos: photo.images) + browser.initializePageIndex(0) + self.present(browser, animated: true, completion: {}) + } } diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/Cell/CertificateTableViewCell.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/Cell/CertificateTableViewCell.swift index ef6a45ed..0c11b6d8 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/Cell/CertificateTableViewCell.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/Cell/CertificateTableViewCell.swift @@ -5,6 +5,7 @@ // Created by Julia on 2023/07/24. // +import Kingfisher import UIKit import Util @@ -96,13 +97,15 @@ final class CertificateTableViewCell: UITableViewCell { super.init(style: style, reuseIdentifier: reuseIdentifier) self.layout() self.attribute() - self.applyDimming() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + var myImageDownloadTask: DownloadTask? + var partnerImageDownloadTask: DownloadTask? + override func prepareForReuse() { super.prepareForReuse() @@ -110,24 +113,32 @@ final class CertificateTableViewCell: UITableViewCell { self.partnerImageView.image = nil self.myCertificateID = "" self.partnerCertificateID = "" + self.myImageDownloadTask?.cancel() + self.partnerImageDownloadTask?.cancel() self.willCertificate = false + self.myImageView.subviews.forEach { $0.removeFromSuperview() } + self.partnerImageView.subviews.forEach { $0.removeFromSuperview() } } func configure(viewModel: ChallengeHistory.ViewModel.CellInfo) { self.dateLabel.text = viewModel.dateText // 유저 인증 O if let myInfo = viewModel.my { - self.myImageView.kf.setImage(with: myInfo.photoURL) + self.myImageDownloadTask = self.myImageView.kf.setImage(with: myInfo.photoURL) self.myTimeLabel.text = myInfo.timeText self.myCertificateID = myInfo.certificateID + self.applyDimming(userImageView: self.myImageView, + isMyImageView: true) } else { // 유저 인증 X, 오늘인지 판단 self.myImageView.image = viewModel.isToday ? .asset(.history_certificate) : .asset(.history_fail) } // 파트너 인증 O if let partnerInfo = viewModel.partner { - self.partnerImageView.kf.setImage(with: partnerInfo.photoURL) + self.partnerImageDownloadTask = self.partnerImageView.kf.setImage(with: partnerInfo.photoURL) self.partnerTimeLabel.text = partnerInfo.timeText self.partnerCertificateID = partnerInfo.certificateID + self.applyDimming(userImageView: self.partnerImageView, + isMyImageView: false) } else { // 파트너 인증 X, 오늘인지 판단 self.partnerImageView.image = viewModel.isToday ? .asset(.history_waiting) : .asset(.history_fail) } @@ -146,22 +157,10 @@ final class CertificateTableViewCell: UITableViewCell { self.lineImageView, self.partnerImageView) - self.contentView.bringSubviewToFront(self.dateView) - self.myImageView.addSubview(self.myTimeLabel) - self.partnerImageView.addSubview(self.partnerTimeLabel) - self.dateLabel.snp.makeConstraints { make in make.centerX.centerY.equalToSuperview() } - self.myTimeLabel.snp.makeConstraints { make in - make.trailing.bottom.equalToSuperview().inset(12) - } - - self.partnerTimeLabel.snp.makeConstraints { make in - make.trailing.bottom.equalToSuperview().inset(12) - } - self.myImageView.snp.makeConstraints { make in make.top.equalToSuperview().offset(17) make.bottom.equalToSuperview().inset(17) @@ -193,12 +192,26 @@ final class CertificateTableViewCell: UITableViewCell { self.selectionStyle = .none } - func applyDimming() { - [self.myImageView, self.partnerImageView].forEach { image in - let dimLayer = CALayer() - dimLayer.backgroundColor = UIColor.black.withAlphaComponent(0.1).cgColor - dimLayer.frame = .init(x: 0, y: 0, width: 127, height: 127) - image.layer.addSublayer(dimLayer) + func applyDimming(userImageView: UIImageView, isMyImageView: Bool) { + let dimView = UIView() + dimView.backgroundColor = UIColor.black.withAlphaComponent(0.1) + userImageView.addSubview(dimView) + + dimView.snp.makeConstraints { make in + make.edges.equalToSuperview() } + + if isMyImageView { + dimView.addSubview(self.myTimeLabel) + self.myTimeLabel.snp.makeConstraints { make in + make.trailing.bottom.equalToSuperview().inset(12) + } + } else { + dimView.addSubview(self.partnerTimeLabel) + self.partnerTimeLabel.snp.makeConstraints { make in + make.trailing.bottom.equalToSuperview().inset(12) + } + } + } } diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryModels.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryModels.swift index 1b96b3ec..98c89bde 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryModels.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryModels.swift @@ -30,6 +30,8 @@ enum ChallengeHistory { var myInfo: User /// 상대방 정보 var partnerInfo: User + /// 완료 여부 + var isFinished: Bool } /// 유저 @@ -122,6 +124,8 @@ enum ChallengeHistory { var iconImage: UIImage /// 설명 var description: String + /// 경고 문구 + var warningText: String /// 하단의 왼쪽, 오른쪽 버튼 타이틀 var buttonTitles: [String] } diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryPresenter.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryPresenter.swift index a9415703..d1b0f0cc 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryPresenter.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryPresenter.swift @@ -27,7 +27,8 @@ protocol ChallengeHistoryPresentationLogic { final class ChallengeHistoryPresenter { weak var viewController: ChallengeHistoryDisplayLogic? - + /// 챌린지가 완료된 상태인지 체크 + private var isCompleted: Bool = false } // MARK: - Presentation Logic @@ -40,14 +41,27 @@ extension ChallengeHistoryPresenter: ChallengeHistoryPresentationLogic { } func presentOptionPopup() { - self.viewController?.displayOptionPopup(title: "챌린지 그만두기") + let title = self.isCompleted ? "챌린지 삭제하기" : "챌린지 그만두기" + self.viewController?.displayOptionPopup(title: title) } func presentQuitPopup() { - let viewModel = ChallengeHistory.ViewModel.QuitPopup(title: "챌린지 그만두기", + var title: String = "챌린지 그만두기" + var description: String = "기존의 챌린지는 삭제 됩니다." + var warningText: String = "*(경고) 그만두기 시 양쪽 모두에게\n삭제 및 종료 됩니다!" + var buttonTitles: [String] = ["취소", "그만두기"] + if self.isCompleted { + title = "챌린지 삭제하기" + description = "완료한 챌린지는 삭제됩니다." + warningText = "*(경고) 삭제하기 시 양쪽 모두에게 삭제됩니다!*" + buttonTitles = ["취소", "삭제하기"] + } + + let viewModel = ChallengeHistory.ViewModel.QuitPopup(title: title, iconImage: .asset(.icon_delete)!, - description: "기존의 챌린지는 삭제 됩니다\n*(경고) 그만두기 시 양쪽 모두에게\n삭제 및 종료 됩니다!*", - buttonTitles: ["취소", "그만두기"]) + description: description, + warningText: warningText, + buttonTitles: buttonTitles) self.viewController?.displayQuitPopup(viewModel: viewModel) } @@ -56,7 +70,8 @@ extension ChallengeHistoryPresenter: ChallengeHistoryPresentationLogic { } func presentChallengeQuitSuccess() { - self.viewController?.displayToast(message: "기존 챌린지를 삭제했어요. 새로운 챌린지를 도전하세요!") + let message = self.isCompleted ? "완료된 챌린지를 삭제했어요." : "기존 챌린지를 삭제했어요. 새로운 챌린지를 도전하세요!" + self.viewController?.displayToast(message: message) } func presentChallengeQuitError(error: Error) { @@ -65,13 +80,15 @@ extension ChallengeHistoryPresenter: ChallengeHistoryPresentationLogic { } extension ChallengeHistoryPresenter { + /// Model -> ViewModel private func map(model: ChallengeHistory.Model.Challenge) -> ChallengeHistory.ViewModel.Challenge { return .init(id: model.id, name: model.name, dDayText: self.makedDayText(start: Date(), - end: model.endDate), + end: model.endDate, + isFinished: model.isFinished), additionalInfo: model.additionalInfo, myNickname: model.myInfo.nickname, partnerNickname: model.partnerInfo.nickname, @@ -152,12 +169,15 @@ extension ChallengeHistoryPresenter { } /// 시작일부터 종료일까지 디데이 계산 - private func makedDayText(start: Date, end: Date) -> String { - if let diffDay = Calendar.current.dateComponents([.day], from: start, to: end).day, diffDay > 0 { - return "D-\(diffDay)" - } else { + private func makedDayText(start: Date, end: Date, isFinished: Bool) -> String { + if isFinished { + self.isCompleted = true return "완료" } + if let diffDay = Calendar.current.dateComponents([.day], from: start, to: end).day { + return "D-\(diffDay)" + } + return "" } /// 시작일부터 종료일까지 날짜 배열 리턴, 최신순 정렬 diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryRouter.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryRouter.swift index ca329ca2..01220453 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryRouter.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryRouter.swift @@ -35,9 +35,6 @@ extension ChallengeHistoryRouter: ChallengeHistoryRoutingLogic { nickname: String, partnerNickname: String) { - guard let dataStore = self.dataStore else { - return - } let fac = ChallengeHistoryDetailSceneFactory().make(with: .init(detail: .init(id: certificate.id, challengeName: title, certificateImageUrl: certificate.certificateImageUrl, diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryViewController.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryViewController.swift index 889662a1..f86759e7 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryViewController.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryViewController.swift @@ -34,9 +34,10 @@ final class ChallengeHistoryViewController: UIViewController, UITableViewDataSou // MARK: - UI private lazy var navigationBar: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: nil, - leftButtonImage: .asset(.icon_back), - rightButtonImage: .asset(.icon_more)) + let v = TTNavigationDetailBar() + v.configure(title: "", + leftButtonImage: .asset(.icon_back), + rightButtonImage: .asset(.icon_more)) v.delegate = self return v }() @@ -284,6 +285,7 @@ extension ChallengeHistoryViewController: ChallengeHistoryDisplayLogic { self.popupView.configure(title: viewModel.title, resultView: UIImageView(image: viewModel.iconImage), description: viewModel.description, + warningText: viewModel.warningText, buttonTitles: viewModel.buttonTitles) self.popupView.isHidden = false } @@ -299,18 +301,11 @@ extension ChallengeHistoryViewController: ChallengeHistoryDisplayLogic { self.partnerNicknameTagView.titleLabel.text = viewModel.partnerNickname self.certificateList = viewModel.cellInfo self.certificateTableView.reloadData() - - if viewModel.dDayText == "완료" { - self.navigationBar.setIsHiddenRightButton(true) - } - else { - self.navigationBar.setIsHiddenRightButton(false) - } } func displayOptionPopup(title: String) { let alertVC = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - let action = UIAlertAction(title: title, style: .default) { [weak self] _ in + let action = UIAlertAction(title: title, style: .destructive) { [weak self] _ in Task { await self?.interactor.didTapOptionPopupQuitButton() } @@ -347,5 +342,4 @@ extension ChallengeHistoryViewController: TTNavigationDetailBarDelegate { await self.interactor.didTapOptionButton() } } - } diff --git a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryWorker.swift b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryWorker.swift index c0a22005..80b6835f 100644 --- a/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryWorker.swift +++ b/Scene/ChallengeHistoryScene/Sources/ChallengeHistoryScene/ChallengeHistoryWorker.swift @@ -80,7 +80,8 @@ final class ChallengeHistoryWorker: ChallengeHistoryWorkerProtocol { startDate: startDate, endDate: endDate, myInfo: myInfo, - partnerInfo: partnerInfo + partnerInfo: partnerInfo, + isFinished: challengeDetailResponse.isFinished ) } diff --git a/Scene/FlowerSelectScene/Sources/FlowerSelectScene/FlowerSelectViewController.swift b/Scene/FlowerSelectScene/Sources/FlowerSelectScene/FlowerSelectViewController.swift index 9428c87c..d5240e09 100644 --- a/Scene/FlowerSelectScene/Sources/FlowerSelectScene/FlowerSelectViewController.swift +++ b/Scene/FlowerSelectScene/Sources/FlowerSelectScene/FlowerSelectViewController.swift @@ -35,7 +35,10 @@ final class FlowerSelectViewController: UIViewController, TTNavigationDetailBarD // MARK: - UI private lazy var navigationbar: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "", leftButtonImage: .asset(.icon_back), rightButtonImage: nil) + let v = TTNavigationDetailBar() + v.configure(title: "", + leftButtonImage: .asset(.icon_back), + rightButtonImage: nil) v.delegate = self return v }() @@ -173,7 +176,7 @@ final class FlowerSelectViewController: UIViewController, TTNavigationDetailBarD } self.challengeButton.snp.makeConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-54) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) } diff --git a/Scene/HistoryScene/Sources/HistoryScene/Cell/HistoryCollectionViewCell.swift b/Scene/HistoryScene/Sources/HistoryScene/Cell/HistoryCollectionViewCell.swift index 5d2845e3..bbe6c2ba 100644 --- a/Scene/HistoryScene/Sources/HistoryScene/Cell/HistoryCollectionViewCell.swift +++ b/Scene/HistoryScene/Sources/HistoryScene/Cell/HistoryCollectionViewCell.swift @@ -33,11 +33,13 @@ public final class HistoryCollectionViewCell: UICollectionViewCell { private lazy var myFlowerImageView: UIImageView = { let v = UIImageView() + v.contentMode = .scaleAspectFit return v }() private lazy var partnerFlowerImageView: UIImageView = { let v = UIImageView() + v.contentMode = .scaleAspectFit return v }() @@ -78,7 +80,7 @@ public final class HistoryCollectionViewCell: UICollectionViewCell { } self.dateLabel.snp.makeConstraints { make in - make.top.equalTo(self.titleLabel.snp.bottom).offset(16) + make.top.equalTo(self.titleLabel.snp.bottom).offset(7) make.leading.equalToSuperview().offset(12) make.trailing.equalToSuperview().inset(12) } @@ -95,11 +97,21 @@ public final class HistoryCollectionViewCell: UICollectionViewCell { } func configure(viewModel: History.ViewModel.CellInfo) { - self.orderLabel.text = viewModel.orderText self.titleLabel.text = viewModel.nameText self.dateLabel.text = viewModel.dateText self.myFlowerImageView.image = viewModel.myFlowerImage self.partnerFlowerImageView.image = viewModel.partnerFlowerImage + + if viewModel.isFinished { + self.partnerFlowerImageView.isHidden = false + self.orderLabel.text = "\(viewModel.order)번째 챌린지" + self.orderLabel.textColor = .mainCoral + } + else { + self.partnerFlowerImageView.isHidden = true + self.orderLabel.text = "\(viewModel.order)번째 챌린지 중" + self.myFlowerImageView.image = .asset(.icon_challenge_progress) + self.orderLabel.textColor = .mainPink + } } - } diff --git a/Scene/HistoryScene/Sources/HistoryScene/HistoryModels.swift b/Scene/HistoryScene/Sources/HistoryScene/HistoryModels.swift index 8766e05f..1908c622 100644 --- a/Scene/HistoryScene/Sources/HistoryScene/HistoryModels.swift +++ b/Scene/HistoryScene/Sources/HistoryScene/HistoryModels.swift @@ -19,6 +19,8 @@ enum History { /// 챌린지 struct Challenge { + // 챌린지 진행 상태 + var viewState: String /// 챌린지 ID var id: String /// 챌린지 순서 - n번째 챌린지 @@ -47,8 +49,10 @@ enum History { /// 히스토리 셀 정보 struct CellInfo { - /// 챌린지 순서 텍스트 - var orderText: String + /// 챌린지 진행 상태 + var isFinished: Bool + /// 챌린지 순서 + var order: Int /// 챌린지 이름 텍스트 var nameText: String /// 챌린지 날짜 정보 텍스트 diff --git a/Scene/HistoryScene/Sources/HistoryScene/HistoryPresenter.swift b/Scene/HistoryScene/Sources/HistoryScene/HistoryPresenter.swift index b8c734ee..267a8723 100644 --- a/Scene/HistoryScene/Sources/HistoryScene/HistoryPresenter.swift +++ b/Scene/HistoryScene/Sources/HistoryScene/HistoryPresenter.swift @@ -51,14 +51,26 @@ private extension HistoryPresenter { let end: String = $0.endDate.dateToString(.shortYearMonthDay) let dateText: String = start + " ~ " + end var partnerFlower: UIImage? + var order = 0 if let partnerInfoFlower = $0.partnerFlower { partnerFlower = FlowerMappingWorker(flowerType: partnerInfoFlower).getSmallImage() } + else { + partnerFlower = .asset(.img_buds) + } var myFlower: UIImage? if let myInfoFlower = $0.myFlower { myFlower = FlowerMappingWorker(flowerType: myInfoFlower).getSmallImage() } - let cellInfo = History.ViewModel.CellInfo(orderText: "\($0.order)번째 챌린지", + else { + myFlower = .asset(.img_buds) + } + + order = $0.order + + let cellInfo = History.ViewModel.CellInfo( + isFinished: $0.viewState == "Finished", + order: order, nameText: $0.name, dateText: dateText, partnerFlowerImage: partnerFlower, diff --git a/Scene/HistoryScene/Sources/HistoryScene/HistoryViewController.swift b/Scene/HistoryScene/Sources/HistoryScene/HistoryViewController.swift index 0cbb31a7..446dc711 100644 --- a/Scene/HistoryScene/Sources/HistoryScene/HistoryViewController.swift +++ b/Scene/HistoryScene/Sources/HistoryScene/HistoryViewController.swift @@ -56,8 +56,8 @@ final class HistoryViewController: UIViewController, TTNavigationBarDelegate, UI lazy var historyEmptyLabel: UILabel = { let v = UILabel() v.textColor = .grey500 - v.font = .h3 - v.text = "챌린지를 완료해\n히스토리를 만들어보세요 :)" + v.font = .body1 + v.text = "진행중인 챌린지가 없어요\n챌린지를 만들어보세요 :)" v.setLineSpacing(10) v.textAlignment = .center v.numberOfLines = 0 diff --git a/Scene/HistoryScene/Sources/HistoryScene/HistoryWorker.swift b/Scene/HistoryScene/Sources/HistoryScene/HistoryWorker.swift index 282872f8..0a37593b 100644 --- a/Scene/HistoryScene/Sources/HistoryScene/HistoryWorker.swift +++ b/Scene/HistoryScene/Sources/HistoryScene/HistoryWorker.swift @@ -31,6 +31,7 @@ final class HistoryWorker: HistoryWorkerProtocol { if historyResponse.first?.user1No == self.meLocalWorker.userNo { return historyResponse.enumerated().map({ index, history in return .init( + viewState: history.viewState, id: String(history.challengeNo), order: index + 1, name: history.name, @@ -44,6 +45,7 @@ final class HistoryWorker: HistoryWorkerProtocol { else { return historyResponse.enumerated().map({ index, history in return .init( + viewState: history.viewState, id: String(history.challengeNo), order: index + 1, name: history.name, diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents 2.json b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents 2.json new file mode 100644 index 00000000..1125a2b2 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents 2.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "lottie_arrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "lottie_arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "lottie_arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents.json b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents.json new file mode 100644 index 00000000..1125a2b2 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "lottie_arrow.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "lottie_arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "lottie_arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow.png new file mode 100644 index 00000000..69853caa Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@2x.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@2x.png new file mode 100644 index 00000000..0d5608eb Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@2x.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@3x.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@3x.png new file mode 100644 index 00000000..ba8ac30e Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_arrow.imageset/lottie_arrow@3x.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents 2.json b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents 2.json new file mode 100644 index 00000000..82a23b7a --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents 2.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon_lottie_background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_lottie_background@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_lottie_background@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents.json b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents.json new file mode 100644 index 00000000..82a23b7a --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icon_lottie_background.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icon_lottie_background@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icon_lottie_background@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background.png new file mode 100644 index 00000000..12b11bab Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@2x.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@2x.png new file mode 100644 index 00000000..8d1796a6 Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@2x.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@3x.png b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@3x.png new file mode 100644 index 00000000..e3dd539b Binary files /dev/null and b/Scene/HomeScene/Sources/HomeScene/Assets/Assets.xcassets/lottie_background.imageset/icon_lottie_background@3x.png differ diff --git a/Scene/HomeScene/Sources/HomeScene/HomeModels.swift b/Scene/HomeScene/Sources/HomeScene/HomeModels.swift index 5908366c..3844d566 100644 --- a/Scene/HomeScene/Sources/HomeScene/HomeModels.swift +++ b/Scene/HomeScene/Sources/HomeScene/HomeModels.swift @@ -222,32 +222,61 @@ enum Home { struct PartnerFlowerViewModel { /// 이미지 var image: UIImage - /// 인증 완료 히든 여부 - var isCertificationCompleteHidden: Bool - /// 칭찬 문구 히든 여부 - var isComplimentCommentHidden: Bool - /// 칭찬 문구 텍스트 - var complimentCommentText: String + /// 꽃 상단 정보 + var topViewModel: TopViewModel /// 상대방 이름 텍스트 var partnerNameText: String + + struct TopViewModel { + /// 인증 완료 히든 여부 + var isCertificationCompleteHidden: Bool + /// 칭찬 문구 히든 여부 + var isComplimentCommentHidden: Bool + /// 칭찬 문구 텍스트 + var complimentCommentText: String + } } /// 내 꽃 struct MyFlowerViewModel { /// 이미지 var image: UIImage - /// 인증 버튼 히든 여부 - var isCertificationButtonHidden: Bool - /// 인증 안내 텍스트 - var cetificationGuideText: String - /// 칭찬 문구 히든 여부 - var isComplimentCommentHidden: Bool - /// 칭찬 문구 텍스트 - var complimentCommentText: String + /// 꽃 상단 정보 + var topViewModel: TopViewModel /// 내 이름 텍스트 var myNameText: String + + struct TopViewModel { + /// 인증 유도 텍스트 히든 여부 + var isHiddenCetificationGuideText: Bool + /// 인증 버튼 히든 여부 + var isCertificationButtonHidden: Bool + /// 인증 안내 텍스트 + var cetificationGuideText: String + /// 칭찬 문구 히든 여부 + var isComplimentCommentHidden: Bool + /// 칭찬 문구 텍스트 + var complimentCommentText: String + } } + + /// 둘다 인증 팝업 + struct BothCertificationPopupViewModel { + var show: (UIImage)? + var dismiss: ()? + + /// 타이틀 + static let title: String = "모두 인증 완료" + /// 메세지 + static let message: String = "서로 인증을 완료했어요!\n 짝꿍에게 응원 한마디를 남겨요" + /// 아니요 + static let noOptionText: String = "괜찮아요" + /// 네 옵션 + static let yesOptionText: String = "칭찬하기" + } + } + /// 챌린지 완료 struct ChallengeCompletedViewModel { @@ -298,55 +327,54 @@ enum Home { struct PartnerFlowerViewModel { /// 이미지 var image: UIImage - /// 꽃 이름, 꽃 말 히든 여부 - var isFlowerTextHidden: Bool - /// 꽃 이름 텍스트 - var flowerNameText: String - /// 꽃 말 텍스트 - var flowerDescText: String /// 상대방 이름 텍스트 var partnerNameText: String + /// 꽃말 보기 히든 여부 + var isFlowerLanguageBubbleHidden: Bool + /// 상대방 꽃말 팝업 + var flowerLanguagePopup: FlowerLanguageViewModel? + } /// 내 꽃 struct MyFlowerViewModel { /// 이미지 var image: UIImage - /// 꽃 이름, 꽃 말 히든 여부 - var isFlowerTextHidden: Bool + /// 내 이름 텍스트 + var myNameText: String + /// 꽃말 보기 히든 여부 + var isFlowerLanguageBubbleHidden: Bool + /// 내 꽃말 팝업 + var flowerLanguagePopup: FlowerLanguageViewModel? + } + + /// 완료 팝업 + struct CompletedPopupViewModel { + var show: (title: String, message: String, image: UIImage)? + var dismiss: ()? + + /// 옵션 + static let optionText: String = "확인" + } + + struct FlowerLanguageViewModel { /// 꽃 이름 텍스트 var flowerNameText: String /// 꽃 말 텍스트 var flowerDescText: String - /// 내 이름 텍스트 - var myNameText: String + /// 꽃 이미지 + var flowerImage: UIImage + /// 몇번째 꽃 텍스트 + var flowerOrderText: String } - } - - /// 둘다 인증 팝업 - struct BothCertificationViewModel { - var show: (UIImage)? - var dismiss: ()? - /// 타이틀 - static let title: String = "모두 인증 완료" - /// 메세지 - static let message: String = "서로 인증을 완료했어요!\n 짝꿍에게 응원 한마디를 남겨요" - /// 아니요 - static let noOptionText: String = "괜찮아요" - /// 네 옵션 - static let yesOptionText: String = "칭찬하기" - } - - /// 완료 팝업 - struct CompletedViewModel { - var show: (title: String, message: String, image: UIImage)? - var dismiss: ()? - - /// 옵션 - static let optionText: String = "확인" + /// 꽃말 팝업 + struct FlowerLanguagePopupViewModel { + var show: (FlowerLanguageViewModel)? + var dismiss: ()? + } } - + struct Toast { var message: String? } diff --git a/Scene/HomeScene/Sources/HomeScene/HomePresenter.swift b/Scene/HomeScene/Sources/HomeScene/HomePresenter.swift index e489a38f..70605499 100644 --- a/Scene/HomeScene/Sources/HomeScene/HomePresenter.swift +++ b/Scene/HomeScene/Sources/HomeScene/HomePresenter.swift @@ -162,12 +162,14 @@ extension Home.Model.Challenge { ), order: .init(challengeOrderText: "", partenrNameText: "", myNameText: ""), partnerFlower: .init( - image: UIImage(), isCertificationCompleteHidden: false, - isComplimentCommentHidden: false, complimentCommentText: "", partnerNameText: "" + image: UIImage(), + topViewModel: .init(isCertificationCompleteHidden: false, isComplimentCommentHidden: false, complimentCommentText: ""), + partnerNameText: "" ), myFlower: .init( - image: UIImage(), isCertificationButtonHidden: false, - cetificationGuideText: "", isComplimentCommentHidden: false, complimentCommentText: "", myNameText: ""), + image: UIImage(), + topViewModel: .init(isHiddenCetificationGuideText: false, isCertificationButtonHidden: false, cetificationGuideText: "", isComplimentCommentHidden: false, complimentCommentText: ""), + myNameText: ""), isHeartHidden: false, stickText: "" ) @@ -195,7 +197,7 @@ extension Home.Model.Challenge { partnerFlowerMapper = FlowerMappingWorker(flowerType: partnerFlower) } viewModel.partnerFlower.image = partnerFlowerMapper?.getMateImageByStep(growStatus: self.partnerInfo.growStatus ?? .seed) ?? UIImage() - viewModel.partnerFlower.complimentCommentText = self.myInfo.todayCert?.complimentComment ?? "" + viewModel.partnerFlower.topViewModel.complimentCommentText = self.myInfo.todayCert?.complimentComment ?? "" viewModel.partnerFlower.partnerNameText = self.partnerInfo.nickname // 내 꽃 매핑 @@ -204,44 +206,45 @@ extension Home.Model.Challenge { myFlowerMapper = FlowerMappingWorker(flowerType: myFlower) } viewModel.myFlower.image = myFlowerMapper?.getMyImageByStep(growStatus: self.myInfo.growStatus ?? .seed) ?? UIImage() - viewModel.myFlower.cetificationGuideText = "내 씨앗을 눌러 인증 해보세요!" - viewModel.myFlower.complimentCommentText = self.partnerInfo.todayCert?.complimentComment ?? "" + viewModel.myFlower.topViewModel.isHiddenCetificationGuideText = !(self.myInfo.growStatus == .seed) + viewModel.myFlower.topViewModel.cetificationGuideText = "씨앗을 눌러 인증 해보세요!" + viewModel.myFlower.topViewModel.complimentCommentText = self.partnerInfo.todayCert?.complimentComment ?? "" viewModel.myFlower.myNameText = self.myInfo.nickname // 챌린지 진행 상태 매핑 switch self.status { - case .inProgress(let inProgressStatus): - switch inProgressStatus { - case .bothUncertificated: - viewModel.myFlower.isCertificationButtonHidden = false - viewModel.partnerFlower.isCertificationCompleteHidden = true - viewModel.myFlower.isComplimentCommentHidden = true - viewModel.partnerFlower.isComplimentCommentHidden = true - viewModel.isHeartHidden = true - - case .onlyPartnerCertificated: - viewModel.myFlower.isCertificationButtonHidden = false - viewModel.partnerFlower.isCertificationCompleteHidden = false - viewModel.myFlower.isComplimentCommentHidden = true - viewModel.partnerFlower.isComplimentCommentHidden = true - viewModel.isHeartHidden = true - - case .onlyMeCertificated: - viewModel.myFlower.isCertificationButtonHidden = true - viewModel.partnerFlower.isCertificationCompleteHidden = true - viewModel.myFlower.isComplimentCommentHidden = true - viewModel.partnerFlower.isComplimentCommentHidden = true - viewModel.isHeartHidden = true - - case .bothCertificated(_): - viewModel.myFlower.isCertificationButtonHidden = true - viewModel.partnerFlower.isCertificationCompleteHidden = true - viewModel.myFlower.isComplimentCommentHidden = false - viewModel.partnerFlower.isComplimentCommentHidden = false - viewModel.isHeartHidden = false - } - default: - break + case .inProgress(let inProgressStatus): + switch inProgressStatus { + case .bothUncertificated: + viewModel.myFlower.topViewModel.isCertificationButtonHidden = false + viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden = true + viewModel.myFlower.topViewModel.isComplimentCommentHidden = true + viewModel.partnerFlower.topViewModel.isComplimentCommentHidden = true + viewModel.isHeartHidden = true + + case .onlyPartnerCertificated: + viewModel.myFlower.topViewModel.isCertificationButtonHidden = false + viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden = false + viewModel.myFlower.topViewModel.isComplimentCommentHidden = true + viewModel.partnerFlower.topViewModel.isComplimentCommentHidden = true + viewModel.isHeartHidden = true + + case .onlyMeCertificated: + viewModel.myFlower.topViewModel.isCertificationButtonHidden = true + viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden = true + viewModel.myFlower.topViewModel.isComplimentCommentHidden = true + viewModel.partnerFlower.topViewModel.isComplimentCommentHidden = true + viewModel.isHeartHidden = true + + case .bothCertificated(_): + viewModel.myFlower.topViewModel.isCertificationButtonHidden = true + viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden = true + viewModel.myFlower.topViewModel.isComplimentCommentHidden = false + viewModel.partnerFlower.topViewModel.isComplimentCommentHidden = false + viewModel.isHeartHidden = false + } + default: + break } // 찌르기 텍스트 @@ -259,14 +262,8 @@ extension Home.Model.Challenge { partnerPercentageNumber: 0, myPercentageNumber: 0 ), order: .init(challengeOrderText: "", partenrNameText: "", myNameText: ""), - partnerFlower: .init( - image: UIImage(), isFlowerTextHidden: false, - flowerNameText: "", flowerDescText: "", partnerNameText: "" - ), - myFlower: .init( - image: UIImage(), isFlowerTextHidden: false, - flowerNameText: "", flowerDescText: "", myNameText: "" - ) + partnerFlower: .init(image: UIImage(), partnerNameText: "", isFlowerLanguageBubbleHidden: false), + myFlower: .init(image: UIImage(), myNameText: "", isFlowerLanguageBubbleHidden: false) ) // 챌린지 정보 매핑 @@ -291,9 +288,8 @@ extension Home.Model.Challenge { partnerFlowerMapper = FlowerMappingWorker(flowerType: partnerFlower) } viewModel.partnerFlower.image = partnerFlowerMapper?.getMateImageByStep(growStatus: self.partnerInfo.growStatus ?? .seed) ?? UIImage() - viewModel.partnerFlower.flowerNameText = partnerFlowerMapper?.getName() ?? "" - viewModel.partnerFlower.flowerDescText = partnerFlowerMapper?.getDesc() ?? "" - viewModel.partnerFlower.isFlowerTextHidden = !(self.partnerInfo.growStatus == .flower || self.partnerInfo.growStatus == .bloom) + let showPartnerFlowerLanguagePopup = (self.partnerInfo.growStatus == .flower || self.partnerInfo.growStatus == .bloom) + viewModel.partnerFlower.isFlowerLanguageBubbleHidden = !showPartnerFlowerLanguagePopup viewModel.partnerFlower.partnerNameText = self.partnerInfo.nickname // 내 꽃 매핑 @@ -302,15 +298,28 @@ extension Home.Model.Challenge { myFlowerMapper = FlowerMappingWorker(flowerType: myFlower) } viewModel.myFlower.image = myFlowerMapper?.getMyImageByStep(growStatus: self.myInfo.growStatus ?? .seed) ?? UIImage() - viewModel.myFlower.flowerNameText = myFlowerMapper?.getName() ?? "" - viewModel.myFlower.flowerDescText = myFlowerMapper?.getDesc() ?? "" - viewModel.myFlower.isFlowerTextHidden = !(self.myInfo.growStatus == .flower || self.myInfo.growStatus == .bloom) + let showMyFlowerLanguagePopup = (self.myInfo.growStatus == .flower || self.myInfo.growStatus == .bloom) + viewModel.myFlower.isFlowerLanguageBubbleHidden = !showMyFlowerLanguagePopup viewModel.myFlower.myNameText = self.myInfo.nickname + // 파트너 꽃말 팝업 매핑 + if showPartnerFlowerLanguagePopup { + viewModel.partnerFlower.flowerLanguagePopup = .init(flowerNameText: partnerFlowerMapper?.getName() ?? "", + flowerDescText: partnerFlowerMapper?.getDesc() ?? "", + flowerImage: partnerFlowerMapper?.getMateImageByStep(growStatus: self.partnerInfo.growStatus ?? .seed) ?? UIImage(), + flowerOrderText: self.calculateFlowerOrderText(order: self.order)) + } + // 내 꽃말 팝업 매핑 + if showMyFlowerLanguagePopup { + viewModel.myFlower.flowerLanguagePopup = .init(flowerNameText: myFlowerMapper?.getName() ?? "", + flowerDescText: myFlowerMapper?.getDesc() ?? "", + flowerImage: myFlowerMapper?.getMateImageByStep(growStatus: self.partnerInfo.growStatus ?? .seed) ?? UIImage(), + flowerOrderText: self.calculateFlowerOrderText(order: self.order)) + } return viewModel } - func toCompletedViewModel() -> Home.ViewModel.CompletedViewModel { + func toCompletedViewModel() -> Home.ViewModel.ChallengeCompletedViewModel.CompletedPopupViewModel { var title: String var message: String var image: UIImage @@ -385,4 +394,12 @@ private extension Home.Model.Challenge { return "" } } + + func calculateFlowerOrderText(order: Int?) -> String { + if let order = order { + return "\(order)번째 챌린지 꽃" + } else { + return "" + } + } } diff --git a/Scene/HomeScene/Sources/HomeScene/HomeViewController.swift b/Scene/HomeScene/Sources/HomeScene/HomeViewController.swift index f2bcc789..97b2f04e 100644 --- a/Scene/HomeScene/Sources/HomeScene/HomeViewController.swift +++ b/Scene/HomeScene/Sources/HomeScene/HomeViewController.swift @@ -17,8 +17,8 @@ protocol HomeDisplayLogic: AnyObject { func displayChallengeAfterStartDateViewModel(viewModel: Home.ViewModel.ChallengeAfterStartDateViewModel) func displayChallengeInProgressViewModel(viewModel: Home.ViewModel.ChallengeInProgressViewModel) func displayChallengeCompletedViewModel(viewModel: Home.ViewModel.ChallengeCompletedViewModel) - func displayBothCertificationViewModel(viewModel: Home.ViewModel.BothCertificationViewModel) - func displayCompletedViewModel(viewModel: Home.ViewModel.CompletedViewModel) + func displayBothCertificationViewModel(viewModel: Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel) + func displayCompletedViewModel(viewModel: Home.ViewModel.ChallengeCompletedViewModel.CompletedPopupViewModel) func displayToast(viewModel: Home.ViewModel.Toast) } @@ -34,8 +34,12 @@ final class HomeViewController: UIViewController { fatalError("init(coder:) has not been implemented") } - // MARK: - UI + // MARK: - Popup + var bothCertificationPopupView: TTPopup? + var completedPopupView: TTPopup? + var flowerLanguagePopupView: TTFlowerPopup? + // MARK: - UI Component /// 네비게이션 바 lazy var navigationBar: TTNavigationBar = { let v = TTNavigationBar(title: "TwoToo", @@ -93,10 +97,6 @@ final class HomeViewController: UIViewController { return v }() - var bothCertificationPopupView: TTPopup? - - var completedPopupView: TTPopup? - lazy var groundImageView: UIImageView = { let v = UIImageView(.home_ground) return v @@ -134,6 +134,14 @@ final class HomeViewController: UIViewController { } } + @objc private func appDidBecomeActive() { + Task { + Loading.shared.showLoadingView() + await self.interactor.didAppear() + Loading.shared.stopLoadingView() + } + } + private func registNotification() { NotificationCenter.default.addObserver( self, @@ -141,6 +149,12 @@ final class HomeViewController: UIViewController { name: NSNotification.Name("modal_dismissed"), object: nil ) + NotificationCenter.default.addObserver( + self, + selector: #selector(self.appDidBecomeActive), + name: UIApplication.didBecomeActiveNotification, + object: nil + ) } // MARK: - Layout @@ -166,10 +180,11 @@ final class HomeViewController: UIViewController { } let tabBarHeight: CGFloat = UIDevice.current.safeAreaBottomHeight + 61 + let groundHeight = UIDevice.current.deviceType == .default ? 140 : 212 self.groundImageView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() - make.height.equalToSuperview().dividedBy(3.5) + make.height.equalTo(groundHeight) make.bottom.equalToSuperview().inset(tabBarHeight) } @@ -272,7 +287,7 @@ extension HomeViewController: HomeDisplayLogic { } } - func displayBothCertificationViewModel(viewModel: Home.ViewModel.BothCertificationViewModel) { + func displayBothCertificationViewModel(viewModel: Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel) { self.displayWithAnimation { [weak self] in viewModel.show.unwrap { let popupContentView = UIView() @@ -285,12 +300,12 @@ extension HomeViewController: HomeDisplayLogic { } let popupView = TTPopup() - popupView.configure(title: Home.ViewModel.BothCertificationViewModel.title, + popupView.configure(title: Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel.title, resultView: popupContentView, - description: Home.ViewModel.BothCertificationViewModel.message, + description: Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel.message, buttonTitles: [ - Home.ViewModel.BothCertificationViewModel.noOptionText, - Home.ViewModel.BothCertificationViewModel.yesOptionText + Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel.noOptionText, + Home.ViewModel.ChallengeInProgressViewModel.BothCertificationPopupViewModel.yesOptionText ]) popupView.didTapLeftButton { @@ -325,7 +340,7 @@ extension HomeViewController: HomeDisplayLogic { } } - func displayCompletedViewModel(viewModel: Home.ViewModel.CompletedViewModel) { + func displayCompletedViewModel(viewModel: Home.ViewModel.ChallengeCompletedViewModel.CompletedPopupViewModel) { self.displayWithAnimation { [weak self] in viewModel.show.unwrap { let popupContentView = UIView() @@ -341,7 +356,7 @@ extension HomeViewController: HomeDisplayLogic { popupView.configure(title: $0.title, resultView: popupContentView, description: $0.message, - buttonTitles: [Home.ViewModel.CompletedViewModel.optionText]) + buttonTitles: [Home.ViewModel.ChallengeCompletedViewModel.CompletedPopupViewModel.optionText]) popupView.didTapLeftButton { Task { @@ -469,8 +484,40 @@ extension HomeViewController: ChallengeInProgressViewDelegate{ await self.interactor.didTapStickButton() } } + } -extension HomeViewController: ChallengeCompletedViewDelegate{ +extension HomeViewController: ChallengeCompletedViewDelegate, TTFlowerPopupDelegate { + + func didTapShowFlowerLaunage(viewModel: Home.ViewModel.ChallengeCompletedViewModel.FlowerLanguagePopupViewModel) { + self.displayWithAnimation { [weak self] in + viewModel.show.unwrap { + let popupView = TTFlowerPopup() + popupView.configure(name: $0.flowerNameText, + description: $0.flowerDescText, + image: $0.flowerImage, + order: $0.flowerOrderText) + + self?.flowerLanguagePopupView = popupView + self?.flowerLanguagePopupView?.delegate = self + + if let flowerLanguagePopupView = self?.flowerLanguagePopupView { + self?.view.addSubview(flowerLanguagePopupView) + } + } + + viewModel.dismiss.unwrap { + self?.flowerLanguagePopupView?.removeFromSuperview() + self?.flowerLanguagePopupView = nil + } + } + } + + // TTFlowerPopupDelegate + func didTapCloseView() { + self.flowerLanguagePopupView?.removeFromSuperview() + self.flowerLanguagePopupView = nil + } + func didTapChallengeCompletedFinishButton() { Task { await self.interactor.didTapChallengeCompleteButton() diff --git a/Scene/HomeScene/Sources/HomeScene/Views/ChallengeCompletedView.swift b/Scene/HomeScene/Sources/HomeScene/Views/ChallengeCompletedView.swift index d72dead4..e3adcbae 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/ChallengeCompletedView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/ChallengeCompletedView.swift @@ -9,7 +9,12 @@ import UIKit import DesignSystem protocol ChallengeCompletedViewDelegate: AnyObject { + /// 챌린지 정보를 탭 했을 때 + func didTapChallengeInfo() + /// 챌린지 완료하기 버튼 탭 했을 때 func didTapChallengeCompletedFinishButton() + /// 내 꽃말 보기 이미지 뷰를 탭했을 때 + func didTapShowFlowerLaunage(viewModel: Home.ViewModel.ChallengeCompletedViewModel.FlowerLanguagePopupViewModel) } /// 챌린지 완료 후 보여질 화면입니다. @@ -20,6 +25,9 @@ final class ChallengeCompletedView: UIView { /// 닉네임 정보, 챌린지 정보를 담은 스택뷰 lazy var topChallengeInfoView: TopChallengeInfoView = { let v = TopChallengeInfoView() + v.addTapAction { [weak self] in + self?.delegate?.didTapChallengeInfo() + } return v }() /// 챌린지 진행도 뷰 @@ -32,11 +40,23 @@ final class ChallengeCompletedView: UIView { let v = TrailingInfoStackView() return v }() + /// 내 꽃 상위 컴포넌트 + lazy var myFlowerTopView: MyFlowerTopView = { + let v = MyFlowerTopView() + v.inCompletedDelegate = self + return v + }() /// 내 꽃 정보 뷰 lazy var myFlowerView: MyFlowerView = { let v = MyFlowerView() return v }() + /// 상대방 꽃 상위 컴포넌트 + lazy var partnerFlowerTopView: PartnerFlowerTopView = { + let v = PartnerFlowerTopView() + v.inCompletedDelegate = self + return v + }() /// 상대방 꽃 정보 뷰 lazy var partnerFlowerView: PartnerFlowerView = { let v = PartnerFlowerView() @@ -68,6 +88,8 @@ final class ChallengeCompletedView: UIView { self.nicknameStackView, self.myFlowerView, self.partnerFlowerView, + self.myFlowerTopView, + self.partnerFlowerTopView, self.confirmButton) self.topChallengeInfoView.snp.makeConstraints { make in @@ -90,34 +112,75 @@ final class ChallengeCompletedView: UIView { make.trailing.equalToSuperview().inset(24) } + let flowerBottomOffset = UIDevice.current.deviceType == .default ? -12 : 33 + + // --> PartnerFlower + self.partnerFlowerTopView.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(self.progressBar.snp.bottom).offset(2) + make.width.equalToSuperview().dividedBy(2) + make.leading.equalToSuperview() +// make.centerX.equalToSuperview().multipliedBy(0.58) + } + self.partnerFlowerView.snp.makeConstraints { make in - make.centerX.equalToSuperview().multipliedBy(0.5) - make.bottom.equalToSuperview().multipliedBy(0.75) + make.top.equalTo(self.partnerFlowerTopView.snp.bottom).offset(5) make.width.equalToSuperview().dividedBy(2) + make.leading.equalToSuperview() + make.bottom.equalTo(self.confirmButton.snp.top).offset(-flowerBottomOffset) } - + + // --> MyFlower + self.myFlowerTopView.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(self.progressBar.snp.bottom).offset(2) + make.width.equalToSuperview().dividedBy(2) + make.trailing.equalToSuperview() +// make.centerX.equalToSuperview().multipliedBy(1.42) // 진행중도 테스트 해봐야 함 + } + self.myFlowerView.snp.makeConstraints { make in - make.bottom.equalToSuperview().multipliedBy(0.75) - make.centerX.equalToSuperview().multipliedBy(1.5) + make.top.equalTo(self.myFlowerTopView.snp.bottom).offset(5) make.width.equalToSuperview().dividedBy(2) + make.trailing.equalToSuperview() + make.bottom.equalTo(self.confirmButton.snp.top).offset(-flowerBottomOffset) } self.confirmButton.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.bottom.equalToSuperview().inset(50) + make.bottom.equalToSuperview().inset(49) make.width.equalTo(177) } - } + private var myPopupViewModel: Home.ViewModel.ChallengeCompletedViewModel.FlowerLanguageViewModel? + private var partnerPopupViewModel: Home.ViewModel.ChallengeCompletedViewModel.FlowerLanguageViewModel? + func configure(viewModel: Home.ViewModel.ChallengeCompletedViewModel) { self.topChallengeInfoView.configureCompleted(viewModel: viewModel.challengeInfo) self.progressBar.configureCompleted(viewModel: viewModel.progress) self.nicknameStackView.configure(challengeOrderText: viewModel.order.challengeOrderText, myNickname: viewModel.order.myNameText, partnerNickname: viewModel.order.partenrNameText) - self.partnerFlowerView.configureCompleted(viewModel: viewModel.partnerFlower) self.myFlowerView.configureCompleted(viewModel: viewModel.myFlower) + self.myFlowerTopView.configureCompleted(isHidden: viewModel.myFlower.isFlowerLanguageBubbleHidden) + self.myPopupViewModel = viewModel.myFlower.flowerLanguagePopup + self.partnerFlowerView.configureCompleted(viewModel: viewModel.partnerFlower) + self.partnerFlowerTopView.configureCompleted(isHidden: viewModel.partnerFlower.isFlowerLanguageBubbleHidden) + self.partnerPopupViewModel = viewModel.partnerFlower.flowerLanguagePopup + } + +} + +// MARK: - ChallengeCompletedViewDelegate +extension ChallengeCompletedView: MyFlowerTopInCompletedDelegate, PartnerFlowerTopInCompletedDelegate { + func didTapShowMyFlowerLanguage() { + if let viewModel = myPopupViewModel { + self.delegate?.didTapShowFlowerLaunage(viewModel: .init(show: viewModel, dismiss: nil)) + } } + func didTapShowPartnerFlowerLanguage() { + if let viewModel = partnerPopupViewModel { + self.delegate?.didTapShowFlowerLaunage(viewModel: .init(show: viewModel, dismiss: nil)) + } + } } diff --git a/Scene/HomeScene/Sources/HomeScene/Views/ChallengeInProgressView.swift b/Scene/HomeScene/Sources/HomeScene/Views/ChallengeInProgressView.swift index 19138292..08238010 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/ChallengeInProgressView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/ChallengeInProgressView.swift @@ -38,12 +38,23 @@ final class ChallengeInProgressView: UIView { return v }() + /// 내 꽃 상위 컴포넌트 + lazy var myFlowerTopView: MyFlowerTopView = { + let v = MyFlowerTopView() + v.inProgressDelegate = self + return v + }() /// 내 꽃 정보 뷰 lazy var myFlowerView: MyFlowerView = { let v = MyFlowerView() v.delegate = self return v }() + /// 상대방 꽃 상위 컴포넌트 + lazy var partnerFlowerTopView: PartnerFlowerTopView = { + let v = PartnerFlowerTopView() + return v + }() /// 상대방 꽃 정보 뷰 lazy var partnerFlowerView: PartnerFlowerView = { let v = PartnerFlowerView() @@ -87,7 +98,9 @@ final class ChallengeInProgressView: UIView { self.addSubviews(self.topChallengeInfoView, self.progressBar, self.nicknameStackView, + self.partnerFlowerTopView, self.partnerFlowerView, + self.myFlowerTopView, self.myFlowerView, self.heartImage, self.nudgeBeeButton, @@ -113,32 +126,50 @@ final class ChallengeInProgressView: UIView { make.trailing.equalToSuperview().inset(24) } + let flowerBottomOffset = UIDevice.current.deviceType == .default ? -10 : 22 + // --> PartnerFlower + self.partnerFlowerTopView.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(self.progressBar.snp.bottom).offset(2) + make.centerX.equalToSuperview().multipliedBy(0.58) + } + self.partnerFlowerView.snp.makeConstraints { make in - make.bottom.equalToSuperview().multipliedBy(0.75) - make.centerX.equalToSuperview().multipliedBy(0.5) + make.top.equalTo(self.partnerFlowerTopView.snp.bottom).offset(5) make.width.equalToSuperview().dividedBy(2) + make.leading.equalToSuperview() + make.bottom.equalTo(self.nudgeBeeButton.snp.top).offset(-flowerBottomOffset) + } + + // --> MyFlower + self.myFlowerTopView.snp.makeConstraints { make in + make.top.greaterThanOrEqualTo(self.progressBar.snp.bottom).offset(2) + make.centerX.equalToSuperview().multipliedBy(1.42) } self.myFlowerView.snp.makeConstraints { make in - make.bottom.equalToSuperview().multipliedBy(0.75) - make.centerX.equalToSuperview().multipliedBy(1.5) + make.top.equalTo(self.myFlowerTopView.snp.bottom).offset(5) make.width.equalToSuperview().dividedBy(2) + make.trailing.equalToSuperview() + make.bottom.equalTo(self.nudgeBeeButton.snp.top).offset(-flowerBottomOffset) } self.heartImage.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.centerY.equalTo(self.myFlowerView.snp.centerY) + make.centerY.equalTo(self.myFlowerView.snp.centerY).offset(-10) } + let beeButtonWidthHeight = UIDevice.current.deviceType == .default ? 50 : 57 + let beeButtonBottomOffset = UIDevice.current.deviceType == .default ? 10 : 38 + self.nudgeBeeButton.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.width.height.equalTo(57) + make.width.height.equalTo(beeButtonWidthHeight) make.bottom.equalTo(self.nudgeTitleLabel.snp.top).offset(-8) } self.nudgeTitleLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.bottom.equalToSuperview().inset(38) + make.bottom.equalToSuperview().offset(-beeButtonBottomOffset) } } @@ -150,14 +181,16 @@ final class ChallengeInProgressView: UIView { myNickname: viewModel.order.myNameText, partnerNickname: viewModel.order.partenrNameText) self.partnerFlowerView.configureInProgress(viewModel: viewModel.partnerFlower) + self.partnerFlowerTopView.configureInProgress(viewModel: viewModel.partnerFlower.topViewModel) self.myFlowerView.configureInProgress(viewModel: viewModel.myFlower) + self.myFlowerTopView.configureInProgress(viewModel: viewModel.myFlower.topViewModel) self.heartImage.isHidden = viewModel.isHeartHidden self.nudgeTitleLabel.text = viewModel.stickText } } -extension ChallengeInProgressView: MyFlowerViewDelegate { +extension ChallengeInProgressView: MyFlowerViewDelegate, MyFlowerTopInProgressDelegate { func didTapEmptySpeechBubbleView() { self.delegate?.didTapMyFlowerEmptySpeechBubbleView() } @@ -165,4 +198,8 @@ extension ChallengeInProgressView: MyFlowerViewDelegate { func didTapCertificateView() { self.delegate?.didTapCertificateButton() } + + func didTapWateringCanView() { + self.delegate?.didTapCertificateButton() + } } diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Common/TTFlowerPopup.swift b/Scene/HomeScene/Sources/HomeScene/Views/Common/TTFlowerPopup.swift new file mode 100644 index 00000000..32695f22 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Views/Common/TTFlowerPopup.swift @@ -0,0 +1,187 @@ +// +// TTFlowerPopup.swift +// +// +// Created by Julia on 10/10/23. +// + +import UIKit +import Util + +protocol TTFlowerPopupDelegate: AnyObject { + func didTapCloseView() +} + +final class TTFlowerPopup: UIView { + + weak var delegate: TTFlowerPopupDelegate? + + lazy var dimView: UIView = { + let v = UIView() + v.backgroundColor = .black.withAlphaComponent(0.5) + v.addTapAction { [weak self] in + self?.delegate?.didTapCloseView() + } + return v + }() + + let flowerNameLabel: UILabel = { + let v = UILabel() + v.textColor = .primary + v.font = .h2 + v.textAlignment = .center + return v + }() + + let flowerDescLabel: UILabel = { + let v = UILabel() + v.textColor = .primary + v.font = .body2 + v.textAlignment = .center + return v + }() + + lazy var flowerImageView: UIImageView = { + let v = UIImageView() + v.contentMode = .scaleAspectFit + return v + }() + + let flowerOrderLabel: PaddingLabel = { + let v = PaddingLabel(padding: .init(top: 4, left: 10, bottom: 4, right: 10)) + v.font = .body1 + v.textColor = .mainCoral + v.backgroundColor = .white + v.layer.cornerRadius = 10 + v.clipsToBounds = true + return v + }() + + lazy var closeButton: UIButton = { + let v = UIButton() + v.setImage(.asset(.icon_cancel), for: .normal) + v.addAction { [weak self] in + self?.delegate?.didTapCloseView() + } + return v + }() + + let backgroundImageView: UIImageView = { + let v = UIImageView(image: .asset(.flowerPopup_background)) + return v + }() + + /// 컴포넌트들을 담는 뷰 + lazy var contentView: UIView = { + let v = UIView() + v.backgroundColor = .second02 + v.addSubviews(self.flowerNameLabel, + self.flowerDescLabel, + self.flowerImageView, + self.flowerOrderLabel, + self.backgroundImageView, + self.closeButton) + v.sendSubviewToBack(self.backgroundImageView) + v.layer.cornerRadius = 20 + return v + }() + + override init(frame: CGRect) { + super.init(frame: .zero) + self.layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func didMoveToSuperview() { + super.didMoveToSuperview() + + if self.superview != nil { + self.snp.remakeConstraints { make in + make.edges.equalToSuperview() + } + } + } + + func configure(name flowerName: String, + description flowerDescription: String, + image: UIImage, + order flowerOrder: String) { + self.flowerNameLabel.text = flowerName + self.flowerDescLabel.text = flowerDescription + self.flowerImageView.image = image + self.flowerOrderLabel.text = flowerOrder + } + + func layout() { + self.addSubviews(self.dimView, + self.contentView) + + self.dimView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + self.contentView.snp.makeConstraints { make in + make.width.equalTo(273) + make.height.equalTo(349) + make.center.equalToSuperview() + } + + self.flowerNameLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(34) + make.centerX.equalToSuperview() + } + + self.flowerDescLabel.snp.makeConstraints { make in + make.top.equalTo(self.flowerNameLabel.snp.bottom).offset(8) + make.centerX.equalToSuperview() + } + + self.flowerImageView.snp.makeConstraints { make in + make.top.equalTo(self.flowerDescLabel.snp.bottom).offset(32) + make.centerX.equalToSuperview() + make.width.equalTo(100) + make.height.lessThanOrEqualTo(165) + } + + self.flowerOrderLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-30) + } + + self.backgroundImageView.snp.makeConstraints { make in + make.leading.trailing.bottom.equalToSuperview() + } + + self.closeButton.snp.makeConstraints { make in + make.top.equalToSuperview().offset(14) + make.trailing.equalToSuperview().inset(14) + } + } + +} + +extension TTFlowerPopup { + /// UILabel의 padding을 설정할 수 있는 클래스 + final class PaddingLabel: UILabel { + private var padding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0) + + convenience init(padding: UIEdgeInsets) { + self.init() + self.padding = padding + } + + override func drawText(in rect: CGRect) { + super.drawText(in: rect.inset(by: padding)) + } + + override var intrinsicContentSize: CGSize { + var contentSize = super.intrinsicContentSize + contentSize.height += padding.top + padding.bottom + contentSize.width += padding.left + padding.right + return contentSize + } + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Common/TTProgressBar.swift b/Scene/HomeScene/Sources/HomeScene/Views/Common/TTProgressBar.swift index dab69d89..b509a308 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Common/TTProgressBar.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Common/TTProgressBar.swift @@ -92,15 +92,18 @@ final class TTProgressBar: UIView { self.myNicknameLabel, self.myPercentContentView, self.myPercentLabel) + + let percentContentLeadingOffset = UIDevice.current.deviceType == .default ? 9 : 14 + // --> partner self.partnerNicknameLabel.snp.makeConstraints { make in make.centerX.equalToSuperview().multipliedBy(0.3) make.centerY.equalToSuperview().multipliedBy(0.6) } - + self.partnerPercentContentView.snp.makeConstraints { make in - make.width.equalTo(100) - make.trailing.equalToSuperview().inset(43) + make.leading.equalTo(self.partnerNicknameLabel.snp.trailing).offset(percentContentLeadingOffset) + make.trailing.equalTo(self.partnerPercentLabel.snp.leading).offset(-6) make.height.equalToSuperview().dividedBy(5.5) make.centerY.equalToSuperview().multipliedBy(0.6) } @@ -117,8 +120,8 @@ final class TTProgressBar: UIView { } self.myPercentContentView.snp.makeConstraints { make in - make.width.equalTo(100) - make.trailing.equalToSuperview().inset(43) + make.leading.equalTo(self.myNicknameLabel.snp.trailing).offset(percentContentLeadingOffset) + make.trailing.equalTo(self.myPercentLabel.snp.leading).offset(-6) make.height.equalToSuperview().dividedBy(5.5) make.centerY.equalToSuperview().multipliedBy(1.4) } diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Common/TopChallengeInfoView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Common/TopChallengeInfoView.swift index 60e63ed5..0a2a2259 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Common/TopChallengeInfoView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Common/TopChallengeInfoView.swift @@ -26,6 +26,11 @@ final class TopChallengeInfoView: UIView { return v }() + let arrowImageView: UIImageView = { + let v = UIImageView(image: .asset(.icon_title_arrow)) + return v + }() + override init(frame: CGRect) { super.init(frame: frame) self.layout() @@ -37,10 +42,14 @@ final class TopChallengeInfoView: UIView { } func layout() { - self.addSubviews(self.titleLabel, self.dateTagView) + self.addSubviews(self.titleLabel, + self.dateTagView, + self.arrowImageView) + + let titleTopBottomOffset = UIDevice.current.deviceType == .default ? 12 : 16 self.titleLabel.snp.makeConstraints { make in - make.top.equalToSuperview().offset(16) + make.top.equalToSuperview().offset(titleTopBottomOffset) make.centerX.equalToSuperview() make.leading.trailing.equalToSuperview().inset(20) } @@ -48,7 +57,13 @@ final class TopChallengeInfoView: UIView { self.dateTagView.snp.makeConstraints { make in make.top.equalTo(self.titleLabel.snp.bottom).offset(8) make.centerX.equalToSuperview() - make.bottom.equalToSuperview().offset(-16) + make.height.equalTo(23) + make.bottom.equalToSuperview().offset(-titleTopBottomOffset) + } + + self.arrowImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.trailing.equalToSuperview().inset(18) } } diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Common/TrailingInfoStackView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Common/TrailingInfoStackView.swift index 9d32e648..84869027 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Common/TrailingInfoStackView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Common/TrailingInfoStackView.swift @@ -70,7 +70,7 @@ final class TrailingInfoStackView: UIView { self.challengeCountLabel) self.heartImageView.snp.makeConstraints { make in - make.width.height.equalTo(12) + make.width.height.equalTo(14) } self.nicknameStackView.snp.makeConstraints { make in diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/InduceCertificationView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/InduceCertificationView.swift deleted file mode 100644 index a2064590..00000000 --- a/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/InduceCertificationView.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// induceCertificationView.swift -// -// -// Created by Julia on 2023/07/13. -// - -import UIKit -import Lottie - -final class InduceCertificationView: UIView { - - lazy var titleLabel: UILabel = { - let v = UILabel() - v.font = .body1 - v.textColor = .grey500 - return v - }() - - lazy var certificatedLottieView: LottieAnimationView = { - let v = LottieAnimationView(name: "watering_lottie", bundle: .module) - v.loopMode = .loop - v.animationSpeed = 0.5 - return v - }() - - override init(frame: CGRect) { - super.init(frame: frame) - self.layout() - } - - private func layout() { - self.addSubviews(self.titleLabel, - self.certificatedLottieView) - - self.certificatedLottieView.play() - - self.titleLabel.snp.makeConstraints { make in - make.top.centerX.equalToSuperview() - make.height.equalTo(16) - } - - self.certificatedLottieView.snp.makeConstraints { make in - make.top.equalTo(self.titleLabel.snp.bottom).offset(10) - make.width.equalTo(75) - make.height.equalTo(70) - make.centerX.bottom.equalToSuperview() - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/SpeechBubbleView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/SpeechBubbleView.swift index f30e3dd4..a947f22c 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/SpeechBubbleView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/SpeechBubbleView.swift @@ -26,6 +26,7 @@ final class SpeechBubbleView: UIView { let titleLabel: UILabel = { let v = UILabel() v.font = .body1 + v.textAlignment = .center v.textColor = .primary v.numberOfLines = 0 return v diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/WateringCanView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/WateringCanView.swift new file mode 100644 index 00000000..0a3debf8 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/Component/WateringCanView.swift @@ -0,0 +1,95 @@ +// +// WateringCanStackView.swift +// +// +// Created by Julia on 2023/07/13. +// + +import UIKit +import Lottie + +/// 물뿌리개 로티 말풍선 뷰 +final class WateringCanView: UIView { + + lazy var titleLabel: UILabel = { + let v = UILabel() + v.font = .body1 + v.textColor = .grey500 + v.textAlignment = .center + return v + }() + + lazy var certificatedLottieView: LottieAnimationView = { + let v = LottieAnimationView(name: "watering_lottie", bundle: .module) + v.loopMode = .loop + v.animationSpeed = 0.5 + return v + }() + + let wateringCanUnderArrowView: UIImageView = { + let v = UIImageView(image: UIImage(named: "lottie_arrow", + in: .module, + with: nil)) + return v + }() + + /// 물뿌리개 로티와 말풍선 꼬리 담은 뷰 + lazy var wateringCanView: UIView = { + let v = UIView() + v.backgroundColor = .second01 + v.layer.cornerRadius = 8 + v.addSubviews(self.certificatedLottieView, + self.wateringCanUnderArrowView) + return v + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.layout() + self.certificatedLottieView.play() + } + + private func layout() { + self.addSubviews(self.titleLabel, + self.wateringCanView) + + var wateringCanViewWidth = 75 + var wateringCanViewHeight = 55 + var wateringCanViewTopOffset = 10 + + if UIDevice.current.deviceType == .default { + wateringCanViewWidth = 65 + wateringCanViewHeight = 45 + wateringCanViewTopOffset = 5 + } + self.titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview() + make.leading.trailing.equalToSuperview() + make.bottom.equalTo(self.wateringCanView.snp.top).offset(-wateringCanViewTopOffset) + } + + self.certificatedLottieView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.center.equalToSuperview() + } + + self.wateringCanView.snp.makeConstraints { make in + make.width.equalTo(wateringCanViewWidth) + make.height.equalTo(wateringCanViewHeight) + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + } + + self.wateringCanUnderArrowView.snp.makeConstraints { make in + make.width.height.equalTo(24) + make.bottom.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + } + + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerTopView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerTopView.swift new file mode 100644 index 00000000..076a0029 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerTopView.swift @@ -0,0 +1,199 @@ +// +// MyFlowerTopView.swift +// +// +// Created by Julia on 2023/09/17. +// + +import UIKit +import DesignSystem + +protocol MyFlowerTopInProgressDelegate: AnyObject { + /// 물뿌리개를 탭 했을 때 - 인증 + func didTapWateringCanView() + /// 비어있는 말풍선을 탭 했을 때 + func didTapEmptySpeechBubbleView() +} + +protocol MyFlowerTopInCompletedDelegate: AnyObject { + /// 꽃말 보기 말풍선을 탭 했을 때 - 팝업 띄우기 + func didTapShowMyFlowerLanguage() +} + +final class MyFlowerTopView: UIView { + + weak var inProgressDelegate: MyFlowerTopInProgressDelegate? + weak var inCompletedDelegate: MyFlowerTopInCompletedDelegate? + + // MARK: - 챌린지 진행 중 + /// 칭찬시 나타나는 말풍선 + lazy var speechBubbleView: SpeechBubbleView = { + let v = SpeechBubbleView(tailPosition: .my) + v.isHidden = true + return v + }() + /// 물뿌리개 인증 로티 뷰 + lazy var wateringCanView: WateringCanView = { + let v = WateringCanView() + v.isHidden = true + v.addTapAction { [weak self] in + self?.inProgressDelegate?.didTapWateringCanView() + } + return v + }() + /// 인증 완료 후 칭찬 문구 작성하기 말풍선 뷰 + lazy var complimentWriteBubbleImageView: UIImageView = { + let v = UIImageView(.icon_bubble_write) + v.isHidden = true + v.isUserInteractionEnabled = true + v.addTapAction { [weak self] in + self?.inProgressDelegate?.didTapEmptySpeechBubbleView() + } + return v + }() + // MARK: - 챌린지 완료 + /// 꽃말 보기 말풍선 이미지 + lazy var showFlowerLanguageBubbleView: UIImageView = { + let v = UIImageView() + v.image = .asset(.icon_bubble_flowerLanguage) + v.isHidden = true + v.isUserInteractionEnabled = true + v.addTapAction { [weak self] in + self?.inCompletedDelegate?.didTapShowMyFlowerLanguage() + } + return v + }() + + /// 챌린지 실패시(꽃을 피우지 못 했을 때) 보여지는 말풍선 뷰 + lazy var challengeFailBubbleView: UIImageView = { + let v = UIImageView() + v.image = .asset(.bubble_challenge_fail) + v.isHidden = true + return v + }() + + // MARK: - Method + override init(frame: CGRect) { + super.init(frame: frame) + self.layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func layout() { + self.addSubviews(self.speechBubbleView, + self.wateringCanView, + self.showFlowerLanguageBubbleView, + self.complimentWriteBubbleImageView, + self.challengeFailBubbleView) + + self.remakeInProgressLayout() + + self.remakeCompletedLayout() + } + + func configureInProgress(viewModel: Home.ViewModel.ChallengeInProgressViewModel.MyFlowerViewModel.TopViewModel) { + self.remakeInProgressLayout() + + self.showFlowerLanguageBubbleView.snp.removeConstraints() + self.challengeFailBubbleView.snp.removeConstraints() + + self.wateringCanView.titleLabel.text = viewModel.cetificationGuideText + self.wateringCanView.titleLabel.isHidden = viewModel.isHiddenCetificationGuideText + + if viewModel.isCertificationButtonHidden { + self.wateringCanView.isHidden = true + self.wateringCanView.snp.removeConstraints() + } + else { + self.wateringCanView.isHidden = false + } + + if viewModel.isComplimentCommentHidden { + self.speechBubbleView.isHidden = true + self.complimentWriteBubbleImageView.isHidden = true + + self.speechBubbleView.snp.removeConstraints() + self.complimentWriteBubbleImageView.snp.removeConstraints() + } + else { + // 칭찬문구 O + if !viewModel.complimentCommentText.isEmpty { + self.speechBubbleView.configure(title: viewModel.complimentCommentText) + self.speechBubbleView.isHidden = false + self.complimentWriteBubbleImageView.isHidden = true + + self.complimentWriteBubbleImageView.snp.removeConstraints() + } + else { // 칭찬문구 X + self.complimentWriteBubbleImageView.isHidden = false + self.speechBubbleView.isHidden = true + + self.speechBubbleView.snp.removeConstraints() + } + } + } + + func configureCompleted(isHidden isFlowerLanguageBubbleHidden: Bool) { + self.remakeCompletedLayout() + + self.wateringCanView.snp.removeConstraints() + self.complimentWriteBubbleImageView.snp.removeConstraints() + self.speechBubbleView.snp.removeConstraints() + + if isFlowerLanguageBubbleHidden { // 챌린지 실패 + self.challengeFailBubbleView.isHidden = false + self.showFlowerLanguageBubbleView.isHidden = true + + self.showFlowerLanguageBubbleView.snp.removeConstraints() + } else { // 챌린지 성공 + self.challengeFailBubbleView.isHidden = true + self.showFlowerLanguageBubbleView.isHidden = false + + self.challengeFailBubbleView.snp.removeConstraints() + } + } + + private func remakeInProgressLayout() { + let speechBubbleBottom = UIDevice.current.deviceType == .default ? 15 : 32 + + self.speechBubbleView.snp.remakeConstraints { make in + make.top.equalToSuperview().offset(5) + make.centerX.equalToSuperview() + make.width.lessThanOrEqualTo(150) + make.bottom.equalToSuperview().offset(-speechBubbleBottom) + } + + self.complimentWriteBubbleImageView.snp.remakeConstraints { make in + make.top.equalToSuperview() + make.leading.trailing.equalToSuperview() + make.height.equalTo(62) + make.bottom.equalToSuperview().offset(-speechBubbleBottom) + } + + self.wateringCanView.snp.remakeConstraints { make in + make.top.equalToSuperview().offset(5) + make.leading.trailing.equalToSuperview() + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-12) + } + } + + private func remakeCompletedLayout() { + self.showFlowerLanguageBubbleView.snp.remakeConstraints { make in + make.top.equalToSuperview() + make.height.equalTo(48) + make.centerX.equalToSuperview().multipliedBy(0.8) + make.bottom.equalToSuperview().offset(-4) + } + + self.challengeFailBubbleView.snp.remakeConstraints { make in + make.top.equalToSuperview() + make.height.equalTo(63) + make.centerX.equalToSuperview().multipliedBy(0.8) + make.bottom.equalToSuperview().offset(-16) + } + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerView.swift index 06e88ea9..2802acc8 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/MyFlowerView.swift @@ -9,68 +9,18 @@ import UIKit import DesignSystem protocol MyFlowerViewDelegate: AnyObject { + /// 꽃 이미지를 탭 했을 때 - 인증 func didTapCertificateView() - func didTapEmptySpeechBubbleView() } final class MyFlowerView: UIView { weak var delegate: MyFlowerViewDelegate? - // MARK: - 챌린지 진행 중 : 꽃 이미지 위에 배치한 컴포넌트 - /// 칭찬시 나타나는 말풍선 - lazy var speechBubbleView: SpeechBubbleView = { - let v = SpeechBubbleView(tailPosition: .my) - v.isHidden = true - return v - }() - /// 꽃 상위에 보여질 인증 유도 스택 뷰 - lazy var induceCertificationView: InduceCertificationView = { - let v = InduceCertificationView() - v.isHidden = true - v.addTapAction { [weak self] in - self?.delegate?.didTapCertificateView() - } - return v - }() - /// 인증 완료 후 칭찬 문구 없을 때 이미지 뷰 - lazy var emptySpeechBubbleImageView: UIImageView = { - let v = UIImageView(.icon_bubble_write) - v.isHidden = true - v.isUserInteractionEnabled = true - v.addTapAction { [weak self] in - self?.delegate?.didTapEmptySpeechBubbleView() - } - return v - }() - // MARK: - 챌린지 완료 : 꽃 이미지 위에 배치한 컴포넌트 - /// 꽃 이름 라벨 - lazy var flowerNameLabel: UILabel = { - let v = UILabel() - v.font = .h3 - v.textColor = .primary - return v - }() - /// 꽃말 라벨 - lazy var flowerDescLabel: UILabel = { - let v = UILabel() - v.font = .body2 - v.textColor = .primary - return v - }() - /// 챌린지 완료뷰 - 꽃 이름, 꽃말 스택뷰 - lazy var flowerInfoStackView: UIStackView = { - let v = UIStackView() - v.axis = .vertical - v.spacing = 7 - v.isHidden = true - v.alignment = .center - v.addArrangedSubviews(self.flowerNameLabel, self.flowerDescLabel) - return v - }() - // MARK: - 공통 컴포넌트 lazy var flowerImageView: UIImageView = { let v = UIImageView() + v.contentMode = .scaleAspectFit + v.isUserInteractionEnabled = true v.addTapAction { [weak self] in self?.delegate?.didTapCertificateView() } @@ -83,11 +33,11 @@ final class MyFlowerView: UIView { cornerRadius: 13) return v }() + // MARK: - Method override init(frame: CGRect) { super.init(frame: frame) self.layout() - self.attribute() } required init?(coder: NSCoder) { @@ -95,45 +45,18 @@ final class MyFlowerView: UIView { } private func layout() { - self.addSubviews(self.speechBubbleView, - self.induceCertificationView, - self.flowerInfoStackView, - self.emptySpeechBubbleImageView, - self.flowerImageView, + self.addSubviews(self.flowerImageView, self.nicknameView) - self.speechBubbleView.snp.makeConstraints { make in - make.centerX.equalTo(self.flowerImageView.snp.centerX) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-42) - } - - self.emptySpeechBubbleImageView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalTo(self.flowerImageView.snp.centerX) - make.width.equalToSuperview().multipliedBy(0.7) - make.height.equalTo(62) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-32) - } - - self.induceCertificationView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.leading.trailing.equalToSuperview() - make.centerX.equalToSuperview().multipliedBy(0.8) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-8) - } - - self.flowerInfoStackView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalToSuperview().multipliedBy(0.8) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-12) - } - self.flowerImageView.snp.makeConstraints { make in + make.top.equalToSuperview() make.centerX.equalToSuperview().multipliedBy(0.8) + make.width.equalTo(113) + make.height.lessThanOrEqualTo(188) + make.bottom.equalTo(self.nicknameView.snp.top).offset(-7) } self.nicknameView.snp.makeConstraints { make in - make.top.equalTo(self.flowerImageView.snp.bottom).offset(7) make.height.equalTo(27) make.bottom.equalToSuperview() make.centerX.equalTo(self.flowerImageView.snp.centerX) @@ -145,33 +68,11 @@ final class MyFlowerView: UIView { func configureInProgress(viewModel: Home.ViewModel.ChallengeInProgressViewModel.MyFlowerViewModel) { self.flowerImageView.image = viewModel.image - self.induceCertificationView.isHidden = viewModel.isCertificationButtonHidden - self.induceCertificationView.titleLabel.text = viewModel.cetificationGuideText self.nicknameView.titleLabel.text = viewModel.myNameText - - if viewModel.isComplimentCommentHidden { - self.speechBubbleView.isHidden = true - self.emptySpeechBubbleImageView.isHidden = true - } - else { - // 칭찬문구 O - if !viewModel.complimentCommentText.isEmpty { - self.speechBubbleView.configure(title: viewModel.complimentCommentText) - self.speechBubbleView.isHidden = false - self.emptySpeechBubbleImageView.isHidden = true - } - else { // 칭찬문구 X - self.emptySpeechBubbleImageView.isHidden = false - self.speechBubbleView.isHidden = true - } - } } func configureCompleted(viewModel: Home.ViewModel.ChallengeCompletedViewModel.MyFlowerViewModel) { self.flowerImageView.image = viewModel.image - self.flowerInfoStackView.isHidden = viewModel.isFlowerTextHidden - self.flowerNameLabel.text = viewModel.flowerNameText - self.flowerDescLabel.text = viewModel.flowerDescText self.nicknameView.titleLabel.text = viewModel.myNameText } } diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerTopView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerTopView.swift new file mode 100644 index 00000000..45c97829 --- /dev/null +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerTopView.swift @@ -0,0 +1,163 @@ +// +// File.swift +// +// +// Created by Julia on 2023/09/17. +// + +import UIKit +import DesignSystem + +protocol PartnerFlowerTopInCompletedDelegate: AnyObject { + /// 꽃말 보기 말풍선을 탭 했을 때 - 팝업 띄우기 + func didTapShowPartnerFlowerLanguage() +} + +final class PartnerFlowerTopView: UIView { + + weak var inCompletedDelegate: PartnerFlowerTopInCompletedDelegate? + + // MARK: - 챌린지 진행 중 : 꽃 이미지 위에 배치한 컴포넌트 + lazy var speechBubbleView: SpeechBubbleView = { + let v = SpeechBubbleView(tailPosition: .partner) + v.isHidden = true + return v + }() + + lazy var finishLabel: UILabel = { + let v = UILabel() + v.text = "완료!" + v.font = .body2 + v.textColor = .mainCoral + return v + }() + + lazy var heartImageView: UIImageView = { + let v = UIImageView(.icon_heart) + return v + }() + /// 챌린지 인증 후 나타나는 "완료! ❤️" 스택뷰 + lazy var certificatedStackView: UIStackView = { + let v = UIStackView() + v.axis = .horizontal + v.alignment = .center + v.spacing = 3 + v.addArrangedSubviews(self.finishLabel, self.heartImageView) + v.isHidden = true + return v + }() + /// 인증 완료 후 칭찬 문구 없을 때 이미지 뷰 + lazy var emptySpeechBubbleImageView: UIImageView = { + let v = UIImageView(.icon_bubble_not_mate) + v.isHidden = true + return v + }() + // MARK: - 챌린지 완료 : 꽃 이미지 위에 배치한 컴포넌트 + /// 꽃말 보기 말풍선 이미지 + lazy var showFlowerLanguageBubbleView: UIImageView = { + let v = UIImageView() + v.image = .asset(.icon_bubble_flowerLanguage) + v.isHidden = true + v.isUserInteractionEnabled = true + v.addTapAction { [weak self] in + self?.inCompletedDelegate?.didTapShowPartnerFlowerLanguage() + } + return v + }() + + /// 챌린지 실패시(꽃을 피우지 못 했을 때) 보여지는 말풍선 뷰 + lazy var challengeFailBubbleView: UIImageView = { + let v = UIImageView() + v.image = .asset(.bubble_challenge_fail) + v.isHidden = true + return v + }() + + // MARK: - Method + override init(frame: CGRect) { + super.init(frame: frame) + self.layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func layout() { + self.addSubviews(self.speechBubbleView, + self.certificatedStackView, + self.showFlowerLanguageBubbleView, + self.emptySpeechBubbleImageView, + self.challengeFailBubbleView) + + self.heartImageView.snp.makeConstraints { make in + make.width.height.equalTo(12) + } + + self.speechBubbleView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.width.lessThanOrEqualTo(150) + make.bottom.equalToSuperview().offset(-10) + } + + self.showFlowerLanguageBubbleView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalToSuperview().multipliedBy(1.2) + make.bottom.equalToSuperview().offset(-4) + } + + self.certificatedStackView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().offset(-10) + } + + self.emptySpeechBubbleImageView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.centerX.equalToSuperview().multipliedBy(0.9) + make.bottom.equalToSuperview().offset(-10) + } + + self.showFlowerLanguageBubbleView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.height.equalTo(48) + make.centerX.equalToSuperview().multipliedBy(1.2) + make.bottom.equalToSuperview().offset(-4) + } + + self.challengeFailBubbleView.snp.makeConstraints { make in + make.centerX.equalToSuperview().multipliedBy(1.2) + make.height.equalTo(63) + make.bottom.equalToSuperview().offset(-16) + } + } + + func configureInProgress(viewModel: Home.ViewModel.ChallengeInProgressViewModel.PartnerFlowerViewModel.TopViewModel) { + self.certificatedStackView.isHidden = viewModel.isCertificationCompleteHidden + if viewModel.isComplimentCommentHidden { + self.speechBubbleView.isHidden = true + self.emptySpeechBubbleImageView.isHidden = true + } + else { + // 칭찬문구 O + if !viewModel.complimentCommentText.isEmpty { + self.speechBubbleView.configure(title: viewModel.complimentCommentText) + self.speechBubbleView.isHidden = false + self.emptySpeechBubbleImageView.isHidden = true + } + else { // 칭찬문구 X + self.emptySpeechBubbleImageView.isHidden = false + self.speechBubbleView.isHidden = true + } + } + } + + func configureCompleted(isHidden isFlowerLanguageBubbleHidden: Bool) { + if isFlowerLanguageBubbleHidden { // 챌린지 실패 + self.challengeFailBubbleView.isHidden = false + self.showFlowerLanguageBubbleView.isHidden = true + } else { // 챌린지 성공 + self.challengeFailBubbleView.isHidden = true + self.showFlowerLanguageBubbleView.isHidden = false + } + } +} diff --git a/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerView.swift b/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerView.swift index 29efc137..66c8965a 100644 --- a/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerView.swift +++ b/Scene/HomeScene/Sources/HomeScene/Views/Flower/PartnerFlowerView.swift @@ -9,69 +9,10 @@ import UIKit import DesignSystem final class PartnerFlowerView: UIView { - // MARK: - 챌린지 진행 중 : 꽃 이미지 위에 배치한 컴포넌트 - lazy var speechBubbleView: SpeechBubbleView = { - let v = SpeechBubbleView(tailPosition: .partner) - v.isHidden = true - return v - }() - - lazy var finishLabel: UILabel = { - let v = UILabel() - v.text = "완료!" - v.font = .body2 - v.textColor = .mainCoral - return v - }() - - lazy var heartImageView: UIImageView = { - let v = UIImageView(.icon_heart) - return v - }() - /// 챌린지 인증 후 나타나는 "완료! ❤️" 스택뷰 - lazy var certificatedStackView: UIStackView = { - let v = UIStackView() - v.axis = .horizontal - v.alignment = .center - v.spacing = 3 - v.addArrangedSubviews(self.finishLabel, self.heartImageView) - v.isHidden = true - return v - }() - /// 인증 완료 후 칭찬 문구 없을 때 이미지 뷰 - lazy var emptySpeechBubbleImageView: UIImageView = { - let v = UIImageView(.icon_bubble_not_mate) - v.isHidden = true - return v - }() - // MARK: - 챌린지 완료 : 꽃 이미지 위에 배치한 컴포넌트 - /// 꽃 이름 라벨 - lazy var flowerNameLabel: UILabel = { - let v = UILabel() - v.font = .h3 - v.textColor = .primary - return v - }() - /// 꽃말 라벨 - lazy var flowerDescLabel: UILabel = { - let v = UILabel() - v.font = .body2 - v.textColor = .primary - return v - }() - /// 꽃 이름, 꽃말 스택뷰 - lazy var flowerInfoStackView: UIStackView = { - let v = UIStackView() - v.axis = .vertical - v.spacing = 7 - v.isHidden = true - v.alignment = .center - v.addArrangedSubviews(self.flowerNameLabel, self.flowerDescLabel) - return v - }() - // MARK: - 공통 컴포넌트 + lazy var flowerImageView: UIImageView = { let v = UIImageView() + v.contentMode = .scaleAspectFit return v }() @@ -85,7 +26,7 @@ final class PartnerFlowerView: UIView { override init(frame: CGRect) { super.init(frame: frame) self.layout() - self.attribute() +// self.backgroundColor = .green } required init?(coder: NSCoder) { @@ -93,81 +34,31 @@ final class PartnerFlowerView: UIView { } private func layout() { - self.addSubviews(self.speechBubbleView, - self.certificatedStackView, - self.flowerInfoStackView, - self.emptySpeechBubbleImageView, - self.flowerImageView, + self.addSubviews(self.flowerImageView, self.nicknameView) - self.heartImageView.snp.makeConstraints { make in - make.width.height.equalTo(12) - } - - self.speechBubbleView.snp.makeConstraints { make in - make.centerX.equalTo(self.flowerImageView.snp.centerX) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-20) - } - - self.flowerInfoStackView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalToSuperview().multipliedBy(1.2) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-12) - } - - self.certificatedStackView.snp.makeConstraints { make in - make.centerX.equalTo(self.flowerImageView.snp.centerX) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-10) - } - - self.emptySpeechBubbleImageView.snp.makeConstraints { make in - make.centerX.equalTo(self.flowerImageView.snp.centerX) - make.bottom.equalTo(self.flowerImageView.snp.top).offset(-18) - } - self.flowerImageView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.width.equalTo(113) make.centerX.equalToSuperview().multipliedBy(1.2) + make.height.lessThanOrEqualTo(188) + make.bottom.equalTo(self.nicknameView.snp.top).offset(-7) } self.nicknameView.snp.makeConstraints { make in - make.top.equalTo(self.flowerImageView.snp.bottom).offset(7) make.height.equalTo(27) make.bottom.equalToSuperview() make.centerX.equalTo(self.flowerImageView.snp.centerX) } } - - private func attribute() { - } - + func configureInProgress(viewModel: Home.ViewModel.ChallengeInProgressViewModel.PartnerFlowerViewModel) { self.flowerImageView.image = viewModel.image - self.certificatedStackView.isHidden = viewModel.isCertificationCompleteHidden self.nicknameView.titleLabel.text = viewModel.partnerNameText - - if viewModel.isComplimentCommentHidden { - self.speechBubbleView.isHidden = true - self.emptySpeechBubbleImageView.isHidden = true - } - else { - // 칭찬문구 O - if !viewModel.complimentCommentText.isEmpty { - self.speechBubbleView.configure(title: viewModel.complimentCommentText) - self.speechBubbleView.isHidden = false - self.emptySpeechBubbleImageView.isHidden = true - } - else { // 칭찬문구 X - self.emptySpeechBubbleImageView.isHidden = false - self.speechBubbleView.isHidden = true - } - } } func configureCompleted(viewModel: Home.ViewModel.ChallengeCompletedViewModel.PartnerFlowerViewModel) { self.flowerImageView.image = viewModel.image - self.flowerInfoStackView.isHidden = viewModel.isFlowerTextHidden - self.flowerNameLabel.text = viewModel.flowerNameText - self.flowerDescLabel.text = viewModel.flowerDescText self.nicknameView.titleLabel.text = viewModel.partnerNameText } } diff --git a/Scene/HomeScene/Tests/HomeSceneTests/Mapping/ChallengeInProgressMappingSpec.swift b/Scene/HomeScene/Tests/HomeSceneTests/Mapping/ChallengeInProgressMappingSpec.swift index d97645a6..122d1cea 100644 --- a/Scene/HomeScene/Tests/HomeSceneTests/Mapping/ChallengeInProgressMappingSpec.swift +++ b/Scene/HomeScene/Tests/HomeSceneTests/Mapping/ChallengeInProgressMappingSpec.swift @@ -112,7 +112,7 @@ final class ChallengeInProgressMappingSpec: QuickSpec { context("내 칭찬 문구") { it("내 꽃의 칭찬 문구 텍스트가 'Test'로 표현된다.") { - expect(viewModel.myFlower.complimentCommentText).to(equal("Test")) + expect(viewModel.myFlower.topViewModel.complimentCommentText).to(equal("Test")) } } @@ -170,7 +170,7 @@ final class ChallengeInProgressMappingSpec: QuickSpec { context("상대방 칭찬 문구") { it("상대방 꽃의 칭찬 문구 텍스트가 'Test'로 표현된다.") { - expect(viewModel.partnerFlower.complimentCommentText).to(equal("Test")) + expect(viewModel.partnerFlower.topViewModel.complimentCommentText).to(equal("Test")) } } @@ -181,19 +181,19 @@ final class ChallengeInProgressMappingSpec: QuickSpec { } it("내 꽃의 인증 버튼 히든 여부가 false로 표현된다.") { - expect(viewModel.myFlower.isCertificationButtonHidden).to(beFalse()) + expect(viewModel.myFlower.topViewModel.isCertificationButtonHidden).to(beFalse()) } it("상대방 꽃의 인증 완료 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isCertificationCompleteHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden).to(beTrue()) } it("내 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.myFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.myFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("상대방 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("하트 히든 여부가 true로 표현된다.") { @@ -208,19 +208,19 @@ final class ChallengeInProgressMappingSpec: QuickSpec { } it("내 꽃의 인증 버튼 히든 여부가 false로 표현된다.") { - expect(viewModel.myFlower.isCertificationButtonHidden).to(beFalse()) + expect(viewModel.myFlower.topViewModel.isCertificationButtonHidden).to(beFalse()) } it("상대방 꽃의 인증 완료 히든 여부가 false로 표현된다.") { - expect(viewModel.partnerFlower.isCertificationCompleteHidden).to(beFalse()) + expect(viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden).to(beFalse()) } it("내 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.myFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.myFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("상대방 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("하트 히든 여부가 true로 표현된다.") { @@ -235,19 +235,19 @@ final class ChallengeInProgressMappingSpec: QuickSpec { } it("내 꽃의 인증 버튼 히든 여부가 true로 표현된다.") { - expect(viewModel.myFlower.isCertificationButtonHidden).to(beTrue()) + expect(viewModel.myFlower.topViewModel.isCertificationButtonHidden).to(beTrue()) } it("상대방 꽃의 인증 완료 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isCertificationCompleteHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden).to(beTrue()) } it("내 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.myFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.myFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("상대방 꽃의 칭찬 문구 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isComplimentCommentHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isComplimentCommentHidden).to(beTrue()) } it("하트 히든 여부가 true로 표현된다.") { @@ -262,19 +262,19 @@ final class ChallengeInProgressMappingSpec: QuickSpec { } it("내 꽃의 인증 버튼 히든 여부가 true로 표현된다.") { - expect(viewModel.myFlower.isCertificationButtonHidden).to(beTrue()) + expect(viewModel.myFlower.topViewModel.isCertificationButtonHidden).to(beTrue()) } it("상대방 꽃의 인증 완료 히든 여부가 true로 표현된다.") { - expect(viewModel.partnerFlower.isCertificationCompleteHidden).to(beTrue()) + expect(viewModel.partnerFlower.topViewModel.isCertificationCompleteHidden).to(beTrue()) } it("내 꽃의 칭찬 문구 히든 여부가 false로 표현된다.") { - expect(viewModel.myFlower.isComplimentCommentHidden).to(beFalse()) + expect(viewModel.myFlower.topViewModel.isComplimentCommentHidden).to(beFalse()) } it("상대방 꽃의 칭찬 문구 히든 여부가 false로 표현된다.") { - expect(viewModel.partnerFlower.isComplimentCommentHidden).to(beFalse()) + expect(viewModel.partnerFlower.topViewModel.isComplimentCommentHidden).to(beFalse()) } it("하트 히든 여부가 false로 표현된다.") { diff --git a/Scene/InvitationSendScene/Sources/InvitationSendScene/InvitationSendViewController.swift b/Scene/InvitationSendScene/Sources/InvitationSendScene/InvitationSendViewController.swift index ead1624d..2151d157 100644 --- a/Scene/InvitationSendScene/Sources/InvitationSendScene/InvitationSendViewController.swift +++ b/Scene/InvitationSendScene/Sources/InvitationSendScene/InvitationSendViewController.swift @@ -108,7 +108,7 @@ final class InvitationSendViewController: UIViewController { make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().inset(24) make.height.equalTo(57) - make.bottom.equalToSuperview().inset(54) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } } } diff --git a/Scene/InvitationWaitScene/Sources/InvitationWaitScene/InvitationWaitViewController.swift b/Scene/InvitationWaitScene/Sources/InvitationWaitScene/InvitationWaitViewController.swift index 3abd3849..29c1508a 100644 --- a/Scene/InvitationWaitScene/Sources/InvitationWaitScene/InvitationWaitViewController.swift +++ b/Scene/InvitationWaitScene/Sources/InvitationWaitScene/InvitationWaitViewController.swift @@ -144,7 +144,7 @@ final class InvitationWaitViewController: UIViewController { make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().inset(24) make.height.equalTo(57) - make.bottom.equalToSuperview().inset(54) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(20) } } } diff --git a/Scene/LoginScene/Package.swift b/Scene/LoginScene/Package.swift index 2bda4f45..07c773e4 100644 --- a/Scene/LoginScene/Package.swift +++ b/Scene/LoginScene/Package.swift @@ -37,7 +37,9 @@ let package = Package( dependencies: [ .product(name: "CoreKit", package: "CoreKit"), .product(name: "KakaoSDK", package: "kakao-ios-sdk"), - .product(name: "FirebaseMessaging", package: "firebase-ios-sdk") + .product(name: "FirebaseMessaging", package: "firebase-ios-sdk"), + .product(name: "FirebaseCrashlytics", package: "firebase-ios-sdk"), + .product(name: "FirebaseAnalytics", package: "firebase-ios-sdk"), ], resources: [.process("Assets")] ), diff --git a/Scene/LoginScene/Sources/LoginScene/LoginWorker.swift b/Scene/LoginScene/Sources/LoginScene/LoginWorker.swift index ad5d401c..6a981bd8 100644 --- a/Scene/LoginScene/Sources/LoginScene/LoginWorker.swift +++ b/Scene/LoginScene/Sources/LoginScene/LoginWorker.swift @@ -81,7 +81,7 @@ final class LoginWorker: LoginWorkerProtocol { let authResponse = try await self.authNetworkWorker.requestAuthorize( socialId: socialId, loginType: loginType, - deviceToken: self.meLocalWorker.deviceToken ?? "" + deviceToken: self.meLocalWorker.deviceToken ?? "FCM Token" ) self.meLocalWorker.token = authResponse.accessToken self.meLocalWorker.nickname = authResponse.nickname @@ -122,6 +122,7 @@ final class LoginWorker: LoginWorkerProtocol { } } + @MainActor private func loginWithKakaoAccountSDK() async throws { return try await withCheckedThrowingContinuation { continuation in UserApi.shared.loginWithKakaoAccount { oAuthToken, error in diff --git a/Scene/MainScene/Sources/MainScene/MainTabBarController.swift b/Scene/MainScene/Sources/MainScene/MainTabBarController.swift index 33028787..0781055c 100644 --- a/Scene/MainScene/Sources/MainScene/MainTabBarController.swift +++ b/Scene/MainScene/Sources/MainScene/MainTabBarController.swift @@ -42,7 +42,7 @@ final class MainTabBarController: UITabBarController { // MARK: - Layout private func setUI() { - self.view.backgroundColor = .clear + self.view.setBackgroundDefault() } func setTab(_ tab: Main.ViewModel.MainTab) { diff --git a/Scene/MyInfoScene/Package.swift b/Scene/MyInfoScene/Package.swift index a0d883e0..64fde65b 100644 --- a/Scene/MyInfoScene/Package.swift +++ b/Scene/MyInfoScene/Package.swift @@ -10,6 +10,10 @@ let package = Package( .library( name: "MyInfoScene", targets: ["MyInfoScene"] + ), + .library( + name: "ChangeNicknameScene", + targets: ["ChangeNicknameScene"] ) ], dependencies: [ @@ -27,7 +31,8 @@ let package = Package( .target( name: "MyInfoScene", dependencies: [ - .product(name: "CoreKit", package: "CoreKit") + .product(name: "CoreKit", package: "CoreKit"), + "ChangeNicknameScene" ], resources: [.process("Assets")] ), @@ -38,6 +43,12 @@ let package = Package( .product(name: "Nimble", package: "Nimble"), .product(name: "Quick", package: "Quick") ] + ), + .target( + name: "ChangeNicknameScene", + dependencies: [ + .product(name: "CoreKit", package: "CoreKit") + ] ) ] ) diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameInteractor.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameInteractor.swift new file mode 100644 index 00000000..777a644d --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameInteractor.swift @@ -0,0 +1,90 @@ +// +// ChangeNicknameInteractor.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. All rights reserved. +// + +import CoreKit + +protocol ChangeNicknameBusinessLogic { + /// 닉네임 문구 입력 + func didEnterMyNickname(name: String) async + /// 확인 버튼 클릭 + func didTapChangeButton() async + /// 변경 버튼 enable + func didUpdateChangeButton() async +} + +protocol ChangeNicknameDataStore: AnyObject {} + +final class ChangeNicknameInteractor: ChangeNicknameDataStore, ChangeNicknameBusinessLogic { + var cancellables: Set = [] + + var presenter: ChangeNicknamePresentationLogic + var router: ChangeNicknameRoutingLogic + var worker: ChangeNicknameWorkerProtocol + + init( + presenter: ChangeNicknamePresentationLogic, + router: ChangeNicknameRoutingLogic, + worker: ChangeNicknameWorkerProtocol + ) { + self.presenter = presenter + self.router = router + self.worker = worker + } + + // MARK: - DataStore + + var nicknameDataSource: String? +} + +// MARK: - Interactive Business Logic + +extension ChangeNicknameInteractor { + + /// 외부 액션 옵저빙 + func observe() { + + } +} + +// MARK: Feature (닉네임 변경) + +extension ChangeNicknameInteractor { + func didEnterMyNickname(name: String) async { + self.nicknameDataSource = name + await self.didUpdateChangeButton() + } + + func didTapChangeButton() async { + do { + try await self.worker.requestChangeNickname(name: self.nicknameDataSource ?? "") + await self.presenter.presentChangeNicknameSucess(text: "닉네임이 변경되었습니다") + await self.router.dismiss() + } + catch { + await self.presenter.presentChangeNicknameError(error: error) + await self.router.dismiss() + } + } + + func didUpdateChangeButton() async { + if self.nicknameDataSource != "" { + await self.presenter.presentEnabled(changeButton: .init(isEnabled: true)) + } + else { + await self.presenter.presentEnabled(changeButton: .init(isEnabled: false)) + } + } +} + +// MARK: - Application Business Logic + +// MARK: UseCase () + +extension ChangeNicknameInteractor { + +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameModels.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameModels.swift new file mode 100644 index 00000000..ace89b2a --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameModels.swift @@ -0,0 +1,36 @@ +// +// ChangeNicknameModels.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. All rights reserved. +// + +import UIKit + +enum ChangeNickname { + + // MARK: Entity + + enum Model { + struct Data { + /// 내 닉네임 + var nickname: String + } + } + + enum ViewModel { + + struct Title { + var text: String? + } + + struct ChangeButton { + var isEnabled: Bool? + } + + struct Toast { + var message: String? + } + } +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknamePresenter.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknamePresenter.swift new file mode 100644 index 00000000..3d5cc07f --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknamePresenter.swift @@ -0,0 +1,40 @@ +// +// ChangeNicknamePresenter.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. All rights reserved. +// + +import UIKit + +@MainActor +protocol ChangeNicknamePresentationLogic { + /// 닉네임 변경 성공을 보여준다 + func presentChangeNicknameSucess(text: String) + /// 닉네임 변경 실패 오류를 보여준다. + func presentChangeNicknameError(error: Error) + /// 변경 버튼 활성화하여 보여준다. + func presentEnabled(changeButton: ChangeNickname.ViewModel.ChangeButton) +} + +final class ChangeNicknamePresenter { + weak var viewController: ChangeNicknameViewControllerDisplayLogic? + +} + +// MARK: - Presentation Logic + +extension ChangeNicknamePresenter: ChangeNicknamePresentationLogic { + func presentChangeNicknameSucess(text: String) { + self.viewController?.displayToast(viewModel: .init(message: text)) + } + + func presentChangeNicknameError(error: Error) { + self.viewController?.displayToast(viewModel: .init(message: "닉네임 변경에 실패했습니다. 다시 시도해주세요")) + } + + func presentEnabled(changeButton: ChangeNickname.ViewModel.ChangeButton) { + self.viewController?.displaySetEnableNextButton(viewModel: .init(isEnabled: changeButton.isEnabled)) + } +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameRouter.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameRouter.swift new file mode 100644 index 00000000..8be2e56f --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameRouter.swift @@ -0,0 +1,26 @@ +// +// ChangeNicknameRouter.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. All rights reserved. +// + +import UIKit + +@MainActor +protocol ChangeNicknameRoutingLogic { + // 화면을 닫는다. + func dismiss() +} + +final class ChangeNicknameRouter { + weak var viewController: ChangeNicknameViewController? + weak var dataStore: ChangeNicknameDataStore? +} + +extension ChangeNicknameRouter: ChangeNicknameRoutingLogic { + func dismiss() { + self.viewController?.navigationController?.popViewController(animated: true) + } +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameSceneFactory.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameSceneFactory.swift new file mode 100644 index 00000000..e309a877 --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameSceneFactory.swift @@ -0,0 +1,46 @@ +// +// ChangeNicknameSceneFactory.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. All rights reserved. +// + +import CoreKit + +@MainActor +public protocol ChangeNicknameScene: AnyObject, Scene { + +} + +public struct ChangeNicknameConfiguration { + public init() {} +} + +public final class ChangeNicknameSceneFactory { + + public init() {} + + public func make(with configuration: ChangeNicknameConfiguration) -> ChangeNicknameScene { + + let localDataSource = LocalDataSource() + let meLocalWorker = MeLocalWorker(localDataSource: localDataSource) + let changeNicknameNetworkWorker = ChangeNicknameNetworkWorker() + let presenter = ChangeNicknamePresenter() + let router = ChangeNicknameRouter() + let worker = ChangeNicknameWorker(meLocalWorker: meLocalWorker, changeNicknameNetworkWorker: changeNicknameNetworkWorker) + let interactor = ChangeNicknameInteractor( + presenter: presenter, + router: router, + worker: worker + ) + let viewController = ChangeNicknameViewController( + interactor: interactor + ) + presenter.viewController = viewController + router.viewController = viewController + router.dataStore = interactor + + return viewController + } +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameViewController.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameViewController.swift new file mode 100644 index 00000000..5175ecea --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameViewController.swift @@ -0,0 +1,194 @@ +// +// ChangeNicknameViewController.swift +// TwoToo +// +// Created by Eddy on 2023/10/12. +// Copyright (c) 2023 TwoToo. TwoToo. All rights reserved. +// + +import CoreKit +import UIKit + +protocol ChangeNicknameViewControllerDisplayLogic: AnyObject { + func displaySetEnableNextButton(viewModel: ChangeNickname.ViewModel.ChangeButton) + func displayToast(viewModel: ChangeNickname.ViewModel.Toast) +} + +final class ChangeNicknameViewController: UIViewController { + var interactor: ChangeNicknameBusinessLogic + + init(interactor: ChangeNicknameBusinessLogic) { + self.interactor = interactor + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - UI + + private lazy var navigationbar: TTNavigationDetailBar = { + let v = TTNavigationDetailBar() + v.configure(title: "", leftButtonImage: .asset(.icon_back), rightButtonImage: nil) + v.delegate = self + return v + }() + + private lazy var mainIconImageView: UIImageView = { + let v = UIImageView() + v.image = .asset(.icon_nicknam_my) + return v + }() + + private lazy var descriptionLabel: UILabel = { + let v = UILabel() + v.setLineSpacing(11) + v.text = "변경할 닉네임을\n입력해주세요" + v.textAlignment = .center + v.font = .h1 + v.textColor = .primary + v.numberOfLines = 0 + return v + }() + + private lazy var nicknameTextField: TTTextField = { + let v = TTTextField(title: "닉네임", placeholder: "4글자 이내 닉네임을 입력해주세요", maxLength: 4) + return v + }() + + private lazy var changeButton: TTPrimaryButtonType = { + let v = TTPrimaryButton.create(title: "변경", .large) + v.setIsEnabled(false) + v.addAction { [weak self] in + Task { + Loading.shared.showLoadingView() + await self?.interactor.didTapChangeButton() + Loading.shared.stopLoadingView() + } + } + return v + }() + + // MARK: - View Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + self.setUI() + self.registKeyboardDelegate() + + self.nicknameTextField.didChangeTextAction = { text in + Task { + await self.interactor.didEnterMyNickname(name: text) + } + } + + self.nicknameTextField.returnValueAction = { text in + Task { + await self.interactor.didEnterMyNickname(name: text) + await self.interactor.didTapChangeButton() + } + } + } + + @objc private func didTapView() { + self.view.endEditing(true) + } + + // MARK: - Layout + + private func setUI() { + self.view.setBackgroundDefault() + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapView)) + self.view.addGestureRecognizer(tapGesture) + self.view.addSubviews( + self.navigationbar, self.mainIconImageView, self.descriptionLabel, + self.nicknameTextField, self.changeButton) + + self.navigationbar.snp.makeConstraints { make in + make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) + make.leading.trailing.equalToSuperview() + make.height.equalTo(44) + } + + self.mainIconImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(self.navigationbar.snp.bottom).offset(2) + make.bottom.equalTo(self.descriptionLabel.snp.top).offset(-4) + } + + self.descriptionLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalTo(self.nicknameTextField.snp.top).offset(-40) + } + + self.nicknameTextField.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.leading.trailing.equalToSuperview().inset(24) + } + + self.changeButton.snp.makeConstraints { make in + make.leading.equalToSuperview().offset(24) + make.trailing.equalToSuperview().offset(-24) + make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-20) + } + } +} + +// MARK: - Trigger + +extension ChangeNicknameViewController: TTNavigationDetailBarDelegate { + func didTapDetailLeftButton() { + self.navigationController?.popViewController(animated: true) + } + + func didTapDetailRightButton() { + + } +} + +// MARK: - Trigger by Parent Scene + +extension ChangeNicknameViewController: ChangeNicknameScene { + +} + +// MARK: - Display Logic + +extension ChangeNicknameViewController: ChangeNicknameViewControllerDisplayLogic { + + func displaySetEnableNextButton(viewModel: ChangeNickname.ViewModel.ChangeButton) { + viewModel.isEnabled.unwrap { + self.changeButton.setIsEnabled($0) + } + } + + func displayToast(viewModel: ChangeNickname.ViewModel.Toast) { + viewModel.message.unwrap { + Toast.shared.makeToast($0) + } + } +} + +// MARK: - Keyboard Setting + +extension ChangeNicknameViewController: KeyboardDelegate { + func willShowKeyboard(keyboardFrame: CGRect, duration: Double) { + + UIView.animate(withDuration: duration) { + self.changeButton.snp.updateConstraints { make in + make.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(keyboardFrame.height + 20) + } + self.view.layoutIfNeeded() + } + } + + func willHideKeyboard(duration: Double) { + UIView.animate(withDuration: duration) { + self.changeButton.snp.updateConstraints { make in + make.bottom.equalTo(self.view.safeAreaLayoutGuide).offset(-20) + } + self.view.layoutIfNeeded() + } + } +} diff --git a/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameWorker.swift b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameWorker.swift new file mode 100644 index 00000000..06810e3d --- /dev/null +++ b/Scene/MyInfoScene/Sources/ChangeNicknameScene/ChangeNicknameWorker.swift @@ -0,0 +1,31 @@ +// +// ChangeNicknameWorker.swift +// +// +// Created by Eddy on 2023/10/12. +// + +import CoreKit + +protocol ChangeNicknameWorkerProtocol { + func requestChangeNickname(name: String) async throws +} + +final class ChangeNicknameWorker: ChangeNicknameWorkerProtocol { + + var meLocalWorker: MeLocalWorkerProtocol + var changeNicknameNetworkWorker: ChangeNicknameNetworkWorkerProtocol + + init( + meLocalWorker: MeLocalWorkerProtocol, + changeNicknameNetworkWorker: ChangeNicknameNetworkWorkerProtocol + ) { + self.meLocalWorker = meLocalWorker + self.changeNicknameNetworkWorker = changeNicknameNetworkWorker + } + + func requestChangeNickname(name: String) async throws { + _ = try await self.changeNicknameNetworkWorker.requestChangeNicknameInquiry(nickname: name) + + } +} diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoInteractor.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoInteractor.swift index e61309c8..6e17d515 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoInteractor.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoInteractor.swift @@ -10,8 +10,8 @@ import CoreKit import Foundation protocol MyInfoBusinessLogic { - /// 첫 진입 - func didLoad() async + /// 진입 + func didAppear() async /// 설명서 버튼 클릭 func didTapGuideButton() async /// Lists에 있는 목록들 클릭 @@ -22,20 +22,12 @@ protocol MyInfoBusinessLogic { func didTapSignOutPopupCancelButton() async /// 회원 탈퇴 완료 확인 버튼 클릭 func didTapSignOutCompleteConfirmButton() async - /// 회원 탈퇴 취소 팝업의 탈퇴 취소 버튼 클릭 - func didTapCancelSignOutCancelButton() async - /// 회원 탈퇴 취소 팝업의 아니요 버튼 클릭 - func didTapSignOutCancelCompleteNobutton() async - /// 회원 탈퇴 취소 완료의 확인 버튼 클릭 - func didTapSignOutCancelCompleteConfirmButton() async /// 회원탈퇴 팝업의 배경 클릭 func didTapSignOutPopupBackground() async /// 회원 탈퇴 완료 팝업의 배경 클릭 func didTapSignOutCompletePopupBackground() async - /// 회원 탈퇴 취소하기 팝업의 배경 클릭 - func didTapSignOutCancelPopupBackground() async - /// 회원 탈퇴 취소 완료 팝업의 배경 클릭 - func didTapSignOutCancelCompletePopupBackground() async + /// 닉네임 변경 버튼을 클릭 + func didTapChangeNicknameButton() async } protocol MyInfoDataStore: AnyObject { @@ -44,7 +36,7 @@ protocol MyInfoDataStore: AnyObject { } final class MyInfoInteractor: MyInfoDataStore, MyInfoBusinessLogic { - + var cancellables: Set = [] var presenter: MyInfoPresentationLogic @@ -109,11 +101,11 @@ extension MyInfoInteractor { } } -// MARK: Feature (첫 진입) +// MARK: Feature (진입) extension MyInfoInteractor { - - func didLoad() async { + + func didAppear() async { do { let mypageInfo = try await self.worker.fetchMypageInfo() @@ -134,7 +126,7 @@ extension MyInfoInteractor { await self.router.routeToMyInfoListsScene(url: url) } - + /// 공지사항, 이용가이드, 투투에 문의하기, 만든이들 클릭했을 때 func didTapMyInfoLists(index: Int) async { let myInfo = MyInfoLists(rawValue: index) @@ -143,35 +135,14 @@ extension MyInfoInteractor { await self.worker.logout() self.didTriggerRouteToLoginScene.send(()) } - + if myInfo == .singout { - let socailLoginType = self.worker.fetchSocialLoginType() - - if socailLoginType == .appleLogin { - try? await self.worker.retryAppleLogin() - - let isSingOutRequired = self.worker.fetchAppleSignOutStatus() - // true면 회원탈퇴 신청한 상태 false면 회원탈퇴 신청전 상태 - if isSingOutRequired { - await self.presenter.presentSignOutCancelPopup() - } else { - await self.presenter.presentSignOutPopup() - } - } - else if socailLoginType == .kakaoLogin { - let isSingOutRequired = self.worker.fetchKakaoSignOutStatus() - // true면 회원탈퇴 신청한 상태 false면 회원탈퇴 신청전 상태 - if isSingOutRequired { - await self.presenter.presentSignOutCancelPopup() - - } else { - await self.presenter.presentSignOutPopup() - } - } + await self.presenter.presentSignOutPopup() + return } guard let url = myInfo?.url else { return } - + await self.router.routeToMyInfoListsScene(url: url) } } @@ -180,49 +151,41 @@ extension MyInfoInteractor { extension MyInfoInteractor { func didTapSignoutPopupSignOutButton() async { - await self.presenter.dismissSignOutPopup() - await self.presenter.presentSignOutCompletePopup() + do { + try await self.worker.signOut() + await self.presenter.dismissSignOutPopup() + await self.presenter.presentSignOutCompletePopup() + } + catch { + await self.presenter.presentSignOutError(error: error) + } } - + func didTapSignOutPopupCancelButton() async { await self.presenter.dismissSignOutPopup() } - + func didTapSignOutCompleteConfirmButton() async { await self.presenter.dismissSignOutCompletePopup() - self.worker.setSignoutStatus(required: true, socialType: self.worker.fetchSocialLoginType()) - } - - func didTapCancelSignOutCancelButton() async { - await self.presenter.dismissSignOutCancelPopup() - await self.presenter.presentSignOutCancelCompletePopup() - } - - func didTapSignOutCancelCompleteNobutton() async { - await self.presenter.dismissSignOutCancelPopup() - } - - func didTapSignOutCancelCompleteConfirmButton() async { - await self.presenter.dismissSignOutCancelCompletePopup() - self.worker.setSignoutStatus(required: false, socialType: self.worker.fetchSocialLoginType()) + self.didTriggerRouteToLoginScene.send(()) } - + func didTapSignOutPopupBackground() async { await self.presenter.dismissSignOutPopup() } - + func didTapSignOutCompletePopupBackground() async { await self.presenter.dismissSignOutCompletePopup() - self.worker.setSignoutStatus(required: true, socialType: self.worker.fetchSocialLoginType()) - } + self.didTriggerRouteToLoginScene.send(()) - func didTapSignOutCancelPopupBackground() async { - await self.presenter.dismissSignOutCancelPopup() } +} - func didTapSignOutCancelCompletePopupBackground() async { - await self.presenter.dismissSignOutCancelCompletePopup() - self.worker.setSignoutStatus(required: false, socialType: self.worker.fetchSocialLoginType()) +// MARK: Feature (닉네임 변경) + +extension MyInfoInteractor { + func didTapChangeNicknameButton() async { + await self.router.routeToChangeNicknameScene() } } diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoModels.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoModels.swift index 600b87c1..ebffc6f4 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoModels.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoModels.swift @@ -18,7 +18,7 @@ enum MyInfo { var partnerNickname: String var challengeTotalCount: String? } - + enum SocialLoginStatus: String, Equatable { /// 카카오 로그인 case kakaoLogin = "Kakao" @@ -35,72 +35,44 @@ enum MyInfo { var title: String } } - + struct Data { var myNickname: String var partnerNickname: String var challengeTotalCount: String? } - + struct Toast { var message: String? } - + /// 회원 탈퇴 팝업 struct SignOutViewModel { var show: (UIImage)? var dismiss: ()? - + /// 타이틀 static let title: String = "회원 탈퇴하기" /// 메세지 - static let message: String = "1~2일 후에 삭제가 완료되어요" + static let warning: String = "*(경고) 회원 탈퇴 시 모든 기록이 삭제되고 복구가 불가능합니다. 또한 파트너 정보도 함께 삭제됩니다!*" /// 취소 static let cancelOptionText: String = "취소" /// 탈퇴하기 옵션 static let signOutOptionText: String = "탈퇴하기" } - + /// 회원 탈퇴 완료 팝업 struct SignOutCompletedViewModel { var show: (UIImage)? var dismiss: ()? - + /// 타이틀 static let title: String = "회원 탈퇴 완료" /// 메세지 static let message: String = "아쉽지만 다음에 또 만나요!" /// 확인 static let confirmOptionText: String = "확인" - - } - - /// 회원 탈퇴 취소 팝업 - struct SignOutCancelViewModel { - var show: (UIImage)? - var dismiss: ()? - - /// 타이틀 - static let title: String = "회원 탈퇴 취소하기" - /// 메세지 - static let message: String = "회원탈퇴가 이미 요청되었어요\n탈퇴를 취소하실건까요?" - /// 아니요 - static let noOptionText: String = "아니요" - /// 탈퇴 취소 - static let SingOutCancelOptionText: String = "탈퇴 취소" - } - - /// 회원 탈퇴 취소 완료 팝업 - struct SignOutCancelCompletedViewModel { - var show: (UIImage)? - var dismiss: ()? - - /// 타이틀 - static let title: String = "회원 탈퇴 취소 완료" - /// 메세지 - static let message: String = "회원 탈퇴 요청 취소되었어요" - /// 확인 - static let confirmOptionText: String = "확인" + } } } diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoPresenter.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoPresenter.swift index c00d7c2e..1e7983d4 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoPresenter.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoPresenter.swift @@ -15,7 +15,7 @@ protocol MyInfoPresentationLogic { func presentMyInfo(model: MyInfo.Model.Data) /// 마이 페이지 오류를 보여준다. func presentMyInfoError(error: Error) - + /// 회원 탈퇴하기 팝업을 보여준다 func presentSignOutPopup() /// 회원 탈퇴하기 팝업을 제거한다. @@ -24,14 +24,8 @@ protocol MyInfoPresentationLogic { func presentSignOutCompletePopup() /// 회원 탈퇴 완료 팝업을 제거한다. func dismissSignOutCompletePopup() - /// 회원 탈퇴 취소 팝업을 보여준다. - func presentSignOutCancelPopup() - /// 회원 탈퇴 취소 팝업을 제거한다. - func dismissSignOutCancelPopup() - /// 회원 탈퇴 취소 완료 팝업을 보여준다. - func presentSignOutCancelCompletePopup() - /// 회원 탈퇴 취소 완료 팝업을 제거한다. - func dismissSignOutCancelCompletePopup() + /// 회원 탈퇴 오류를 보여준다. + func presentSignOutError(error: Error) } final class MyInfoPresenter { @@ -53,9 +47,9 @@ extension MyInfoPresenter: MyInfoPresentationLogic { .init(title: "로그아웃"), .init(title: "회원탈퇴") ]) - + let totalCount = "\(model.challengeTotalCount ?? "0")번째 꽃 피우는중" - + self.viewController?.displayLists(viewModel: myInfoItems) self.viewController?.displayMyInfo(viewModel: .init(myNickname: model.myNickname, partnerNickname: model.partnerNickname, challengeTotalCount: totalCount)) } @@ -63,36 +57,28 @@ extension MyInfoPresenter: MyInfoPresentationLogic { func presentMyInfoError(error: Error) { self.viewController?.displayToast(viewModel: .init(message: error.localizedDescription)) } - + func presentSignOutPopup() { self.viewController?.displaySignOutPopup(viewModel: .init(show: .asset(.icon_delete))) } - + func dismissSignOutPopup() { self.viewController?.displaySignOutPopup(viewModel: .init(dismiss: ())) } - + func presentSignOutCompletePopup() { self.viewController?.displaySignOutCompletePopup(viewModel: .init(show: .asset(.icon_delete))) } - + func dismissSignOutCompletePopup() { self.viewController?.displaySignOutCompletePopup(viewModel: .init(dismiss: ())) } - - func presentSignOutCancelPopup() { - self.viewController?.displaySignOutCancelPopup(viewModel: .init(show: .asset(.icon_nickname_mate))) - } - - func dismissSignOutCancelPopup() { - self.viewController?.displaySignOutCancelPopup(viewModel: .init(dismiss: ())) - } - - func presentSignOutCancelCompletePopup() { - self.viewController?.displaySignOutCancelCompletePopup(viewModel: .init(show: .asset(.icon_congratulation))) + + func presentSignOutError(error: Error) { + self.viewController?.displayToast(viewModel: .init(message: error.localizedDescription)) } - - func dismissSignOutCancelCompletePopup() { - self.viewController?.displaySignOutCancelCompletePopup(viewModel: .init(dismiss: ())) + + func presentChangeNicknameView() { + } } diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoRouter.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoRouter.swift index d32956fe..c7fd0d41 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoRouter.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoRouter.swift @@ -8,11 +8,14 @@ import UIKit import SafariServices +import ChangeNicknameScene @MainActor protocol MyInfoRoutingLogic { /// 마이페이지 내 공지사항, 이용가이드, 투투에 문의하기, 만든이들 화면으로 이동한다. func routeToMyInfoListsScene(url: URL) + /// 닉네임 변경 화면으로 이동한다. + func routeToChangeNicknameScene() } final class MyInfoRouter { @@ -23,9 +26,15 @@ final class MyInfoRouter { extension MyInfoRouter: MyInfoRoutingLogic { /// 사파리 웹뷰로 보여준다. func routeToMyInfoListsScene(url: URL) { - + let safariViewController = SFSafariViewController(url: url) self.viewController?.present(safariViewController, animated: true, completion: nil) } + + func routeToChangeNicknameScene() { + let changeNicknameScene = ChangeNicknameSceneFactory().make(with: .init()).viewController + changeNicknameScene.hidesBottomBarWhenPushed = true + self.viewController?.navigationController?.pushViewController(changeNicknameScene, animated: true) + } } diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoSceneFactory.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoSceneFactory.swift index d1195531..da4fc009 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoSceneFactory.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoSceneFactory.swift @@ -30,17 +30,19 @@ public final class MyInfoSceneFactory { let localDataSource = LocalDataSource() let meLocalWorker = MeLocalWorker(localDataSource: localDataSource) + let invitationLocalWorker = InvitationLocalWorker(localDataSource: localDataSource) + let invitedUserLocalWorker = InvitedUserLocalWorker(localDataSource: localDataSource) let meNetworkWorker = MeNetworkWorker() - let appleLoginWorker = CommonAppleLoginWorker() - let myInfoLocalWorker = MyInfoLocalWorker(localDataSource: localDataSource) + let signOutNetworkWorker = SignOutNetworkWorker() let presenter = MyInfoPresenter() let router = MyInfoRouter() let worker = MyInfoWorker( meLocalWorker: meLocalWorker, + invitationLocalWorker: invitationLocalWorker, + invitedUserLocalWorker: invitedUserLocalWorker, meNetworkWorker: meNetworkWorker, - appleLoginWorker: appleLoginWorker, - myInfoLocalWorker: myInfoLocalWorker + signOutNetworkWorker: signOutNetworkWorker ) let interactor = MyInfoInteractor( presenter: presenter, diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoViewController.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoViewController.swift index cf5195e9..c3f2121c 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoViewController.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoViewController.swift @@ -20,10 +20,6 @@ protocol MyInfoDisplayLogic: AnyObject { func displaySignOutPopup(viewModel: MyInfo.ViewModel.SignOutViewModel) /// 회원 탈퇴 완료 팝업을 보여준다. func displaySignOutCompletePopup(viewModel: MyInfo.ViewModel.SignOutCompletedViewModel) - /// 회원탈퇴 취소 팝업을 보여준다. - func displaySignOutCancelPopup(viewModel: MyInfo.ViewModel.SignOutCancelViewModel) - /// 회원탈퇴 취소 완료 팝업을 보여준다. - func displaySignOutCancelCompletePopup(viewModel: MyInfo.ViewModel.SignOutCancelCompletedViewModel) } final class MyInfoViewController: UIViewController { @@ -44,7 +40,7 @@ final class MyInfoViewController: UIViewController { v.image = .asset(.icon_nicknam_my) return v }() - + private lazy var nameStackView: UIStackView = { let v = UIStackView() v.axis = .horizontal @@ -55,17 +51,17 @@ final class MyInfoViewController: UIViewController { private lazy var myNicknameLabel: UILabel = { let v = UILabel() v.textColor = .mainCoral - v.font = .body3 + v.font = .body2 return v }() - + private lazy var partnerNicknameLabel: UILabel = { let v = UILabel() v.textColor = .mainCoral - v.font = .body3 + v.font = .body2 return v }() - + private lazy var heartImageView: UIImageView = { let v = UIImageView() v.image = .asset(.icon_heart) @@ -75,12 +71,37 @@ final class MyInfoViewController: UIViewController { private lazy var challengeCountLabel: UILabel = { let v = UILabel() v.textColor = .mainCoral - v.font = .body3 + v.font = .body2 + return v + }() + + private lazy var nicknameStackView: UIStackView = { + let v = UIStackView() + v.axis = .horizontal + v.backgroundColor = .white + v.layer.cornerRadius = 15 + v.spacing = 10 + v.isLayoutMarginsRelativeArrangement = true + + v.addTapAction { [weak self] in + Task { + await self?.interactor.didTapChangeNicknameButton() + } + } + return v + }() + + private lazy var changeImageView: UIImageView = { + let v = UIImageView() + v.image = .asset(.icon_edit) + v.contentMode = .scaleAspectFit return v }() - private lazy var myNameTagView: TTTagView = { - let v = TTTagView(textColor: .primary, fontSize: .body2, cornerRadius: 15) + private lazy var myNameTagLabel: UILabel = { + let v = UILabel() + v.textColor = .primary + v.font = .body2 return v }() @@ -99,13 +120,13 @@ final class MyInfoViewController: UIViewController { v.isScrollEnabled = false return v }() - + var signOutPopupView: TTPopup? - + var signOutCompletePopupView: TTPopup? - + var signOutCancelPopupView: TTPopup? - + var signoutCancelCompletePopupView: TTPopup? init(interactor: MyInfoBusinessLogic) { @@ -123,25 +144,30 @@ final class MyInfoViewController: UIViewController { super.viewDidLoad() self.setUI() + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Task { Loading.shared.showLoadingView() - await self.interactor.didLoad() + await self.interactor.didAppear() Loading.shared.stopLoadingView() } - - self.navigationController?.interactivePopGestureRecognizer?.delegate = self } // MARK: - Layout private func setUI() { self.view.setBackgroundDefault() - + self.nameStackView.addArrangedSubviews(self.myNicknameLabel, self.heartImageView, self.partnerNicknameLabel) + self.nicknameStackView.addArrangedSubviews(self.myNameTagLabel, self.changeImageView) self.view.addSubviews( self.navigationBar, self.mainImageView, self.nameStackView, self.challengeCountLabel, - self.myNameTagView, self.separator, self.tableView + self.nicknameStackView, self.separator, self.tableView ) self.navigationBar.snp.makeConstraints { make in @@ -152,17 +178,19 @@ final class MyInfoViewController: UIViewController { self.mainImageView.snp.makeConstraints { make in make.top.equalTo(self.navigationBar.snp.bottom) - make.leading.trailing.equalToSuperview().inset(113) - make.height.equalTo(UIScreen.main.bounds.height * 0.158) + make.centerX.equalToSuperview() + make.width.equalTo(149) + make.height.equalTo(129) } + self.nicknameStackView.layoutMargins = .init(top: 3, left: 10, bottom: 3, right: 10) self.nameStackView.snp.makeConstraints { make in make.top.equalTo(self.mainImageView.snp.bottom).offset(19) make.centerX.equalTo(self.mainImageView.snp.centerX) } - + self.heartImageView.snp.makeConstraints { make in - make.height.width.equalTo(10) + make.height.width.equalTo(14) } self.challengeCountLabel.snp.makeConstraints { make in @@ -170,14 +198,14 @@ final class MyInfoViewController: UIViewController { make.centerX.equalTo(self.mainImageView.snp.centerX) } - self.myNameTagView.snp.makeConstraints { make in + self.nicknameStackView.snp.makeConstraints { make in make.top.equalTo(self.challengeCountLabel.snp.bottom).offset(14) make.centerX.equalTo(self.mainImageView.snp.centerX) - make.height.equalTo(28) + make.height.equalTo(30) } self.separator.snp.makeConstraints { make in - make.top.equalTo(self.myNameTagView.snp.bottom).offset(34) + make.top.equalTo(self.myNameTagLabel.snp.bottom).offset(34) make.leading.trailing.equalToSuperview() } @@ -218,48 +246,47 @@ extension MyInfoViewController: MyInfoDisplayLogic { imageView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + let popupView = TTPopup() popupView.configure(title: MyInfo.ViewModel.SignOutViewModel.title, resultView: popupContentView, - description: MyInfo.ViewModel.SignOutViewModel.message, + warningText: MyInfo.ViewModel.SignOutViewModel.warning, buttonTitles: [ MyInfo.ViewModel.SignOutViewModel.cancelOptionText, MyInfo.ViewModel.SignOutViewModel.signOutOptionText ]) - + popupView.didTapLeftButton { Task { await self.interactor.didTapSignOutPopupCancelButton() } } - + popupView.didTapRightButton { Task { - try await Task.sleep(nanoseconds: 1000000000) await self.interactor.didTapSignoutPopupSignOutButton() } } - + popupView.didTapBackground { Task { await self.interactor.didTapSignOutPopupBackground() } } - + self.signOutPopupView = popupView - + if let signOutPopupView = self.signOutPopupView { self.view.addSubview(signOutPopupView) } } - + viewModel.dismiss.unwrap { self.signOutPopupView?.removeFromSuperview() self.signOutPopupView = nil } } - + func displaySignOutCompletePopup(viewModel: MyInfo.ViewModel.SignOutCompletedViewModel) { viewModel.show.unwrap { let popupContentView = UIView() @@ -270,7 +297,7 @@ extension MyInfoViewController: MyInfoDisplayLogic { imageView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + let popupView = TTPopup() popupView.configure(title: MyInfo.ViewModel.SignOutCompletedViewModel.title, resultView: popupContentView, @@ -278,129 +305,33 @@ extension MyInfoViewController: MyInfoDisplayLogic { buttonTitles: [ MyInfo.ViewModel.SignOutCompletedViewModel.confirmOptionText ]) - + popupView.didTapLeftButton { Task { await self.interactor.didTapSignOutCompleteConfirmButton() } } - - + + popupView.didTapBackground { Task { await self.interactor.didTapSignOutCompletePopupBackground() } } - + self.signOutCompletePopupView = popupView - + if let signOutCompletePopupView = self.signOutCompletePopupView { self.view.addSubview(signOutCompletePopupView) } } - + viewModel.dismiss.unwrap { self.signOutCompletePopupView?.removeFromSuperview() self.signOutCompletePopupView = nil } } - - func displaySignOutCancelPopup(viewModel: MyInfo.ViewModel.SignOutCancelViewModel) { - viewModel.show.unwrap { - let popupContentView = UIView() - let imageView = UIImageView() - imageView.image = $0 - imageView.contentMode = .scaleAspectFit - popupContentView.addSubview(imageView) - imageView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - let popupView = TTPopup() - popupView.configure(title: MyInfo.ViewModel.SignOutCancelViewModel.title, - resultView: popupContentView, - description: MyInfo.ViewModel.SignOutCancelViewModel.message, - buttonTitles: [ - MyInfo.ViewModel.SignOutCancelViewModel.noOptionText, - MyInfo.ViewModel.SignOutCancelViewModel.SingOutCancelOptionText - ]) - - popupView.didTapLeftButton { - Task { - await self.interactor.didTapSignOutCancelCompleteNobutton() - } - } - - popupView.didTapRightButton { - Task { - try await Task.sleep(nanoseconds: 1000000000) - await self.interactor.didTapCancelSignOutCancelButton() - } - } - - popupView.didTapBackground { - Task { - await self.interactor.didTapSignOutCancelPopupBackground() - } - } - - self.signOutCancelPopupView = popupView - - if let signOutCancelPopupView = self.signOutCancelPopupView { - self.view.addSubview(signOutCancelPopupView) - } - } - - viewModel.dismiss.unwrap { - self.signOutCancelPopupView?.removeFromSuperview() - self.signOutCancelPopupView = nil - } - } - - func displaySignOutCancelCompletePopup(viewModel: MyInfo.ViewModel.SignOutCancelCompletedViewModel) { - viewModel.show.unwrap { - let popupContentView = UIView() - let imageView = UIImageView() - imageView.image = $0 - imageView.contentMode = .scaleAspectFit - popupContentView.addSubview(imageView) - imageView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - - let popupView = TTPopup() - popupView.configure(title: MyInfo.ViewModel.SignOutCancelCompletedViewModel.title, - resultView: popupContentView, - description: MyInfo.ViewModel.SignOutCancelCompletedViewModel.message, - buttonTitles: [ - MyInfo.ViewModel.SignOutCancelCompletedViewModel.confirmOptionText - ]) - - popupView.didTapLeftButton { - Task { - await self.interactor.didTapSignOutCancelCompleteConfirmButton() - } - } - - popupView.didTapBackground { - Task { - await self.interactor.didTapSignOutCancelCompletePopupBackground() - } - } - - self.signoutCancelCompletePopupView = popupView - - if let signoutCancelCompletePopupView = self.signoutCancelCompletePopupView { - self.view.addSubview(signoutCancelCompletePopupView) - } - } - - viewModel.dismiss.unwrap { - self.signoutCancelCompletePopupView?.removeFromSuperview() - self.signoutCancelCompletePopupView = nil - } - } - + func displayLists(viewModel: MyInfo.ViewModel.Lists) { UIView.transition( with: self.view, @@ -418,7 +349,7 @@ extension MyInfoViewController: MyInfoDisplayLogic { completion: nil ) } - + func displayMyInfo(viewModel: MyInfo.ViewModel.Data) { UIView.transition( with: self.view, @@ -431,12 +362,12 @@ extension MyInfoViewController: MyInfoDisplayLogic { self.challengeCountLabel.text = viewModel.challengeTotalCount self.myNicknameLabel.text = viewModel.myNickname self.partnerNicknameLabel.text = viewModel.partnerNickname - self.myNameTagView.titleLabel.text = viewModel.myNickname + self.myNameTagLabel.text = viewModel.myNickname }, completion: nil ) } - + func displayToast(viewModel: MyInfo.ViewModel.Toast) { viewModel.message.unwrap { Toast.shared.makeToast($0) @@ -456,7 +387,7 @@ extension MyInfoViewController: UITableViewDataSource, UITableViewDelegate { cell.configure(text: self.myInfoLists[indexPath.row].title) return cell } - + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { Task { diff --git a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoWorker.swift b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoWorker.swift index 482ecd32..4887f7f4 100644 --- a/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoWorker.swift +++ b/Scene/MyInfoScene/Sources/MyInfoScene/MyInfoWorker.swift @@ -13,35 +13,30 @@ protocol MyInfoWorkerProtocol { func fetchMypageInfo() async throws -> MyInfo.Model.Data /// 로그아웃 func logout() async - /// 소셜로그인 타입 조회 - func fetchSocialLoginType() -> MyInfo.Model.SocialLoginStatus - /// 애플 로그인 재인증 - func retryAppleLogin() async throws - /// 회원탈퇴 요청 상태 세팅 - func setSignoutStatus(required: Bool, socialType: MyInfo.Model.SocialLoginStatus) - /// 카카오 회원탈퇴 요청 상태 - func fetchKakaoSignOutStatus() -> Bool - /// 애플 회원탈퇴 요청 상태 - func fetchAppleSignOutStatus() -> Bool + /// 회원탈퇴 + func signOut() async throws } final class MyInfoWorker: MyInfoWorkerProtocol { - + var meLocalWorker: MeLocalWorkerProtocol + var invitationLocalWorker: InvitationLocalWorkerProtocol + var invitedUserLocalWorker: InvitedUserLocalWorkerProtocol var meNetworkWorker: MeNetworkWorkerProtocol - var appleLoginWorker: AppleLoginWorkerProtocol - var myInfoLocalWorker: MyInfoLocalWorkerProtocol + var signOutNetworkWorker: SignOutNetworkWorkerProtocol init( meLocalWorker: MeLocalWorkerProtocol, + invitationLocalWorker: InvitationLocalWorkerProtocol, + invitedUserLocalWorker: InvitedUserLocalWorkerProtocol, meNetworkWorker: MeNetworkWorkerProtocol, - appleLoginWorker: AppleLoginWorkerProtocol, - myInfoLocalWorker: MyInfoLocalWorkerProtocol + signOutNetworkWorker: SignOutNetworkWorkerProtocol ) { self.meLocalWorker = meLocalWorker + self.invitationLocalWorker = invitationLocalWorker + self.invitedUserLocalWorker = invitedUserLocalWorker self.meNetworkWorker = meNetworkWorker - self.appleLoginWorker = appleLoginWorker - self.myInfoLocalWorker = myInfoLocalWorker + self.signOutNetworkWorker = signOutNetworkWorker } func fetchMypageInfo() async throws -> MyInfo.Model.Data { @@ -57,33 +52,16 @@ final class MyInfoWorker: MyInfoWorkerProtocol { func logout() async { self.meLocalWorker.token = "" + self.invitationLocalWorker.isInvitationSend = false + self.invitationLocalWorker.invitationLink = "" + self.invitedUserLocalWorker.invitedUser = "" + self.invitedUserLocalWorker.invitedUserNo = 0 } - func fetchSocialLoginType() -> MyInfo.Model.SocialLoginStatus { - return .init(rawValue: self.meLocalWorker.socialType ?? "") ?? .appleLogin - } - - func retryAppleLogin() async throws { - _ = try await self.appleLoginWorker.retryAppleLogin() - } - - func setSignoutStatus(required: Bool, socialType: MyInfo.Model.SocialLoginStatus) { - switch socialType { - case .kakaoLogin: - self.myInfoLocalWorker.kakaoSignOutRequestCompleted = required - - case .appleLogin: - self.myInfoLocalWorker.appleSignOutRequestCompleted = required - } - } - - func fetchKakaoSignOutStatus() -> Bool { - guard let requestCompletedStatus = self.myInfoLocalWorker.kakaoSignOutRequestCompleted else { return true} - return requestCompletedStatus - } - - func fetchAppleSignOutStatus() -> Bool { - guard let requestCompletedStatus = self.myInfoLocalWorker.appleSignOutRequestCompleted else { return true} - return requestCompletedStatus + func signOut() async throws { + + _ = try await self.signOutNetworkWorker.requestSignOut() + + await self.logout() } } diff --git a/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistViewController.swift b/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistViewController.swift index f5659870..f1e1e59f 100644 --- a/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistViewController.swift +++ b/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistViewController.swift @@ -152,7 +152,7 @@ final class NicknameRegistViewController: UIViewController { make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().inset(24) make.height.equalTo(57) - make.bottom.equalTo(guide.snp.bottom) + make.bottom.equalTo(guide.snp.bottom).inset(20) } } @@ -205,7 +205,7 @@ extension NicknameRegistViewController: KeyboardDelegate { make.top.equalTo(self.titleLabel.snp.bottom).offset(10) } self.confirmButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(keyboardFrame.height - 10) + make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(keyboardFrame.height + 20) } self.view.layoutIfNeeded() } @@ -217,7 +217,7 @@ extension NicknameRegistViewController: KeyboardDelegate { make.top.equalTo(self.titleLabel.snp.bottom).offset(35) } self.confirmButton.snp.updateConstraints { make in - make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom) + make.bottom.equalTo(self.view.safeAreaLayoutGuide.snp.bottom).inset(20) } self.view.layoutIfNeeded() } diff --git a/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistWorker.swift b/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistWorker.swift index 6d8651c7..081be230 100644 --- a/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistWorker.swift +++ b/Scene/NicknameRegistScene/Sources/NicknameRegistScene/NicknameRegistWorker.swift @@ -35,6 +35,7 @@ final class NicknameRegistWorker: NicknameRegistWorkerProtocol { var invitedUser: NicknameRegist.Model.InvitedUser? { if let user: String = self.invitedUserLocalWorker.invitedUser, + !user.isEmpty, let userNo: Int = self.invitedUserLocalWorker.invitedUserNo { if self.meLocalWorker.userNo == userNo { return nil diff --git a/Scene/NudgeSendScene/Sources/NudgeSendScene/NudgeSendViewController.swift b/Scene/NudgeSendScene/Sources/NudgeSendScene/NudgeSendViewController.swift index f6f71ded..0d0c0035 100644 --- a/Scene/NudgeSendScene/Sources/NudgeSendScene/NudgeSendViewController.swift +++ b/Scene/NudgeSendScene/Sources/NudgeSendScene/NudgeSendViewController.swift @@ -53,7 +53,9 @@ final class NudgeSendViewController: UIViewController, BottomSheetViewController let v = TTPrimaryButton.create(title: "보내기", .large) v.didTapButton { [weak self] in Task { + Loading.shared.showLoadingView() await self?.interactor.didTapSendButton() + Loading.shared.stopLoadingView() } } v.setIsEnabled(false) diff --git a/TwoToo.xcodeproj/project.pbxproj b/TwoToo.xcodeproj/project.pbxproj index 40df33c4..362162e4 100644 --- a/TwoToo.xcodeproj/project.pbxproj +++ b/TwoToo.xcodeproj/project.pbxproj @@ -8,9 +8,6 @@ /* Begin PBXBuildFile section */ 583509D22A789ADD00C8D5BC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 583509D12A789ADD00C8D5BC /* Assets.xcassets */; }; - A322FC132A455F2900DAB491 /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = A322FC122A455F2900DAB491 /* FloatingPanel */; }; - A32AC9632A417ED40086EE5B /* NaviTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A32AC9622A417ED40086EE5B /* NaviTestViewController.swift */; }; - C8396CC02A2C40BC00A79E98 /* SceneKit in Frameworks */ = {isa = PBXBuildFile; productRef = C8396CBF2A2C40BC00A79E98 /* SceneKit */; }; C85CB5742A755AA100968E67 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C85CB5732A755AA100968E67 /* GoogleService-Info.plist */; }; C87E317A2A2B041900FD31C8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87E31792A2B041900FD31C8 /* AppDelegate.swift */; }; C87E317C2A2B041900FD31C8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87E317B2A2B041900FD31C8 /* SceneDelegate.swift */; }; @@ -18,12 +15,12 @@ C87E31812A2B041900FD31C8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C87E317F2A2B041900FD31C8 /* Main.storyboard */; }; C87E31862A2B041A00FD31C8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C87E31842A2B041A00FD31C8 /* LaunchScreen.storyboard */; }; C87E31902A2B435B00FD31C8 /* SplashScene in Resources */ = {isa = PBXBuildFile; fileRef = C87E318F2A2B435B00FD31C8 /* SplashScene */; }; + C8DB2B472ADA423A00FD85D9 /* SceneKit in Frameworks */ = {isa = PBXBuildFile; productRef = C8DB2B462ADA423A00FD85D9 /* SceneKit */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 583509D12A789ADD00C8D5BC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 641E51372A5A4ED0002C8561 /* TwoToo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TwoToo.entitlements; sourceTree = ""; }; - A32AC9622A417ED40086EE5B /* NaviTestViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaviTestViewController.swift; sourceTree = ""; }; C8396CBD2A2C3F2500A79E98 /* SceneKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = SceneKit; sourceTree = ""; }; C8396CBE2A2C3FA600A79E98 /* ChallengeCreateFinishScene */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ChallengeCreateFinishScene; sourceTree = ""; }; C85CB5732A755AA100968E67 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; @@ -65,8 +62,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C8396CC02A2C40BC00A79E98 /* SceneKit in Frameworks */, - A322FC132A455F2900DAB491 /* FloatingPanel in Frameworks */, + C8DB2B472ADA423A00FD85D9 /* SceneKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,7 +95,6 @@ 641E51372A5A4ED0002C8561 /* TwoToo.entitlements */, C87E31792A2B041900FD31C8 /* AppDelegate.swift */, C87E317B2A2B041900FD31C8 /* SceneDelegate.swift */, - A32AC9622A417ED40086EE5B /* NaviTestViewController.swift */, C87E317D2A2B041900FD31C8 /* ViewController.swift */, C87E317F2A2B041900FD31C8 /* Main.storyboard */, C87E31842A2B041A00FD31C8 /* LaunchScreen.storyboard */, @@ -166,6 +161,7 @@ C87E31722A2B041900FD31C8 /* Sources */, C87E31732A2B041900FD31C8 /* Frameworks */, C87E31742A2B041900FD31C8 /* Resources */, + C8DB2B452ADA3FDA00FD85D9 /* Run Script */, ); buildRules = ( ); @@ -173,8 +169,7 @@ ); name = TwoToo; packageProductDependencies = ( - C8396CBF2A2C40BC00A79E98 /* SceneKit */, - A322FC122A455F2900DAB491 /* FloatingPanel */, + C8DB2B462ADA423A00FD85D9 /* SceneKit */, ); productName = TwoToo; productReference = C87E31762A2B041900FD31C8 /* TwoToo.app */; @@ -205,7 +200,6 @@ ); mainGroup = C87E316D2A2B041900FD31C8; packageReferences = ( - A322FC112A455F2900DAB491 /* XCRemoteSwiftPackageReference "FloatingPanel" */, ); productRefGroup = C87E31772A2B041900FD31C8 /* Products */; projectDirPath = ""; @@ -231,12 +225,34 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + C8DB2B452ADA3FDA00FD85D9 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}", + "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)", + ); + name = "Run Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\" ${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ C87E31722A2B041900FD31C8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A32AC9632A417ED40086EE5B /* NaviTestViewController.swift in Sources */, C87E317E2A2B041900FD31C8 /* ViewController.swift in Sources */, C87E317A2A2B041900FD31C8 /* AppDelegate.swift in Sources */, C87E317C2A2B041900FD31C8 /* SceneDelegate.swift in Sources */, @@ -389,6 +405,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = MRMBCYN83C; GENERATE_INFOPLIST_FILE = YES; @@ -406,14 +423,17 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = "kr.mash-up.TwoToo"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Dev TwoToo"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -444,14 +464,17 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = "kr.mash-up.TwoToo"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Dev TwoToo"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -478,24 +501,8 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - A322FC112A455F2900DAB491 /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scenee/FloatingPanel"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ - A322FC122A455F2900DAB491 /* FloatingPanel */ = { - isa = XCSwiftPackageProductDependency; - package = A322FC112A455F2900DAB491 /* XCRemoteSwiftPackageReference "FloatingPanel" */; - productName = FloatingPanel; - }; - C8396CBF2A2C40BC00A79E98 /* SceneKit */ = { + C8DB2B462ADA423A00FD85D9 /* SceneKit */ = { isa = XCSwiftPackageProductDependency; productName = SceneKit; }; diff --git a/TwoToo.xcodeproj/xcshareddata/xcschemes/TwoToo.xcscheme b/TwoToo.xcodeproj/xcshareddata/xcschemes/TwoToo.xcscheme index a44929bc..884b9169 100644 --- a/TwoToo.xcodeproj/xcshareddata/xcschemes/TwoToo.xcscheme +++ b/TwoToo.xcodeproj/xcshareddata/xcschemes/TwoToo.xcscheme @@ -49,6 +49,16 @@ ReferencedContainer = "container:TwoToo.xcodeproj"> + + + + + + STORAGE_BUCKET twotoo-2b7e7.appspot.com IS_ADS_ENABLED - + IS_ANALYTICS_ENABLED - + IS_APPINVITE_ENABLED IS_GCM_ENABLED diff --git a/TwoToo/Info.plist b/TwoToo/Info.plist index f1470ad8..ee136e8c 100644 --- a/TwoToo/Info.plist +++ b/TwoToo/Info.plist @@ -27,8 +27,8 @@ - FirebaseAppDelegateProxyEnabled - No + FirebaseAppDelegateProxyEnabled + No FirebaseDynamicLinksCustomDomains https://twotoo.page.link diff --git a/TwoToo/NaviTestViewController.swift b/TwoToo/NaviTestViewController.swift deleted file mode 100644 index 531353f0..00000000 --- a/TwoToo/NaviTestViewController.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// NaviTestViewController.swift -// TwoToo -// -// Created by Julia on 2023/06/20. -// - -import CoreKit -import SceneKit -import UIKit -import DesignSystem - -public class NaviTestViewController: UIViewController { - - lazy var naviView: TTNavigationBar = { - let v = TTNavigationBar(title: "Twotoo", - rightButtonImage: .asset(.icon_cancel)) - v.delegate = self - return v - }() - - lazy var navi2View: TTNavigationBar = { - let v = TTNavigationBar(title: "마이페이지", - rightButtonImage: .asset(.icon_info)) - v.delegate = self - return v - }() - - lazy var navi3View: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "상세", - leftButtonImage: .asset(.icon_back), - rightButtonImage: .asset(.icon_more)) - v.delegate = self - return v - }() - - lazy var navi4View: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: nil, - leftButtonImage: .asset(.icon_back), - rightButtonImage: nil) - v.delegate = self - return v - }() - - lazy var navi5View: TTNavigationDetailBar = { - let v = TTNavigationDetailBar(title: "Hello", - leftButtonImage: .asset(.icon_more), - rightButtonImage: nil) - v.delegate = self - return v - }() - - public override func viewDidLoad() { - super.viewDidLoad() - - self.view.backgroundColor = .systemBackground - - self.view.addSubview(self.naviView) - self.view.addSubview(self.navi2View) - self.view.addSubview(self.navi3View) - self.view.addSubview(self.navi4View) - self.view.addSubview(self.navi5View) - - self.naviView.snp.makeConstraints { make in - make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) - make.leading.trailing.equalToSuperview().inset(10) - make.height.equalTo(40) - } - - self.navi2View.snp.makeConstraints { make in - make.top.equalTo(self.naviView.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(10) - make.height.equalTo(40) - } - - self.navi3View.snp.makeConstraints { make in - make.top.equalTo(self.navi2View.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(10) - make.height.equalTo(40) - } - - self.navi4View.snp.makeConstraints { make in - make.top.equalTo(self.navi3View.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(10) - make.height.equalTo(40) - } - - self.navi5View.snp.makeConstraints { make in - make.top.equalTo(self.navi4View.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(10) - make.height.equalTo(40) - } - } -} - - -extension NaviTestViewController: TTNavigationBarDelegate{ - public func didTapDetailLeftButton() { - print("Detail More button") - } - - public func didTapDetailRightButton() { - print("Detail Right button") - } -} - -extension NaviTestViewController: TTNavigationDetailBarDelegate{ - public func tapBackButton() { - print("Back button") - } - - public func didTapRightButton() { - print("Info button") - } -}