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