diff --git a/backend/core/build.gradle b/backend/core/build.gradle index b7f74397..f7009086 100644 --- a/backend/core/build.gradle +++ b/backend/core/build.gradle @@ -22,19 +22,20 @@ repositories { } dependencies { - //jwt implementation 'com.auth0:java-jwt:4.2.1' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' + implementation 'org.springframework.boot:spring-boot-starter-amqp' implementation 'org.mapstruct:mapstruct:1.5.5.Final' implementation 'org.jsoup:jsoup:1.15.3' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final' diff --git a/backend/core/src/main/java/com/rollthedice/backend/global/config/SwaggerConfig.java b/backend/core/src/main/java/com/rollthedice/backend/global/config/SwaggerConfig.java new file mode 100644 index 00000000..df2e83ef --- /dev/null +++ b/backend/core/src/main/java/com/rollthedice/backend/global/config/SwaggerConfig.java @@ -0,0 +1,51 @@ +package com.rollthedice.backend.global.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class SwaggerConfig { + private final String JWT = "JWT"; + private final String BEARER = "Bearer"; + + @Bean + public OpenAPI openAPI() { + SecurityRequirement securityRequirement = new SecurityRequirement().addList(JWT); + return new OpenAPI() + .servers(getServers()) + .info(getInfo()) + .addSecurityItem(securityRequirement) + .components(getComponents()); + } + + private List getServers() { + return List.of(new Server() + .url("/api") + .description("백엔드 api 서버") + ); + } + + private Info getInfo() { + return new Info() + .title("Roll The Dice API") + .description("요약된 뉴스를 AR로 재미있게 즐기는 서비스") + .version("demo"); + } + + private Components getComponents() { + return new Components().addSecuritySchemes(JWT, new SecurityScheme() + .name(JWT) + .type(SecurityScheme.Type.HTTP) + .scheme(BEARER) + .bearerFormat(JWT) + ); + } +} diff --git a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj index 9638b79e..be50979e 100644 --- a/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj +++ b/iOS/RollTheDice/RollTheDice.xcodeproj/project.pbxproj @@ -7,24 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 352EE6832B76103400E51B60 /* ChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352EE6822B76103400E51B60 /* ChatTitleView.swift */; }; - 352EE6872B76120500E51B60 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352EE6862B76120500E51B60 /* ChatModel.swift */; }; - 35AA272F2B79FCAE004EFFAE /* ChatExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA272E2B79FCAE004EFFAE /* ChatExampleView.swift */; }; - 35AA27312B79FCF3004EFFAE /* ChatExampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27302B79FCF3004EFFAE /* ChatExampleViewModel.swift */; }; - 35AA27342B79FE70004EFFAE /* ChatError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27332B79FE70004EFFAE /* ChatError.swift */; }; - 35AA27362B79FE7F004EFFAE /* MockUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27352B79FE7F004EFFAE /* MockUser.swift */; }; - 35AA27382B79FE96004EFFAE /* MockMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27372B79FE96004EFFAE /* MockMessage.swift */; }; - 35AA273A2B79FEAA004EFFAE /* MockAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27392B79FEAA004EFFAE /* MockAttachment.swift */; }; - 35AA273E2B79FED7004EFFAE /* Lorem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA273D2B79FED7004EFFAE /* Lorem.swift */; }; - 35AA27402B79FF5D004EFFAE /* MockChatData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA273F2B79FF5D004EFFAE /* MockChatData.swift */; }; - 35AA27432B79FF91004EFFAE /* ChatInteractorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27422B79FF91004EFFAE /* ChatInteractorProtocol.swift */; }; - 35AA27452B79FFAB004EFFAE /* MockChatInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27442B79FFAB004EFFAE /* MockChatInteractor.swift */; }; - 35AA27492B79FFF4004EFFAE /* Date+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27482B79FFF4004EFFAE /* Date+Random.swift */; }; - 35AA274D2B7A09CA004EFFAE /* ChattingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA274C2B7A09CA004EFFAE /* ChattingView.swift */; }; - 35AA27522B7A1420004EFFAE /* AIChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27512B7A1420004EFFAE /* AIChatView.swift */; }; - 35AA27542B7A1489004EFFAE /* AIChatCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27532B7A1489004EFFAE /* AIChatCustomView.swift */; }; - 35AA27562B7A1495004EFFAE /* MyChatCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27552B7A1495004EFFAE /* MyChatCustomView.swift */; }; - 35AA27582B7A1A34004EFFAE /* AIChatTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35AA27572B7A1A34004EFFAE /* AIChatTitleView.swift */; }; 35C71BF22B79F39900F777D1 /* ExyteChat in Frameworks */ = {isa = PBXBuildFile; productRef = 35C71BF12B79F39900F777D1 /* ExyteChat */; }; 6C32379F2B7C376D00B699AB /* Bookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C32379E2B7C376D00B699AB /* Bookmark.swift */; }; 6C3237A12B7C377600B699AB /* BookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3237A02B7C377600B699AB /* BookmarkViewModel.swift */; }; @@ -36,6 +18,13 @@ 6C3237B22B7C385000B699AB /* NewsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3237B12B7C385000B699AB /* NewsListViewModel.swift */; }; 6C3237B52B7C433D00B699AB /* ChatTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3237B42B7C433D00B699AB /* ChatTypeView.swift */; }; 6C3237B72B7C434600B699AB /* ChatType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3237B62B7C434600B699AB /* ChatType.swift */; }; + 6C454A782B9DA657006FD9D0 /* SignUpQuestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A772B9DA657006FD9D0 /* SignUpQuestionView.swift */; }; + 6C454A7A2B9DA67C006FD9D0 /* SignUpViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A792B9DA67C006FD9D0 /* SignUpViewModel.swift */; }; + 6C454A7C2B9DA71C006FD9D0 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A7B2B9DA71C006FD9D0 /* SignUpView.swift */; }; + 6C454A7E2B9DAA3F006FD9D0 /* SignUpFinishView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A7D2B9DAA3F006FD9D0 /* SignUpFinishView.swift */; }; + 6C454A822B9DAFA3006FD9D0 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A812B9DAFA3006FD9D0 /* Path.swift */; }; + 6C454A842B9DAFCB006FD9D0 /* PathType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A832B9DAFCB006FD9D0 /* PathType.swift */; }; + 6C454A882B9DB6C2006FD9D0 /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C454A872B9DB6C2006FD9D0 /* CustomNavigationBar.swift */; }; 6C77048C2B722686001B17CB /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C77048B2B722686001B17CB /* MainTabView.swift */; }; 6C77048F2B7229B1001B17CB /* NewsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C77048E2B7229B1001B17CB /* NewsListView.swift */; }; 6C7704992B722A20001B17CB /* MainTabViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7704982B722A20001B17CB /* MainTabViewModel.swift */; }; @@ -45,31 +34,28 @@ 6C7704A12B722CEB001B17CB /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7704A02B722CEB001B17CB /* ProfileView.swift */; }; 6C7704A32B722D3B001B17CB /* ColorAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6C7704A22B722D3B001B17CB /* ColorAssets.xcassets */; }; 6C7704A52B723104001B17CB /* ImageAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6C7704A42B723104001B17CB /* ImageAssets.xcassets */; }; + 6CA901962BA2EC0100E20259 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CA901952BA2EC0100E20259 /* Font.swift */; }; 6CC4DDC72B5574670080E7E8 /* RollTheDiceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC4DDC62B5574670080E7E8 /* RollTheDiceApp.swift */; }; 6CC4DDC92B5574670080E7E8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CC4DDC82B5574670080E7E8 /* ContentView.swift */; }; 6CC4DDCB2B5574690080E7E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CC4DDCA2B5574690080E7E8 /* Assets.xcassets */; }; 6CC4DDCE2B5574690080E7E8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CC4DDCD2B5574690080E7E8 /* Preview Assets.xcassets */; }; + 6CDB29C92BA97C550081037B /* Pretendard-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C02BA97C550081037B /* Pretendard-Black.otf */; }; + 6CDB29CA2BA97C550081037B /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C12BA97C550081037B /* Pretendard-ExtraBold.otf */; }; + 6CDB29CB2BA97C550081037B /* Pretendard-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C22BA97C550081037B /* Pretendard-Regular.otf */; }; + 6CDB29CC2BA97C550081037B /* Pretendard-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C32BA97C550081037B /* Pretendard-Thin.otf */; }; + 6CDB29CD2BA97C550081037B /* Pretendard-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C42BA97C550081037B /* Pretendard-SemiBold.otf */; }; + 6CDB29CE2BA97C550081037B /* Pretendard-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C52BA97C550081037B /* Pretendard-ExtraLight.otf */; }; + 6CDB29CF2BA97C550081037B /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C62BA97C550081037B /* Pretendard-Bold.otf */; }; + 6CDB29D02BA97C550081037B /* Pretendard-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C72BA97C550081037B /* Pretendard-Medium.otf */; }; + 6CDB29D12BA97C550081037B /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6CDB29C82BA97C550081037B /* Pretendard-Light.otf */; }; + 6CDB29F92BAA07350081037B /* GPTChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDB29F82BAA07350081037B /* GPTChat.swift */; }; + 6CDB29FB2BAA07B10081037B /* GPTChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDB29FA2BAA07B10081037B /* GPTChatViewModel.swift */; }; + 6CDB29FD2BAA07FD0081037B /* GPTChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDB29FC2BAA07FD0081037B /* GPTChatView.swift */; }; + 6CDB29FF2BAA08280081037B /* GPTChatListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CDB29FE2BAA08280081037B /* GPTChatListViewModel.swift */; }; + 6CDB2A022BAA085A0081037B /* OpenAI in Frameworks */ = {isa = PBXBuildFile; productRef = 6CDB2A012BAA085A0081037B /* OpenAI */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 352EE6822B76103400E51B60 /* ChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTitleView.swift; sourceTree = ""; }; - 352EE6862B76120500E51B60 /* ChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatModel.swift; sourceTree = ""; }; - 35AA272E2B79FCAE004EFFAE /* ChatExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatExampleView.swift; sourceTree = ""; }; - 35AA27302B79FCF3004EFFAE /* ChatExampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatExampleViewModel.swift; sourceTree = ""; }; - 35AA27332B79FE70004EFFAE /* ChatError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatError.swift; sourceTree = ""; }; - 35AA27352B79FE7F004EFFAE /* MockUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUser.swift; sourceTree = ""; }; - 35AA27372B79FE96004EFFAE /* MockMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessage.swift; sourceTree = ""; }; - 35AA27392B79FEAA004EFFAE /* MockAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAttachment.swift; sourceTree = ""; }; - 35AA273D2B79FED7004EFFAE /* Lorem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lorem.swift; sourceTree = ""; }; - 35AA273F2B79FF5D004EFFAE /* MockChatData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockChatData.swift; sourceTree = ""; }; - 35AA27422B79FF91004EFFAE /* ChatInteractorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInteractorProtocol.swift; sourceTree = ""; }; - 35AA27442B79FFAB004EFFAE /* MockChatInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockChatInteractor.swift; sourceTree = ""; }; - 35AA27482B79FFF4004EFFAE /* Date+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Random.swift"; sourceTree = ""; }; - 35AA274C2B7A09CA004EFFAE /* ChattingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChattingView.swift; sourceTree = ""; }; - 35AA27512B7A1420004EFFAE /* AIChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIChatView.swift; sourceTree = ""; }; - 35AA27532B7A1489004EFFAE /* AIChatCustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIChatCustomView.swift; sourceTree = ""; }; - 35AA27552B7A1495004EFFAE /* MyChatCustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyChatCustomView.swift; sourceTree = ""; }; - 35AA27572B7A1A34004EFFAE /* AIChatTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIChatTitleView.swift; sourceTree = ""; }; 6C32379E2B7C376D00B699AB /* Bookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bookmark.swift; sourceTree = ""; }; 6C3237A02B7C377600B699AB /* BookmarkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkViewModel.swift; sourceTree = ""; }; 6C3237A42B7C37D100B699AB /* BookmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkView.swift; sourceTree = ""; }; @@ -80,6 +66,13 @@ 6C3237B12B7C385000B699AB /* NewsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsListViewModel.swift; sourceTree = ""; }; 6C3237B42B7C433D00B699AB /* ChatTypeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTypeView.swift; sourceTree = ""; }; 6C3237B62B7C434600B699AB /* ChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatType.swift; sourceTree = ""; }; + 6C454A772B9DA657006FD9D0 /* SignUpQuestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpQuestionView.swift; sourceTree = ""; }; + 6C454A792B9DA67C006FD9D0 /* SignUpViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewModel.swift; sourceTree = ""; }; + 6C454A7B2B9DA71C006FD9D0 /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; + 6C454A7D2B9DAA3F006FD9D0 /* SignUpFinishView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpFinishView.swift; sourceTree = ""; }; + 6C454A812B9DAFA3006FD9D0 /* Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Path.swift; sourceTree = ""; }; + 6C454A832B9DAFCB006FD9D0 /* PathType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathType.swift; sourceTree = ""; }; + 6C454A872B9DB6C2006FD9D0 /* CustomNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; 6C77048B2B722686001B17CB /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = ""; }; 6C77048E2B7229B1001B17CB /* NewsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsListView.swift; sourceTree = ""; }; 6C7704982B722A20001B17CB /* MainTabViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabViewModel.swift; sourceTree = ""; }; @@ -89,11 +82,26 @@ 6C7704A02B722CEB001B17CB /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 6C7704A22B722D3B001B17CB /* ColorAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ColorAssets.xcassets; sourceTree = ""; }; 6C7704A42B723104001B17CB /* ImageAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ImageAssets.xcassets; sourceTree = ""; }; + 6CA901952BA2EC0100E20259 /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; 6CC4DDC32B5574670080E7E8 /* RollTheDice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RollTheDice.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6CC4DDC62B5574670080E7E8 /* RollTheDiceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RollTheDiceApp.swift; sourceTree = ""; }; 6CC4DDC82B5574670080E7E8 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 6CC4DDCA2B5574690080E7E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 6CC4DDCD2B5574690080E7E8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6CDB29BF2BA9735C0081037B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 6CDB29C02BA97C550081037B /* Pretendard-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Black.otf"; sourceTree = ""; }; + 6CDB29C12BA97C550081037B /* Pretendard-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-ExtraBold.otf"; sourceTree = ""; }; + 6CDB29C22BA97C550081037B /* Pretendard-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Regular.otf"; sourceTree = ""; }; + 6CDB29C32BA97C550081037B /* Pretendard-Thin.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Thin.otf"; sourceTree = ""; }; + 6CDB29C42BA97C550081037B /* Pretendard-SemiBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-SemiBold.otf"; sourceTree = ""; }; + 6CDB29C52BA97C550081037B /* Pretendard-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-ExtraLight.otf"; sourceTree = ""; }; + 6CDB29C62BA97C550081037B /* Pretendard-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.otf"; sourceTree = ""; }; + 6CDB29C72BA97C550081037B /* Pretendard-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Medium.otf"; sourceTree = ""; }; + 6CDB29C82BA97C550081037B /* Pretendard-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Light.otf"; sourceTree = ""; }; + 6CDB29F82BAA07350081037B /* GPTChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPTChat.swift; sourceTree = ""; }; + 6CDB29FA2BAA07B10081037B /* GPTChatViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPTChatViewModel.swift; sourceTree = ""; }; + 6CDB29FC2BAA07FD0081037B /* GPTChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPTChatView.swift; sourceTree = ""; }; + 6CDB29FE2BAA08280081037B /* GPTChatListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPTChatListViewModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,6 +109,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6CDB2A022BAA085A0081037B /* OpenAI in Frameworks */, 35C71BF22B79F39900F777D1 /* ExyteChat in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -108,135 +117,84 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 352EE6842B7611E900E51B60 /* View */ = { - isa = PBXGroup; - children = ( - 35AA27502B7A1412004EFFAE /* AIChat */, - 35AA272D2B79FC77004EFFAE /* ChatTestView */, - ); - path = View; - sourceTree = ""; - }; - 352EE6852B7611F500E51B60 /* Model */ = { - isa = PBXGroup; - children = ( - 352EE6862B76120500E51B60 /* ChatModel.swift */, - ); - path = Model; - sourceTree = ""; - }; - 35AA272D2B79FC77004EFFAE /* ChatTestView */ = { - isa = PBXGroup; - children = ( - 35AA27472B79FFE5004EFFAE /* Extension */, - 35AA27462B79FFD5004EFFAE /* View */, - 35AA273B2B79FEBB004EFFAE /* Drivers */, - 35AA27322B79FE64004EFFAE /* Data */, - ); - path = ChatTestView; - sourceTree = ""; - }; - 35AA27322B79FE64004EFFAE /* Data */ = { - isa = PBXGroup; - children = ( - 35AA27332B79FE70004EFFAE /* ChatError.swift */, - 35AA27352B79FE7F004EFFAE /* MockUser.swift */, - 35AA27372B79FE96004EFFAE /* MockMessage.swift */, - 35AA27392B79FEAA004EFFAE /* MockAttachment.swift */, - ); - path = Data; - sourceTree = ""; - }; - 35AA273B2B79FEBB004EFFAE /* Drivers */ = { - isa = PBXGroup; - children = ( - 35AA27412B79FF7B004EFFAE /* Chat */, - 35AA273C2B79FEC5004EFFAE /* Data Generation */, - ); - path = Drivers; - sourceTree = ""; - }; - 35AA273C2B79FEC5004EFFAE /* Data Generation */ = { + 6C32379D2B7C374E00B699AB /* BookmarkCard */ = { isa = PBXGroup; children = ( - 35AA273D2B79FED7004EFFAE /* Lorem.swift */, - 35AA273F2B79FF5D004EFFAE /* MockChatData.swift */, + 6C32379E2B7C376D00B699AB /* Bookmark.swift */, + 6C3237A02B7C377600B699AB /* BookmarkViewModel.swift */, + 6C3237A42B7C37D100B699AB /* BookmarkView.swift */, + 6C3237A62B7C37E500B699AB /* BookmarkListViewModel.swift */, ); - path = "Data Generation"; + path = BookmarkCard; sourceTree = ""; }; - 35AA27412B79FF7B004EFFAE /* Chat */ = { + 6C3237A82B7C380200B699AB /* NewsCard */ = { isa = PBXGroup; children = ( - 35AA27422B79FF91004EFFAE /* ChatInteractorProtocol.swift */, - 35AA27442B79FFAB004EFFAE /* MockChatInteractor.swift */, + 6C3237A92B7C381500B699AB /* NewsView.swift */, + 6C3237AD2B7C382E00B699AB /* NewsViewModel.swift */, + 6C3237AB2B7C382200B699AB /* News.swift */, ); - path = Chat; + path = NewsCard; sourceTree = ""; }; - 35AA27462B79FFD5004EFFAE /* View */ = { + 6C3237B32B7C433000B699AB /* ChatType */ = { isa = PBXGroup; children = ( - 352EE6822B76103400E51B60 /* ChatTitleView.swift */, - 35AA272E2B79FCAE004EFFAE /* ChatExampleView.swift */, - 35AA27302B79FCF3004EFFAE /* ChatExampleViewModel.swift */, - 35AA274C2B7A09CA004EFFAE /* ChattingView.swift */, + 6C3237B42B7C433D00B699AB /* ChatTypeView.swift */, + 6C3237B62B7C434600B699AB /* ChatType.swift */, ); - path = View; + path = ChatType; sourceTree = ""; }; - 35AA27472B79FFE5004EFFAE /* Extension */ = { + 6C454A762B9DA62C006FD9D0 /* SignUp */ = { isa = PBXGroup; children = ( - 35AA27482B79FFF4004EFFAE /* Date+Random.swift */, + 6C454A772B9DA657006FD9D0 /* SignUpQuestionView.swift */, + 6C454A7B2B9DA71C006FD9D0 /* SignUpView.swift */, + 6C454A792B9DA67C006FD9D0 /* SignUpViewModel.swift */, + 6C454A7D2B9DAA3F006FD9D0 /* SignUpFinishView.swift */, ); - path = Extension; + path = SignUp; sourceTree = ""; }; - 35AA27502B7A1412004EFFAE /* AIChat */ = { + 6C454A7F2B9DAF21006FD9D0 /* Model */ = { isa = PBXGroup; children = ( - 35AA27512B7A1420004EFFAE /* AIChatView.swift */, - 35AA27532B7A1489004EFFAE /* AIChatCustomView.swift */, - 35AA27552B7A1495004EFFAE /* MyChatCustomView.swift */, - 35AA27572B7A1A34004EFFAE /* AIChatTitleView.swift */, + 6C454A802B9DAF9F006FD9D0 /* Path */, ); - path = AIChat; + path = Model; sourceTree = ""; }; - 6C32379D2B7C374E00B699AB /* BookmarkCard */ = { + 6C454A802B9DAF9F006FD9D0 /* Path */ = { isa = PBXGroup; children = ( - 6C32379E2B7C376D00B699AB /* Bookmark.swift */, - 6C3237A02B7C377600B699AB /* BookmarkViewModel.swift */, - 6C3237A42B7C37D100B699AB /* BookmarkView.swift */, - 6C3237A62B7C37E500B699AB /* BookmarkListViewModel.swift */, + 6C454A812B9DAFA3006FD9D0 /* Path.swift */, + 6C454A832B9DAFCB006FD9D0 /* PathType.swift */, ); - path = BookmarkCard; + path = Path; sourceTree = ""; }; - 6C3237A82B7C380200B699AB /* NewsCard */ = { + 6C454A852B9DB429006FD9D0 /* General */ = { isa = PBXGroup; children = ( - 6C3237A92B7C381500B699AB /* NewsView.swift */, - 6C3237AD2B7C382E00B699AB /* NewsViewModel.swift */, - 6C3237AB2B7C382200B699AB /* News.swift */, + 6C454A862B9DB433006FD9D0 /* CustomNavigationBar */, ); - path = NewsCard; + path = General; sourceTree = ""; }; - 6C3237B32B7C433000B699AB /* ChatType */ = { + 6C454A862B9DB433006FD9D0 /* CustomNavigationBar */ = { isa = PBXGroup; children = ( - 6C3237B42B7C433D00B699AB /* ChatTypeView.swift */, - 6C3237B62B7C434600B699AB /* ChatType.swift */, + 6C454A872B9DB6C2006FD9D0 /* CustomNavigationBar.swift */, ); - path = ChatType; + path = CustomNavigationBar; sourceTree = ""; }; 6C7704852B72260F001B17CB /* Resources */ = { isa = PBXGroup; children = ( + 6CA901812BA2EBBA00E20259 /* Font */, 6CC4DDCA2B5574690080E7E8 /* Assets.xcassets */, 6C7704A22B722D3B001B17CB /* ColorAssets.xcassets */, 6C7704A42B723104001B17CB /* ImageAssets.xcassets */, @@ -247,7 +205,9 @@ 6C7704862B722618001B17CB /* Source */ = { isa = PBXGroup; children = ( + 6C454A852B9DB429006FD9D0 /* General */, 6C7704882B722647001B17CB /* View */, + 6C454A7F2B9DAF21006FD9D0 /* Model */, ); path = Source; sourceTree = ""; @@ -255,6 +215,7 @@ 6C7704882B722647001B17CB /* View */ = { isa = PBXGroup; children = ( + 6C454A762B9DA62C006FD9D0 /* SignUp */, 6C77048A2B72267E001B17CB /* MainTab */, 6C77048D2B7229A3001B17CB /* News */, 6C7704902B7229B6001B17CB /* Chat */, @@ -288,9 +249,8 @@ 6C7704902B7229B6001B17CB /* Chat */ = { isa = PBXGroup; children = ( + 6CDB29F72BAA06FB0081037B /* ChatGPT */, 6C3237B32B7C433000B699AB /* ChatType */, - 352EE6852B7611F500E51B60 /* Model */, - 352EE6842B7611E900E51B60 /* View */, ); path = Chat; sourceTree = ""; @@ -320,6 +280,31 @@ path = Profile; sourceTree = ""; }; + 6CA901812BA2EBBA00E20259 /* Font */ = { + isa = PBXGroup; + children = ( + 6CA901952BA2EC0100E20259 /* Font.swift */, + 6CA901822BA2EBBF00E20259 /* Pretendard */, + ); + path = Font; + sourceTree = ""; + }; + 6CA901822BA2EBBF00E20259 /* Pretendard */ = { + isa = PBXGroup; + children = ( + 6CDB29C02BA97C550081037B /* Pretendard-Black.otf */, + 6CDB29C62BA97C550081037B /* Pretendard-Bold.otf */, + 6CDB29C12BA97C550081037B /* Pretendard-ExtraBold.otf */, + 6CDB29C52BA97C550081037B /* Pretendard-ExtraLight.otf */, + 6CDB29C82BA97C550081037B /* Pretendard-Light.otf */, + 6CDB29C72BA97C550081037B /* Pretendard-Medium.otf */, + 6CDB29C22BA97C550081037B /* Pretendard-Regular.otf */, + 6CDB29C42BA97C550081037B /* Pretendard-SemiBold.otf */, + 6CDB29C32BA97C550081037B /* Pretendard-Thin.otf */, + ); + path = Pretendard; + sourceTree = ""; + }; 6CC4DDBA2B5574670080E7E8 = { isa = PBXGroup; children = ( @@ -339,6 +324,7 @@ 6CC4DDC52B5574670080E7E8 /* RollTheDice */ = { isa = PBXGroup; children = ( + 6CDB29BF2BA9735C0081037B /* Info.plist */, 6C7704862B722618001B17CB /* Source */, 6C7704852B72260F001B17CB /* Resources */, 6CC4DDC62B5574670080E7E8 /* RollTheDiceApp.swift */, @@ -356,6 +342,17 @@ path = "Preview Content"; sourceTree = ""; }; + 6CDB29F72BAA06FB0081037B /* ChatGPT */ = { + isa = PBXGroup; + children = ( + 6CDB29F82BAA07350081037B /* GPTChat.swift */, + 6CDB29FA2BAA07B10081037B /* GPTChatViewModel.swift */, + 6CDB29FC2BAA07FD0081037B /* GPTChatView.swift */, + 6CDB29FE2BAA08280081037B /* GPTChatListViewModel.swift */, + ); + path = ChatGPT; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -374,6 +371,7 @@ name = RollTheDice; packageProductDependencies = ( 35C71BF12B79F39900F777D1 /* ExyteChat */, + 6CDB2A012BAA085A0081037B /* OpenAI */, ); productName = RollTheDice; productReference = 6CC4DDC32B5574670080E7E8 /* RollTheDice.app */; @@ -405,6 +403,7 @@ mainGroup = 6CC4DDBA2B5574670080E7E8; packageReferences = ( 35C71BF02B79F39900F777D1 /* XCRemoteSwiftPackageReference "Chat" */, + 6CDB2A002BAA085A0081037B /* XCRemoteSwiftPackageReference "OpenAI" */, ); productRefGroup = 6CC4DDC42B5574670080E7E8 /* Products */; projectDirPath = ""; @@ -420,10 +419,19 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6CDB29D12BA97C550081037B /* Pretendard-Light.otf in Resources */, + 6CDB29CF2BA97C550081037B /* Pretendard-Bold.otf in Resources */, 6CC4DDCE2B5574690080E7E8 /* Preview Assets.xcassets in Resources */, 6C7704A32B722D3B001B17CB /* ColorAssets.xcassets in Resources */, + 6CDB29CE2BA97C550081037B /* Pretendard-ExtraLight.otf in Resources */, + 6CDB29CA2BA97C550081037B /* Pretendard-ExtraBold.otf in Resources */, + 6CDB29CC2BA97C550081037B /* Pretendard-Thin.otf in Resources */, 6C7704A52B723104001B17CB /* ImageAssets.xcassets in Resources */, + 6CDB29CB2BA97C550081037B /* Pretendard-Regular.otf in Resources */, + 6CDB29CD2BA97C550081037B /* Pretendard-SemiBold.otf in Resources */, 6CC4DDCB2B5574690080E7E8 /* Assets.xcassets in Resources */, + 6CDB29C92BA97C550081037B /* Pretendard-Black.otf in Resources */, + 6CDB29D02BA97C550081037B /* Pretendard-Medium.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -434,43 +442,37 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 35AA27522B7A1420004EFFAE /* AIChatView.swift in Sources */, + 6C454A882B9DB6C2006FD9D0 /* CustomNavigationBar.swift in Sources */, + 6C454A7A2B9DA67C006FD9D0 /* SignUpViewModel.swift in Sources */, 6C3237AA2B7C381500B699AB /* NewsView.swift in Sources */, - 35AA27342B79FE70004EFFAE /* ChatError.swift in Sources */, - 35AA27402B79FF5D004EFFAE /* MockChatData.swift in Sources */, + 6CDB29FF2BAA08280081037B /* GPTChatListViewModel.swift in Sources */, + 6CDB29F92BAA07350081037B /* GPTChat.swift in Sources */, + 6CDB29FD2BAA07FD0081037B /* GPTChatView.swift in Sources */, 6C3237A12B7C377600B699AB /* BookmarkViewModel.swift in Sources */, - 35AA274D2B7A09CA004EFFAE /* ChattingView.swift in Sources */, 6C3237AC2B7C382200B699AB /* News.swift in Sources */, 6CC4DDC92B5574670080E7E8 /* ContentView.swift in Sources */, 6C3237A52B7C37D100B699AB /* BookmarkView.swift in Sources */, 6C3237AE2B7C382E00B699AB /* NewsViewModel.swift in Sources */, - 35AA27362B79FE7F004EFFAE /* MockUser.swift in Sources */, 6C77048F2B7229B1001B17CB /* NewsListView.swift in Sources */, - 35AA27562B7A1495004EFFAE /* MyChatCustomView.swift in Sources */, - 35AA273E2B79FED7004EFFAE /* Lorem.swift in Sources */, - 35AA27432B79FF91004EFFAE /* ChatInteractorProtocol.swift in Sources */, - 35AA27582B7A1A34004EFFAE /* AIChatTitleView.swift in Sources */, - 35AA27492B79FFF4004EFFAE /* Date+Random.swift in Sources */, - 35AA27312B79FCF3004EFFAE /* ChatExampleViewModel.swift in Sources */, - 35AA273A2B79FEAA004EFFAE /* MockAttachment.swift in Sources */, 6C3237A72B7C37E500B699AB /* BookmarkListViewModel.swift in Sources */, - 35AA27542B7A1489004EFFAE /* AIChatCustomView.swift in Sources */, - 35AA27452B79FFAB004EFFAE /* MockChatInteractor.swift in Sources */, + 6C454A822B9DAFA3006FD9D0 /* Path.swift in Sources */, + 6C454A842B9DAFCB006FD9D0 /* PathType.swift in Sources */, 6C3237B52B7C433D00B699AB /* ChatTypeView.swift in Sources */, 6C3237B22B7C385000B699AB /* NewsListViewModel.swift in Sources */, - 35AA272F2B79FCAE004EFFAE /* ChatExampleView.swift in Sources */, - 35AA27382B79FE96004EFFAE /* MockMessage.swift in Sources */, - 352EE6832B76103400E51B60 /* ChatTitleView.swift in Sources */, + 6C454A782B9DA657006FD9D0 /* SignUpQuestionView.swift in Sources */, + 6CA901962BA2EC0100E20259 /* Font.swift in Sources */, + 6CDB29FB2BAA07B10081037B /* GPTChatViewModel.swift in Sources */, 6C32379F2B7C376D00B699AB /* Bookmark.swift in Sources */, + 6C454A7E2B9DAA3F006FD9D0 /* SignUpFinishView.swift in Sources */, 6C7704A12B722CEB001B17CB /* ProfileView.swift in Sources */, 6C77049F2B722CE4001B17CB /* ARView.swift in Sources */, 6C3237B72B7C434600B699AB /* ChatType.swift in Sources */, 6CC4DDC72B5574670080E7E8 /* RollTheDiceApp.swift in Sources */, 6C77048C2B722686001B17CB /* MainTabView.swift in Sources */, 6C7704992B722A20001B17CB /* MainTabViewModel.swift in Sources */, + 6C454A7C2B9DA71C006FD9D0 /* SignUpView.swift in Sources */, 6C77049D2B722CE0001B17CB /* BookmarkListView.swift in Sources */, 6C77049B2B722A5A001B17CB /* TabType.swift in Sources */, - 352EE6872B76120500E51B60 /* ChatModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -607,6 +609,7 @@ DEVELOPMENT_TEAM = 4YH4UGRTMH; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = RollTheDice/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -637,6 +640,7 @@ DEVELOPMENT_TEAM = 4YH4UGRTMH; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = RollTheDice/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -688,6 +692,14 @@ minimumVersion = 1.3.5; }; }; + 6CDB2A002BAA085A0081037B /* XCRemoteSwiftPackageReference "OpenAI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/MacPaw/OpenAI"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.2.6; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -696,6 +708,11 @@ package = 35C71BF02B79F39900F777D1 /* XCRemoteSwiftPackageReference "Chat" */; productName = ExyteChat; }; + 6CDB2A012BAA085A0081037B /* OpenAI */ = { + isa = XCSwiftPackageProductDependency; + package = 6CDB2A002BAA085A0081037B /* XCRemoteSwiftPackageReference "OpenAI" */; + productName = OpenAI; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 6CC4DDBB2B5574670080E7E8 /* Project object */; diff --git a/iOS/RollTheDice/RollTheDice/Info.plist b/iOS/RollTheDice/RollTheDice/Info.plist new file mode 100644 index 00000000..cf8c9292 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Info.plist @@ -0,0 +1,18 @@ + + + + + UIAppFonts + + Pretendard-Black.otf + Pretendard-Bold.otf + Pretendard-ExtraBold.otf + Pretendard-ExtraLight.otf + Pretendard-Light.otf + Pretendard-Medium.otf + Pretendard-Regular.otf + Pretendard-SemiBold.otf + Pretendard-Thin.otf + + + diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Font.swift b/iOS/RollTheDice/RollTheDice/Resources/Font/Font.swift new file mode 100644 index 00000000..6208c30b --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Resources/Font/Font.swift @@ -0,0 +1,34 @@ +// +// Font.swift +// RollTheDice +// +// Created by Subeen on 3/14/24. +// + +import SwiftUI + +extension Font { + + // Bold + static let pretendardBold40: Font = .custom("Pretendard-Bold", size: 40) + static let pretendardBold32: Font = .custom("Pretendard-Bold", size: 32) + static let pretendardBold24: Font = .custom("Pretendard-Bold", size: 24) + static let pretendardBold14: Font = .custom("Pretendard-Bold", size: 14) + static let pretendardBold12: Font = .custom("Pretendard-Bold", size: 12) + + // Regular + static let pretendardRegular20: Font = .custom("Pretendard-Regular", size: 20) + static let pretendardRegular14: Font = .custom("Pretendard-Regular", size: 14) + +} + + +/// 폰트가 추가되었는지 확인 +func checkFontFile() { + for fontFamily in UIFont.familyNames { + for fontName in UIFont.fontNames(forFamilyName: fontFamily) { + print(fontName) + } + } +} + diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Black.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Black.otf new file mode 100644 index 00000000..a0d849e7 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Black.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Bold.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Bold.otf new file mode 100644 index 00000000..8e5e30a2 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Bold.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraBold.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraBold.otf new file mode 100644 index 00000000..388f3ca4 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraBold.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraLight.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraLight.otf new file mode 100644 index 00000000..40c8b69c Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-ExtraLight.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Light.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Light.otf new file mode 100644 index 00000000..228679e9 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Light.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Medium.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Medium.otf new file mode 100644 index 00000000..05750698 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Medium.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Regular.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Regular.otf new file mode 100644 index 00000000..08bf4cfc Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Regular.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-SemiBold.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-SemiBold.otf new file mode 100644 index 00000000..e7e36abc Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-SemiBold.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Thin.otf b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Thin.otf new file mode 100644 index 00000000..77e792d7 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/Font/Pretendard/Pretendard-Thin.otf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/Contents.json b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/Contents.json b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/Contents.json new file mode 100644 index 00000000..53987739 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "appleLoginBtn.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/appleLoginBtn.pdf b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/appleLoginBtn.pdf new file mode 100644 index 00000000..4c76df35 Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/appleLoginBtn.imageset/appleLoginBtn.pdf differ diff --git a/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/Contents.json b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/Contents.json new file mode 100644 index 00000000..d2abc72e --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "kakaoLoginBtn.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/kakaoLoginBtn.pdf b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/kakaoLoginBtn.pdf new file mode 100644 index 00000000..89fab65f Binary files /dev/null and b/iOS/RollTheDice/RollTheDice/Resources/ImageAssets.xcassets/SignIn/kakaoLoginBtn.imageset/kakaoLoginBtn.pdf differ diff --git a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift index 158aace7..e0d33739 100644 --- a/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift +++ b/iOS/RollTheDice/RollTheDice/RollTheDiceApp.swift @@ -9,9 +9,44 @@ import SwiftUI @main struct RollTheDiceApp: App { + + @StateObject var appState = AppState() + @StateObject private var pathModel = PathModel() + + @StateObject var newsListViewModel = NewsListViewModel() + var body: some Scene { WindowGroup { - MainTabView() + + if appState.hasLogin { + NavigationStack(path: $pathModel.paths) { + MainTabView() + .environmentObject(newsListViewModel) + .navigationDestination(for: PathType.self, destination: { pathType in + + switch pathType { + case .chatView(isAiMode: true) : + GPTChatView() + .navigationBarBackButtonHidden() + + case .chatView(isAiMode: false): + Text("user") + .navigationBarBackButtonHidden() + } + }) + } + .environmentObject(pathModel) + + } else { + SignUpView() + } } + } } + +/// 로그인 상태 관리 +class AppState: ObservableObject { + + @AppStorage("hasLogin") var hasLogin = true +} diff --git a/iOS/RollTheDice/RollTheDice/Source/General/CustomNavigationBar/CustomNavigationBar.swift b/iOS/RollTheDice/RollTheDice/Source/General/CustomNavigationBar/CustomNavigationBar.swift new file mode 100644 index 00000000..1eb47d0e --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/General/CustomNavigationBar/CustomNavigationBar.swift @@ -0,0 +1,85 @@ +// +// CustomNavigationBar.swift +// DittoCommunity +// +// Created by Subeen on 2023/11/25. +// + +import SwiftUI + +struct CustomNavigationBar: View { + let title: String + + let isDisplayLeadingBtn: Bool + let isDisplayTrailingBtn: Bool + + let leadingItems: [(Image, () -> Void)] + let trailingItems: [(Image, () -> Void)] + + init( + title: String = "", + isDisplayLeadingBtn: Bool = true, + isDisplayTrailingBtn: Bool = true, + leadingItems: [(Image, () -> Void)] = [(Image("chevron.left"), {})], + trailingItems: [(Image, () -> Void)] = [(Image("chevron.left"), {})] + ) { + self.title = title + self.isDisplayLeadingBtn = isDisplayLeadingBtn + self.isDisplayTrailingBtn = isDisplayTrailingBtn + self.leadingItems = leadingItems + self.trailingItems = trailingItems + } + + var body: some View { + HStack(spacing: 0) { + if isDisplayLeadingBtn { +// ForEach(leadingItems, id:\.self) { item in +// Button( +// action: { +// item.1() +// }, +// label: { +// leadingItems[index].0 +// .foregroundStyle(.basicWhite) +// } +// ) +// } + } + + Spacer() + + Text(title) + .foregroundStyle(.basicWhite) + + Spacer() + + if isDisplayTrailingBtn { + ForEach(trailingItems.indices) { index in + Button( + action: { + trailingItems[index].1() + }, + label: { + trailingItems[index].0 + .foregroundStyle(.basicWhite) + } + ) + } + } + } + .padding(.horizontal, 20) + .frame(height: 44) + } +} + +struct IdentifiableImage: Identifiable { + let id = UUID() + let image: Image +} + +struct CustomNavigationBar_Previews: PreviewProvider { + static var previews: some View { + CustomNavigationBar() + .previewLayout(.sizeThatFits) + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/Model/Path/Path.swift b/iOS/RollTheDice/RollTheDice/Source/Model/Path/Path.swift new file mode 100644 index 00000000..1ac68ddd --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/Model/Path/Path.swift @@ -0,0 +1,16 @@ +// +// Path.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import Foundation + +class PathModel: ObservableObject { + @Published var paths: [PathType] + + init(paths: [PathType] = []) { // 빈 배열로 초기화. 앱 실행시 특정 화면을 보여주고 싶다면 해당 뷰로 초기화할 것. + self.paths = paths + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift b/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift new file mode 100644 index 00000000..7cd3d0f5 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/Model/Path/PathType.swift @@ -0,0 +1,12 @@ +// +// PathType.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import Foundation + +enum PathType: Hashable { + case chatView(isAiMode: Bool) +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkListViewModel.swift index 9dbf484b..8d2a5731 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkListViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkListViewModel.swift @@ -13,9 +13,16 @@ public class BookmarkListViewModel: ObservableObject { init( bookmarkList: [Bookmark] = [ .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "exampleNews", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false), - .init(title: "해외원정 가던 줄기세포치료 이젠 국내서도 받을 수 있다", date: "2023년2월13일", image: "exampleNews", content: "첨생법 개정안이 이달 1일 국회를 통과해 내년부터 시행된다. 이에 따라 식약처 허가 없이도 안전성·유효성이 확인되면 국내에서도 첨단재생의료 치료가 허용되고 모든 질환에 대한 임상연구가 가능해졌다. ", isBookmarked: false), - .init(title: "홀로 선 자립준비청년, 배곯지 않게…우체국, 매일 식비 지원", date: "2023년2월13일", content: "과학기술정보통신부 우정사업본부는 사회에 첫발을 내딛는 자립준비청년이 건강한 사회구성원으로 성장하도록 식비를 지원하는 '우체국 청년밥심 스타트 온(溫)' 사업을 확대 추진한다고 14일 밝혔다.", isBookmarked: false), - .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "exampleNews", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + .init(title: "앤스로픽 최신 AI 모델 '클로드3' 출시", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/014/2024/03/05/0005151141_001_20240305101610000.jpg?type=w647", content: "오픈AI의 대항마 앤스로픽이 생성형 인공지능(AI) 최신 모델 '클로드'(Claude)3를 선보이면서 생성형 AI 주도권을 잡기 위한 경쟁이 다시 뜨거워지고 있다. 앤스로픽은 지난 한 해 동안 구글과 세일즈포스, 아마존 등에서 총 73억 달러(약 9조7309억 원)를 투자받고 '클로드3'를 내놨는데 오픈AI의 챗GPT-4를 능가한다고 도발했다.", isBookmarked: true), + .init(title: "SK C&C, 외부 전문가 대거 영입… “신성장 동력 강화”", date: "2023년2월13일", image: "https://imgnews.pstatic.net/image/366/2024/03/05/0000975131_001_20240305093504301.jpg?type=w647", content: "SK C&C는 국내외 신성장 동력 강화를 위해 인공지능(AI)·클라우드·디지털 팩토리·ESG(환경·사회·지배구조) 등 4대 성장 사업과 디지털 컨설팅 중심으로 외부 전문가를 대거 영입해 전진 배치했다고 5일 밝혔다.", isBookmarked: false), + .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + + .init(title: "이종호 과기장관 5년 뒤 구축될 바이오파운드리에 산학연 역량 모아야", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/003/2024/03/05/NISI20231030_0001398722_web_20231030141808_20240305103108748.jpg?type=w647", content: "정부가 내년부터 합성생물학의 필수 인프라인 '국가 바이오파운드리' 구축에 본격 나서는 가운데 이종호 과학기술정보통신부 장관이 한국생명공학연구원에서 운영 중인 사전연구용 시설을 찾아 구체적인 인프라 구축 계획 등을 점검했다.이 장관은 5일 합성생물학 육성을 위해 바이오파운드리 연구현장을 방문하고, 산·학·연 전문가들을 만났다. 이번 방문은 지난해 미국, 영국과의 정상회담 시 논의됐던 첨단바이오 협력을 위한 후속조치의 일환으로 추진됐다.", isBookmarked: false), + .init(title: "SW산업 육성에 7308억 예산투자, SaaS 등 육성", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/008/2024/03/05/0005007507_001_20240305135301539.jpg?type=w647", content: "SW(소프트웨어) 산업의 SaaS(서비스형 소프트웨어) 전환 등 SW산업 육성을 위해 정부 예산 7308억원이 투자된다. 강도현 과학기술정보통신부 제2차관은 5일 오전 서울 을지로 더존비즈온 을지사옥에서 열린 'AI(인공지능) 일상화, SW도 이제 서비스형 SW'라는 주제로 열린 현장 간담회에서 정부도 대한민국 SW산업의 미래가 SaaS에 있다고 생각하고 올해 'SaaS 혁신펀드'를 새롭게 조성하는 등 SaaS 육성 및 기존 SW 기업의 SaaS 전환을 위해 다양한 지원을 추진 중이라며 이같이 밝혔다.", isBookmarked: false), + .init(title: "파수, 기업용 AI ‘엔터프라이즈 LLM’ 출시", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/018/2024/03/05/0005685566_001_20240305103201036.jpg?type=w647", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), ] ) { self.bookmarkList = bookmarkList diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift index f59a9444..c62ed345 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkCard/BookmarkView.swift @@ -26,11 +26,16 @@ struct BookmarkView: View { Spacer() HStack { - Image(bookmark.image) - .frame(width: 100, height: 100) - .clipShape( - RoundedRectangle(cornerRadius: 15) - ) + AsyncImage(url: URL(string: bookmark.image)) { image in + image + .resizable() + .scaledToFill() + .frame(width: 150, height: 150) + .clipShape(RoundedRectangle(cornerRadius: 15)) + + } placeholder: { + Image("photo.circle.fill") + } Spacer() } .padding(.bottom, 30) @@ -66,5 +71,5 @@ struct BookmarkView: View { } #Preview { - BookmarkView(bookmark: .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "exampleNews", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false)) + BookmarkView(bookmark: .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false)) } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift index 5a35c125..f652615a 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Bookmark/BookmarkListView.swift @@ -21,7 +21,7 @@ struct BookmarkListView: View { private struct BookmarkListContentView: View { @EnvironmentObject var bookmarkListViewModel: BookmarkListViewModel - var columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2) + var columns: [GridItem] = [ GridItem(), GridItem() ] fileprivate var body: some View { ScrollViewReader { value in @@ -37,6 +37,7 @@ struct BookmarkListView: View { // } } } + .padding(.vertical, 90) } } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChat.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChat.swift new file mode 100644 index 00000000..8112a66b --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChat.swift @@ -0,0 +1,28 @@ +// +// ChatGPTChat.swift +// RollTheDice +// +// Created by Subeen on 3/20/24. +// + + +import Foundation + +struct GPTChat: Hashable { + var title: String + var messages: [Message] + + init( + title: String = "", + messages: [Message] = [] + ) { + self.title = title + self.messages = messages + } +} + +struct Message: Hashable, Identifiable { + var id: UUID = .init() + var content: String + var isUser: Bool +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatListViewModel.swift new file mode 100644 index 00000000..db009df2 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatListViewModel.swift @@ -0,0 +1,64 @@ +// +// GPTChatListViewModel.swift +// RollTheDice +// +// Created by Subeen on 3/20/24. +// + +import Foundation +import OpenAI + +class GPTChatListViewModel: ObservableObject { + @Published var chatList: [GPTChat] + + // TODO: Token Hidden + /// token 추가해야 함 + let openAI = OpenAI(apiToken: "") + + init( + chatList: [GPTChat] = [ + .init(title: "111111111", messages: []), + ] + ) { + self.chatList = chatList + } +} + +extension GPTChatListViewModel { + /// OpenAI + func sendNewMessage(index: Int, content: String) { + print("call sendNewMessage func") + + let userMessage = Message(content: content, isUser: true) + self.chatList[index].messages.append(userMessage) + + getBotReply(index: index) + } + + func getBotReply(index: Int) { + print("call getBotReply func") + + openAI.chats( + query: .init( + model: .gpt3_5Turbo, + messages: self.chatList[index].messages.map( + {Chat(role: .user, content: $0.content)})) + ) { result in + switch result { + case .success(let success): + guard let choice = success.choices.first else { + return + } + let message = choice.message.content + + DispatchQueue.main.async { + self.chatList[index].messages.append(.init(content: message ?? "Error", isUser: false)) + print("gpt msg: \(self.chatList[index].messages[1])") + } + + case .failure(let failure): + print(failure) + } + } + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatView.swift new file mode 100644 index 00000000..5ecbbb4b --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatView.swift @@ -0,0 +1,134 @@ +// +// GPTChatView.swift +// RollTheDice +// +// Created by Subeen on 3/20/24. +// + +import SwiftUI + +struct GPTChatView: View { + + @StateObject var chatListViewModel = GPTChatListViewModel() + @State var selectedChat: GPTChat? = nil + + var body: some View { + ZStack { + Color.backgroundDark.ignoresSafeArea(.all) + VStack { + CustomNavigationBar(title: selectedChat?.title ?? "", isDisplayLeadingBtn: true, leadingItems: [(Image("chevron.left"), {})]) + MessageTitleView(chatListViewModel: chatListViewModel, selectedChat: $selectedChat) + } + } + } + + + private struct MessageTitleView: View { + @ObservedObject var chatListViewModel: GPTChatListViewModel + @Binding var selectedChat: GPTChat? + @State var string: String = "" + @State var index: Int? = 0 + + fileprivate var body: some View { + if chatListViewModel.chatList.isEmpty { + // TODO: 채팅방 생성 뷰 + } else { + HStack { + ScrollView { + ForEach(chatListViewModel.chatList.indices, id: \.self) { index in + Button { + selectedChat = chatListViewModel.chatList[index] + } label: { + TitleCellView(title: chatListViewModel.chatList[index].title) + } + + } + } + if selectedChat != nil { + VStack { + ScrollView { + ForEach(chatListViewModel.chatList[index ?? 0].messages ) { message in + MessageCellView(message: message) + } + } + Divider() + + HStack { + TextField("Message...", text: $string, axis: .vertical) + .padding(5) + .background(Color.gray.opacity(0.1)) + .foregroundStyle(.basicWhite) + .cornerRadius(15) + Button { + print("string : \(string)") + + chatListViewModel.sendNewMessage(index: index ?? 0, content: string) + string = "" + } label: { + Image(systemName: "paperplane") + } + } + .padding() + + + } + } else { + // TODO: 채팅방 선택 유도 뷰 + Spacer() + } + + } + } + } + } + + private struct TitleCellView: View { + var title: String + fileprivate var body: some View { + HStack { + Text(title) + .multilineTextAlignment(.leading) + .padding(.horizontal) + .background(Color.basicBlack) + .foregroundStyle(.basicWhite) + .clipShape(RoundedRectangle(cornerRadius: 15)) + } + } + } + + private struct MessageCellView: View { + var message: Message + + fileprivate var body: some View { + Group { + if message.isUser { + HStack { + Spacer() + Text(message.content) + .multilineTextAlignment(.leading) + .padding() + .background(Color.blue) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 15)) + } + + } else { + HStack { + Text(message.content) + .padding() + .background(Color.orange) + .foregroundStyle(.white) + .clipShape(RoundedRectangle(cornerRadius: 15)) + Spacer() + } + } + } + } + } +} + + + +#Preview { + GPTChatView() +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatViewModel.swift new file mode 100644 index 00000000..1b77dc33 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatGPT/GPTChatViewModel.swift @@ -0,0 +1,16 @@ +// +// GPTChatViewModel.swift +// RollTheDice +// +// Created by Subeen on 3/20/24. +// + +import Foundation + +class GPTChatViewModel: ObservableObject { + @Published var chat: GPTChat + + init(chat: GPTChat) { + self.chat = chat + } +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatType/ChatTypeView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatType/ChatTypeView.swift index 065cc1f7..9c43fc90 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatType/ChatTypeView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/Chat/ChatType/ChatTypeView.swift @@ -8,26 +8,26 @@ import SwiftUI struct ChatTypeView: View { + @EnvironmentObject private var pathModel: PathModel @State var isSelected: Bool = false var body: some View { ZStack { Color.backgroundDark.ignoresSafeArea(.all) ChatTypeContentView(isSelected: $isSelected) - if isSelected { - AIChatView() - } } } private struct ChatTypeContentView: View { @Binding var isSelected: Bool + @EnvironmentObject private var pathModel: PathModel fileprivate var body: some View { HStack { Button { isSelected.toggle() + pathModel.paths.append(.chatView(isAiMode: true)) } label: { RoundedRectangle(cornerRadius: 15) .foregroundStyle(.primary01) @@ -37,10 +37,10 @@ struct ChatTypeView: View { VStack { HStack { Text("Chat GPT랑\n토론하기") - .multilineTextAlignment(/*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/) + .multilineTextAlignment(.leading) .font(.system(size: 40, weight: .bold)) .foregroundStyle(.basicWhite) - + Spacer() } @@ -61,7 +61,7 @@ struct ChatTypeView: View { } Button { - + pathModel.paths.append(.chatView(isAiMode: false)) } label: { RoundedRectangle(cornerRadius: 15) .foregroundStyle(.gray01) @@ -97,4 +97,5 @@ struct ChatTypeView: View { #Preview { ChatTypeView() + .environmentObject(PathModel()) } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/Model/ChatModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/Model/ChatModel.swift deleted file mode 100644 index 68d70486..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/Model/ChatModel.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// ChatModel.swift -// RollTheDice -// -// Created by 신예진 on 2/9/24. -// - -import SwiftUI diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatCustomView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatCustomView.swift deleted file mode 100644 index 89461036..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatCustomView.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// AIChatCustomView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import SwiftUI - -struct AIChatCustomView: View { - var body: some View { - ChatBubbleView(text: "ChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.\nChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.\nChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.") - } -} - -struct ChatBubbleView: View { - let text: String - - var body: some View { - VStack { - VStack(alignment: .leading) { - Text(text) - .padding() - .background(Color("primary01")) - .foregroundColor(.white) - .cornerRadius(30) - .padding(.bottom,20) - } - - Image("tim") - .resizable() - .frame(width: 30, height: 30) - .padding(.leading,-120) - } - } -} - - -struct AIChatCustomView_Previews: PreviewProvider { - static var previews: some View { - AIChatCustomView() - .previewLayout(.sizeThatFits) - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatTitleView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatTitleView.swift deleted file mode 100644 index 5c569150..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatTitleView.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// AIChatTitleView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import SwiftUI - -//왼쪽 TITLE 눌렀을 때 들어가는 View -struct AIChatTitleView: View { - @State private var selectedTitle: String = "" - @State private var selectedIndex: Int? - - var body: some View { - VStack { - ForEach(0..<20) { index in - Button(action: { - selectedTitle = "여기에채팅제목이뜹니다. \(index)" - selectedIndex = index - }) { - Text("여기에채팅제목이뜹니다. \(index)") - .foregroundColor(.basicWhite) - .padding() - } - .background(selectedIndex == index ? Color.primary01 : Color.black) - .cornerRadius(8) - .padding(.horizontal) - } - } - } -} - -struct AIChatTitleView_Previews: PreviewProvider { - static var previews: some View { - AIChatTitleView() - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatView.swift deleted file mode 100644 index b7d0691d..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/AIChatView.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// AIChatView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import SwiftUI - -struct AIChatView: View { - @State private var text: String = "" - @State private var messages: [String] = [] - - var body: some View { - ZStack { - Color.backgroundDark.ignoresSafeArea(.all) - HStack { - ScrollView(showsIndicators: false) { - AIChatTitleView() - } - - VStack { - ScrollView{ - - HStack { - Spacer() - - VStack { - HStack { - AIChatCustomView() - Spacer() - } - - HStack { - Spacer() - MyChatCustomView() - } - - VStack(alignment: .trailing) { - HStack { - Spacer() - VStack { - ForEach(messages, id: \.self) { message in - Text(message) - .padding() - .background(.basicWhite) - .foregroundColor(.black) - .cornerRadius(30) - .padding(.bottom, 20) - } - } - } - } - } - } - - } - - VStack { - - HStack { - TextField("Type a message", text: $text) - .textFieldStyle(RoundedBorderTextFieldStyle()) - .padding() - - Button(action: sendMessage) { - Image(systemName: "arrow.up.circle") - .resizable() - .frame(width: 30, height: 30) - .padding(.trailing) - } - } - } - } - } - } - - } - - func sendMessage() { - if !text.isEmpty { - messages.append(text) - text = "" - } - } - -} - -struct AIChatView_Previews: PreviewProvider { - static var previews: some View { - AIChatView() - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/MyChatCustomView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/MyChatCustomView.swift deleted file mode 100644 index 15bfc2d6..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/AIChat/MyChatCustomView.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// MyChatCustomView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import SwiftUI - -struct MyChatCustomView: View { - var body: some View { - MyChatBubbleView(text: "ChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.\nChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.\nChatGPT와의 채팅입니다\n원하는내용으로채팅시작해보세요.\n저는 준비가 되었습니다.\n진지한님 채팅을 시작해보세요.") - } -} - -struct MyChatBubbleView: View { - let text: String - - var body: some View { - VStack { - VStack(alignment: .trailing) { - Text(text) - .padding() - .background(Color("basicWhite")) - .foregroundColor(.black) - .cornerRadius(30) - .padding(.bottom,20) - } - - Image("steve") - .resizable() - .frame(width: 30, height: 30) - .padding(.leading,200) - } - } -} - -struct MyChatCustomView_Previews: PreviewProvider { - static var previews: some View { - MyChatCustomView() - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/ChatError.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/ChatError.swift deleted file mode 100644 index e1107d6d..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/ChatError.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// ChatError.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation - -enum ChatError: Error { - case unknown(source: Error?) -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockAttachment.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockAttachment.swift deleted file mode 100644 index 4a9e1169..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockAttachment.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// MockAttachment.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import ExyteChat - -struct MockImage { - let id: String - let thumbnail: URL - let full: URL - - func toChatAttachment() -> Attachment { - Attachment( - id: id, - thumbnail: thumbnail, - full: full, - type: .image - ) - } -} - -struct MockVideo { - let id: String - let thumbnail: URL - let full: URL - - func toChatAttachment() -> Attachment { - Attachment( - id: id, - thumbnail: thumbnail, - full: full, - type: .video - ) - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockMessage.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockMessage.swift deleted file mode 100644 index 0be42f76..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockMessage.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// MockMessage.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import ExyteChat - -struct MockMessage { - let uid: String - let sender: MockUser - let createdAt: Date - var status: Message.Status? - - let text: String - let images: [MockImage] - let videos: [MockVideo] - let recording: Recording? - let replyMessage: ReplyMessage? -} - -extension MockMessage { - func toChatMessage() -> ExyteChat.Message { - ExyteChat.Message( - id: uid, - user: sender.toChatUser(), - status: status, - createdAt: createdAt, - text: text, - attachments: images.map { $0.toChatAttachment() } + videos.map { $0.toChatAttachment() }, - recording: recording, - replyMessage: replyMessage - ) - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockUser.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockUser.swift deleted file mode 100644 index 972fa96a..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Data/MockUser.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// MockUser.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import ExyteChat - -struct MockUser: Equatable { - let uid: String - let name: String - let avatar: URL? - - init(uid: String, name: String, avatar: URL? = nil) { - self.uid = uid - self.name = name - self.avatar = avatar - } -} - -extension MockUser { - var isCurrentUser: Bool { - uid == "1" - } -} - -extension MockUser { - func toChatUser() -> ExyteChat.User { - ExyteChat.User(id: uid, name: name, avatarURL: avatar, isCurrentUser: isCurrentUser) - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/ChatInteractorProtocol.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/ChatInteractorProtocol.swift deleted file mode 100644 index 6e16af3b..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/ChatInteractorProtocol.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// ChatInteractorProtocol.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import Combine -import ExyteChat - -protocol ChatInteractorProtocol { - var messages: AnyPublisher<[MockMessage], Never> { get } - var senders: [MockUser] { get } - var otherSenders: [MockUser] { get } - - func send(draftMessage: ExyteChat.DraftMessage) - - func connect() - func disconnect() - - func loadNextPage() -> Future -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/MockChatInteractor.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/MockChatInteractor.swift deleted file mode 100644 index 9c1f634a..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Chat/MockChatInteractor.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// MockChatInteractor.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import Combine -import ExyteChat - -final class MockChatInteractor: ChatInteractorProtocol { - private lazy var chatData = MockChatData() - - private lazy var chatState = CurrentValueSubject<[MockMessage], Never>(generateStartMessages()) - private lazy var sharedState = chatState.share() - - private let isActive: Bool - private var isLoading = false - private var lastDate = Date() - - private var subscriptions = Set() - - var messages: AnyPublisher<[MockMessage], Never> { - sharedState.eraseToAnyPublisher() - } - - var senders: [MockUser] { - var members = [chatData.steve, chatData.tim] - if isActive { members.append(chatData.bob) } - return members - } - - var otherSenders: [MockUser] { - senders.filter { !$0.isCurrentUser } - } - - init(isActive: Bool = false) { - self.isActive = isActive - } - - /// TODO: Generate error with random chance - /// TODO: Save images from url to files. Imitate upload process - func send(draftMessage: ExyteChat.DraftMessage) { - if draftMessage.id != nil { - guard let index = chatState.value.firstIndex(where: { $0.uid == draftMessage.id }) else { - // TODO: Create error - return - } - chatState.value.remove(at: index) - } - - Task { - var status: Message.Status = .sending - if Int.random(min: 0, max: 20) == 0 { - status = .error(draftMessage) - } - let message = await draftMessage.toMockMessage(user: chatData.tim, status: status) - DispatchQueue.main.async { [weak self] in - self?.chatState.value.append(message) - } - } - } - - func connect() { - Timer.publish(every: 2, on: .main, in: .default) - .autoconnect() - .sink { [weak self] _ in - self?.updateSendingStatuses() - if self?.isActive ?? false { - self?.generateNewMessage() - } - } - .store(in: &subscriptions) - } - - func disconnect() { - subscriptions.removeAll() - } - - func loadNextPage() -> Future { - Future { [weak self] promise in - guard let self = self, !self.isLoading else { - promise(.success(false)) - return - } - self.isLoading = true - DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in - guard let self = self else { return } - let messages = self.generateStartMessages() - self.chatState.value = messages + self.chatState.value - self.isLoading = false - promise(.success(true)) - } - } - } -} - -private extension MockChatInteractor { - func generateStartMessages() -> [MockMessage] { - defer { - lastDate = lastDate.addingTimeInterval(-(60*60*24)) - } - return (0...10) - .map { index in - chatData.randomMessage(senders: senders, date: lastDate.randomTime()) - } - .sorted { lhs, rhs in - lhs.createdAt < rhs.createdAt - } - } - - func generateNewMessage() { - let message = chatData.randomMessage(senders: otherSenders) - chatState.value.append(message) - } - - func updateSendingStatuses() { - let updated = chatState.value.map { - var message = $0 - if message.status == .sending { - message.status = .sent - } else if message.status == .sent { - message.status = .read - } - return message - } - chatState.value = updated - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/Lorem.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/Lorem.swift deleted file mode 100644 index 0a6c9837..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/Lorem.swift +++ /dev/null @@ -1,289 +0,0 @@ -// -// Lorem.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation - -public class Lorem { - private static let wordList = [ - "안녕하세요", "반갑습니다", "감사합니다", "좋아요", "즐거워요", - "행복합니다", "축하합니다", "화이팅", "잘가요", "다시 만나요", - "사랑해요", "고마워요", "미안해요", "죄송합니다", "기쁘다", - "슬퍼요", "화나요", "힘내세요", "힘들어요", "좋은 하루 되세요", - "행운을 빕니다", "꿈을 이루세요", "건강하세요", "열심히 해봐요", - "맛있게 드세요", "즐거운 시간 보내세요", "잘 자요", "좋은 꿈 꾸세요", - "포기하지 마세요", "열심히 할게요", "맛있는 음식", "아름다운 자연", - "행복한 가정", "좋은 친구", "따뜻한 마음", "즐거운 여행", "힐링타임", - "재미있는 취미", "새로운 시작", "행복한 미래", "자유로운 삶", "평화로운 마음" - ] - - private static var markdownSymbols = ["*", "_", "**", "**"] - - /** - Return a random word. - - - returns: Returns a random word. - */ - public class func word() -> String { - return wordList.random()! - } - - /** - Return an array of `count` words. - - - parameter count: The number of words to return. - - - returns: Returns an array of `count` words. - */ - public class func words(nbWords: Int = 3) -> [String] { - return wordList.random(nbWords) - } - - /** - Return a string of `count` words. - - - parameter count: The number of words the string should contain. - - - returns: Returns a string of `count` words. - */ - public class func words(nbWords: Int = 3, useMarkdown: Bool = false) -> String { - words(nbWords: nbWords) - .map { - guard useMarkdown, Int.random(min: 0, max: 10) == 0 else { - return $0 - } - let symbol = markdownSymbols.random()! - return symbol + $0 + symbol - } - .joined(separator: " ") - } - - /** - Generate a sentence of `nbWords` words. - - parameter nbWords: The number of words the sentence should contain. - - parameter variable: If `true`, the number of words will vary between - +/- 40% of `nbWords`. - - returns: - */ - public class func sentence(nbWords: Int = 6, variable: Bool = true, useMarkdown: Bool = false) -> String { - if nbWords <= 0 { - return "" - } - - let result: String = words( - nbWords: variable ? nbWords.randomize(variation: 40) : nbWords, - useMarkdown: useMarkdown - ) - - return result.firstCapitalized + "." - } - - /** - Generate an array of sentences. - - parameter nbSentences: The number of sentences to generate. - - - returns: Returns an array of random sentences. - */ - public class func sentences(nbSentences: Int = 3) -> [String] { - return (0.. String { - if nbSentences <= 0 { - return "" - } - - return sentences(nbSentences: variable ? nbSentences.randomize(variation: 40) : nbSentences).joined(separator: " ") - } - - /** - Generate an array of random paragraphs. - - parameter nbParagraphs: The number of paragraphs to generate. - - returns: Returns an array of `nbParagraphs` paragraphs. - */ - public class func paragraphs(nbParagraphs: Int = 3) -> [String] { - return (0.. String { - return paragraphs(nbParagraphs: nbParagraphs).joined(separator: "\n\n") - } - - /** - Generate a string of at most `maxNbChars` characters. - - parameter maxNbChars: The maximum number of characters the string - should contain. - - returns: Returns a string of at most `maxNbChars` characters. - */ - public class func text(maxNbChars: Int = 200) -> String { - var result: [String] = [] - - if maxNbChars < 5 { - return "" - } else if maxNbChars < 25 { - while result.count == 0 { - var size = 0 - - while size < maxNbChars { - let w = (size != 0 ? " " : "") + word() - result.append(w) - size += w.count - } - - _ = result.popLast() - } - } else if maxNbChars < 100 { - while result.count == 0 { - var size = 0 - - while size < maxNbChars { - let s = (size != 0 ? " " : "") + sentence() - result.append(s) - size += s.count - } - - _ = result.popLast() - } - } else { - while result.count == 0 { - var size = 0 - - while size < maxNbChars { - let p = (size != 0 ? "\n" : "") + paragraph() - result.append(p) - size += p.count - } - - _ = result.popLast() - } - } - - return result.joined(separator: "") - } -} - -extension String { - var firstCapitalized: String { - var string = self - string.replaceSubrange(string.startIndex...string.startIndex, with: String(string[string.startIndex]).capitalized) - return string - } -} - -public extension Array { - /** - Shuffle the array in-place using the Fisher-Yates algorithm. - */ - mutating func shuffle() { - if count == 0 { - return - } - - for i in 0..<(count - 1) { - let j = Int(arc4random_uniform(UInt32(count - i))) + i - if j != i { - self.swapAt(i, j) - } - } - } - - /** - Return a shuffled version of the array using the Fisher-Yates - algorithm. - - - returns: Returns a shuffled version of the array. - */ - func shuffled() -> [Element] { - var list = self - list.shuffle() - - return list - } - - /** - Return a random element from the array. - - returns: Returns a random element from the array or `nil` if the - array is empty. - */ - func random() -> Element? { - return (count > 0) ? self.shuffled()[0] : nil - } - - /** - Return a random subset of `cnt` elements from the array. - - returns: Returns a random subset of `cnt` elements from the array. - */ - func random(_ count: Int = 1) -> [Element] { - let result = shuffled() - - return (count > result.count) ? result : Array(result[0.. Int { - precondition(min <= max, "attempt to call random() with min > max") - - let diff = UInt(bitPattern: max &- min) - let result = UInt.random(min: 0, max: diff) - - return min + Int(bitPattern: result) - } - - public func randomize(variation: Int) -> Int { - let multiplier = Double(Int.random(min: 100 - variation, max: 100 + variation)) / 100 - let randomized = Double(self) * multiplier - - return Int(randomized) + 1 - } -} - -private extension UInt { - static func random(min: UInt, max: UInt) -> UInt { - precondition(min <= max, "attempt to call random() with min > max") - - if min == UInt.min && max == UInt.max { - var result: UInt = 0 - arc4random_buf(&result, MemoryLayout.size(ofValue: result)) - - return result - } else { - let range = max - min + 1 - let limit = UInt.max - UInt.max % range - var result: UInt = 0 - - repeat { - arc4random_buf(&result, MemoryLayout.size(ofValue: result)) - } while result >= limit - - result = result % range - - return min + result - } - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/MockChatData.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/MockChatData.swift deleted file mode 100644 index 2e9a3740..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Drivers/Data Generation/MockChatData.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// MockChatData.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import UIKit - -final class MockChatData { - - // Alternative for avatars `https://ui-avatars.com/api/?name=Tim` - let system = MockUser(uid: "0", name: "System") - let tim = MockUser( - uid: "1", - name: "Tim", - avatar: AssetExtractor.createLocalUrl(forImageNamed: "tim")! - ) - let steve = MockUser( - uid: "2", - name: "Steve", - avatar: AssetExtractor.createLocalUrl(forImageNamed: "steve")! - ) - let bob = MockUser( - uid: "3", - name: "Bob", - avatar: AssetExtractor.createLocalUrl(forImageNamed: "bob")! - ) - - func randomMessage(senders: [MockUser] = [], date: Date? = nil) -> MockMessage { - let senders = senders.isEmpty ? [tim, steve, bob] : senders - let sender = senders.random()! - let date = date ?? Date() - let images = randomImages() - - let shouldGenerateText = images.isEmpty ? true : .random() - - return MockMessage( - uid: UUID().uuidString, - sender: sender, - createdAt: date, - status: sender.isCurrentUser ? .read : nil, - text: shouldGenerateText ? Lorem.sentence(nbWords: Int.random(in: 3...10), useMarkdown: true) : "", - images: images, - videos: [], - recording: nil, - replyMessage: nil - ) - } - - func randomImages() -> [MockImage] { - guard Int.random(min: 0, max: 10) == 0 else { - return [] - } - - let count = Int.random(min: 1, max: 5) - return (0...count).map { _ in - randomMockImage() - } - } - - func randomMockImage() -> MockImage { - let color = randomColorHex() - return MockImage( - id: UUID().uuidString, - thumbnail: URL(string: "https://via.placeholder.com/150/\(color)")!, - full: URL(string: "https://via.placeholder.com/600/\(color)")! - ) - } - - func randomColorHex() -> String { - (0...6) - .map { _ in randomHexChar() } - .joined() - } -} - -private extension MockChatData { - func randomHexChar() -> String { - let letters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"] - return letters.random()! - } -} - -class AssetExtractor { - - static func createLocalUrl(forImageNamed name: String) -> URL? { - - let fileManager = FileManager.default - let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0] - let url = cacheDirectory.appendingPathComponent("\(name).pdf") - - guard fileManager.fileExists(atPath: url.path) else { - guard - let image = UIImage(named: name), - let data = image.pngData() - else { return nil } - - fileManager.createFile(atPath: url.path, contents: data, attributes: nil) - return url - } - - return url - } - -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Extension/Date+Random.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Extension/Date+Random.swift deleted file mode 100644 index 20e104ab..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/Extension/Date+Random.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Date+Random.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation - -extension Date { - func randomTime() -> Date { - var components = Calendar.current.dateComponents([.year, .month, .day], from: self) - components.hour = Int.random(min: 0, max: 23) - components.minute = Int.random(min: 0, max: 59) - components.second = Int.random(min: 0, max: 59) - - return Calendar.current.date(from: components)! - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleView.swift deleted file mode 100644 index adf2ca6e..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleView.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// ChatExampleView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import SwiftUI -import ExyteChat - -struct ChatExampleView: View { - - @StateObject private var viewModel: ChatExampleViewModel - - private let title: String - - init(viewModel: ChatExampleViewModel = ChatExampleViewModel(), title: String) { - _viewModel = StateObject(wrappedValue: viewModel) - self.title = title - } - - var body: some View { - - ChatView(messages: viewModel.messages) { draft in - viewModel.send(draft: draft) - } - .enableLoadMore(offset: 3) { message in - viewModel.loadMoreMessage(before: message) - } - .messageUseMarkdown(messageUseMarkdown: true) - .chatNavigation( - title: viewModel.chatTitle, - status: viewModel.chatStatus, - cover: viewModel.chatCover - ) - .mediaPickerTheme( - main: .init( - text: .pink, - albumSelectionBackground: .black, - fullscreenPhotoBackground: .basicBlack - ), - selection: .init( - emptyTint: .white, - emptyBackground: .black.opacity(0.25), - selectedTint: .basicBlack, - fullscreenTint: .white - ) - ) - .onAppear(perform: viewModel.onStart) - .onDisappear(perform: viewModel.onStop) - } -} - - -#Preview { - ChatExampleView(viewModel: .init(), title: "") -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleViewModel.swift deleted file mode 100644 index 88ca64a4..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatExampleViewModel.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// ChatExampleViewModel.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import Combine -import ExyteChat -import ExyteMediaPicker - -final class ChatExampleViewModel: ObservableObject { - @Published var messages: [Message] = [] - - var chatTitle: String { - interactor.otherSenders.count == 1 ? interactor.otherSenders.first!.name : "Group chat" - } - var chatStatus: String { - interactor.otherSenders.count == 1 ? "online" : "\(interactor.senders.count) members" - } - var chatCover: URL? { - interactor.otherSenders.count == 1 ? interactor.otherSenders.first!.avatar : nil - } - - private let interactor: ChatInteractorProtocol - private var subscriptions = Set() - - init(interactor: ChatInteractorProtocol = MockChatInteractor()) { - self.interactor = interactor - } - - func send(draft: DraftMessage) { - interactor.send(draftMessage: draft) - } - - func onStart() { - interactor.messages - .compactMap { messages in - messages.map { $0.toChatMessage() } - } - .assign(to: &$messages) - - interactor.connect() - } - - func onStop() { - interactor.disconnect() - } - - func loadMoreMessage(before message: Message) { - interactor.loadNextPage() - .sink { _ in } - .store(in: &subscriptions) - } -} - -extension DraftMessage { - func makeMockImages() async -> [MockImage] { - await medias - .filter { $0.type == .image } - .asyncMap { (media : Media) -> (Media, URL?, URL?) in - (media, await media.getThumbnailURL(), await media.getURL()) - } - .filter { (media: Media, thumb: URL?, full: URL?) -> Bool in - thumb != nil && full != nil - } - .map { media, thumb, full in - MockImage(id: media.id.uuidString, thumbnail: thumb!, full: full!) - } - } - - func makeMockVideos() async -> [MockVideo] { - await medias - .filter { $0.type == .video } - .asyncMap { (media : Media) -> (Media, URL?, URL?) in - (media, await media.getThumbnailURL(), await media.getURL()) - } - .filter { (media: Media, thumb: URL?, full: URL?) -> Bool in - thumb != nil && full != nil - } - .map { media, thumb, full in - MockVideo(id: media.id.uuidString, thumbnail: thumb!, full: full!) - } - } - - func toMockMessage(user: MockUser, status: Message.Status = .read) async -> MockMessage { - MockMessage( - uid: id ?? UUID().uuidString, - sender: user, - createdAt: createdAt, - status: user.isCurrentUser ? status : nil, - text: text, - images: await makeMockImages(), - videos: await makeMockVideos(), - recording: recording, - replyMessage: replyMessage - ) - } -} - -extension Sequence { - func asyncMap( - _ transform: (Element) async throws -> T - ) async rethrows -> [T] { - var values = [T]() - - for element in self { - try await values.append(transform(element)) - } - - return values - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatTitleView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatTitleView.swift deleted file mode 100644 index fd6d9995..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChatTitleView.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// TestView.swift -// RollTheDice -// -// Created by 신예진 on 2/9/24. -// - -import SwiftUI - -//왼쪽 TITLE 눌렀을 때 들어가는 View -struct ChatTitleView: View { - @State private var selectedTitle: String = "" - @State private var selectedIndex: Int? - - var body: some View { - VStack { - ForEach(0..<20) { index in - Button(action: { - selectedTitle = "여기에채팅제목이뜹니다. \(index)" - selectedIndex = index - }) { - Text("여기에채팅제목이뜹니다. \(index)") - .foregroundColor(selectedIndex == index ? .white : .primary) - .padding() - } - .background(selectedIndex == index ? Color.primary01 : Color.black) - .cornerRadius(8) - .padding(.horizontal) - } - } - } -} - -struct ChatTitleView_Previews: PreviewProvider { - static var previews: some View { - ChatTitleView() - } -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChattingView.swift b/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChattingView.swift deleted file mode 100644 index 7948b3ad..00000000 --- a/iOS/RollTheDice/RollTheDice/Source/View/Chat/View/ChatTestView/View/ChattingView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ChattingView.swift -// RollTheDice -// -// Created by 신예진 on 2/12/24. -// - -import Foundation -import SwiftUI - -struct ChattingView: View { - - var body: some View { - HStack { - ChatTitleView() - - Spacer() - - ChatExampleView(title: "ChattingView") - } - } - - -} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift b/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift index 91f83498..8f6bf0cb 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/MainTab/MainTabView.swift @@ -9,49 +9,49 @@ import SwiftUI struct MainTabView: View { - @StateObject private var mainTabViewModel = MainTabViewModel() - + @EnvironmentObject private var pathModel: PathModel @EnvironmentObject private var newsListViewModel: NewsListViewModel + @StateObject private var mainTabViewModel = MainTabViewModel() + var body: some View { ZStack { Color.backgroundDark .ignoresSafeArea(.all) - NavigationStack { - TabView(selection: $mainTabViewModel.selectedTabItem) { - NewsListView() - .tabItem { - Image(systemName: "newspaper") - } - .environmentObject(NewsListViewModel()) - .tag(0) - ChatTypeView() - .tabItem { - Image(systemName: "message") - } - .tag(1) - BookmarkListView() - .tabItem { - Image(systemName: "bookmark") - } - .environmentObject(BookmarkListViewModel()) - .tag(2) - - ARView() - .tabItem { - Image(systemName: "square.stack.3d.up.fill") - } - .tag(3) - - ProfileView() - .tabItem { - Image(systemName: "person.crop.circle") - } - .tag(4) - } + TabView(selection: $mainTabViewModel.selectedTabItem) { + NewsListView() + .tabItem { + Image(systemName: "newspaper") + } + .environmentObject(newsListViewModel) + .tag(0) + ChatTypeView() + .tabItem { + Image(systemName: "message") + } + .tag(1) + BookmarkListView() + .tabItem { + Image(systemName: "bookmark") + } + .environmentObject(BookmarkListViewModel()) + .tag(2) + + ARView() + .tabItem { + Image(systemName: "square.stack.3d.up.fill") + } + .tag(3) + + ProfileView() + .tabItem { + Image(systemName: "person.crop.circle") + } + .tag(4) } } + } } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsCard/NewsView.swift b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsCard/NewsView.swift index 0c1c86a1..9203393c 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsCard/NewsView.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsCard/NewsView.swift @@ -23,6 +23,7 @@ struct NewsView: View { .padding(.top, 50) HStack { Spacer() +// .frame(height: 10) Text(news.date) .font(.system(size: 12, weight: .bold)) .foregroundStyle(.gray05) @@ -30,11 +31,17 @@ struct NewsView: View { Spacer() .frame(height: 10) - Image(news.image) - .frame(width: 320, height: 190) - .clipShape( - RoundedRectangle(cornerRadius: 15) - ) + AsyncImage(url: URL(string: news.image)) { image in + image + .resizable() + .scaledToFill() + .frame(maxHeight: 150) + .clipShape(RoundedRectangle(cornerRadius: 15)) + + } placeholder: { + Image("photo.circle.fill") + } + Spacer() .frame(height: 30) @@ -73,7 +80,7 @@ struct NewsView: View { } #Preview(traits: .sizeThatFitsLayout) { - NewsView(news: .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "exampleNews", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false)) + NewsView(news: .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "https://imgnews.pstatic.net/image/008/2024/03/05/0005007355_001_20240305100101016.jpg?type=w647", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false)) .previewInterfaceOrientation(.landscapeLeft) .previewLayout(.sizeThatFits) } diff --git a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsListViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsListViewModel.swift index 94367b11..5d9e5b0f 100644 --- a/iOS/RollTheDice/RollTheDice/Source/View/News/NewsListViewModel.swift +++ b/iOS/RollTheDice/RollTheDice/Source/View/News/NewsListViewModel.swift @@ -12,10 +12,10 @@ public class NewsListViewModel: ObservableObject { init( newsList: [News] = [ - .init(title: "2024년 ‘소셜 미디어 다이어트’를 위해 바꿔볼 것", date: "2023년12월3일", image: "exampleNews", content: "2024년으로 접어든지 한 달이 넘었다. 하지만 올 해가 어떻게 흘러갈지 예측하기는 쉽지 않다. 한 가지 확실한 것은 정치적으로 매우 중요한 해라는 점이다. 미국과 러시아, 우크라이나, 방글라데시, 인도, 대만, 한국, 남아프리카공화국, 유럽의회, 영국에서 선거가 치러질 예정이다.", isBookmarked: false), - .init(title: "해외원정 가던 줄기세포치료 이젠 국내서도 받을 수 있다", date: "2023년2월13일", image: "exampleNews", content: "첨생법 개정안이 이달 1일 국회를 통과해 내년부터 시행된다. 이에 따라 식약처 허가 없이도 안전성·유효성이 확인되면 국내에서도 첨단재생의료 치료가 허용되고 모든 질환에 대한 임상연구가 가능해졌다. ", isBookmarked: true), - .init(title: "홀로 선 자립준비청년, 배곯지 않게…우체국, 매일 식비 지원", date: "2023년2월13일", image: "exampleNews", content: "과학기술정보통신부 우정사업본부는 사회에 첫발을 내딛는 자립준비청년이 건강한 사회구성원으로 성장하도록 식비를 지원하는 '우체국 청년밥심 스타트 온(溫)' 사업을 확대 추진한다고 14일 밝혔다.", isBookmarked: false), - .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "exampleNews", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), + .init(title: "네이버, 사우디 'LEAP 2024'서 AI·로봇 등 자사 기술력 뽐낸다", date: "2023년3월3일", image: "https://imgnews.pstatic.net/image/008/2024/03/05/0005007355_001_20240305100101016.jpg?type=w647", content: "네이버(NAVER)는 사우디아라비아에서 지난 4일부터 7일까지 열리는 글로벌 IT전시회 LEAP 2024에서 AI(인공지능), 클라우드, 로봇 등 자사 핵심 기술을 선보이고, 글로벌 업체들과 비즈니스 협력을 강화한다고 5일 밝혔다.", isBookmarked: false), + .init(title: "앤스로픽 최신 AI 모델 '클로드3' 출시", date: "2023년3월13일", image: "https://imgnews.pstatic.net/image/014/2024/03/05/0005151141_001_20240305101610000.jpg?type=w647", content: "오픈AI의 대항마 앤스로픽이 생성형 인공지능(AI) 최신 모델 '클로드'(Claude)3를 선보이면서 생성형 AI 주도권을 잡기 위한 경쟁이 다시 뜨거워지고 있다. 앤스로픽은 지난 한 해 동안 구글과 세일즈포스, 아마존 등에서 총 73억 달러(약 9조7309억 원)를 투자받고 '클로드3'를 내놨는데 오픈AI의 챗GPT-4를 능가한다고 도발했다.", isBookmarked: true), + .init(title: "SK C&C, 외부 전문가 대거 영입… “신성장 동력 강화”", date: "2023년2월13일", image: "https://imgnews.pstatic.net/image/366/2024/03/05/0000975131_001_20240305093504301.jpg?type=w647", content: "SK C&C는 국내외 신성장 동력 강화를 위해 인공지능(AI)·클라우드·디지털 팩토리·ESG(환경·사회·지배구조) 등 4대 성장 사업과 디지털 컨설팅 중심으로 외부 전문가를 대거 영입해 전진 배치했다고 5일 밝혔다.", isBookmarked: false), + .init(title: "NHN, 작년 영업익 555억원...전년비 42% ↑", date: "2023년2월13일", image: "https://cdnimage.dailian.co.kr/news/202402/news_1707866329_1327972_m_1.png", content: "2NHN은 연결기준 지난해 영업이익이 555억원으로 전년 대비 42.2% 증가했다고 14일 밝혔다.같은 기간 매출은 7.3% 증가한 2조2696억원으로 연간 최대치를 기록했다. 작년 4분기 매출은 5983억원으로 전년 동기 대비 6.7% 올랐다. 반면 영업손실은 78억원으로 적자전환했다. 커머스 부문의 장기 미회수채권 대손상각비 인식과 기술 부문의 기 인식 매출 차감 등 일회성 요인이 영향을 미쳤다.", isBookmarked: false), ] ) { self.newsList = newsList diff --git a/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpFinishView.swift b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpFinishView.swift new file mode 100644 index 00000000..df1ed229 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpFinishView.swift @@ -0,0 +1,18 @@ +// +// SignUpFinishView.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import SwiftUI + +struct SignUpFinishView: View { + var body: some View { + Text("test 끝") + } +} + +#Preview { + SignUpFinishView() +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpQuestionView.swift b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpQuestionView.swift new file mode 100644 index 00000000..493fc761 --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpQuestionView.swift @@ -0,0 +1,140 @@ +// +// SignUpViewModel.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import SwiftUI + +struct SignUpQuestionView: View { + +// @StateObject var p + @StateObject var viewModel = SignUpViewModel() + @State var process: Int = 1 + @State var isSelected: Bool = false + + var body: some View { + if viewModel.hasDoneTest { + + SignUpFinishView() + } else { + VStack { + CustomNavigationBar() + ProcessView(process: $process) + + VStack { + Spacer() + QuestionView(viewModel: viewModel, process: $process, isSelected: $isSelected) + Spacer() + NextButtonView(process: $process, isSelected: $isSelected) + } + .padding(.horizontal, 15) + } + } + } + + +} + +private struct ProcessView: View { + @Binding var process: Int + + fileprivate var body: some View { + VStack { + ZStack { + Rectangle() + .frame(height: 10) + HStack { + Rectangle() + .frame(width: CGFloat(process) * 180 ,height: 10) + .foregroundStyle(.primary01) + .animation(.bouncy, value: process) + Spacer() + } + } + } + } +} + +private struct QuestionView: View { + @ObservedObject var viewModel: SignUpViewModel + @Binding var process: Int + @State var question: String = "" + @State var options: [String] = [] + @State var selection: Int = -1 /// 선택한 버튼의 index + @Binding var isSelected: Bool + + fileprivate var body: some View { + VStack { + + Spacer() + HStack { + Text(question) + .font(.largeTitle) + .foregroundStyle(.basicWhite) + Spacer() + } + Spacer() + HStack(spacing: 10) { + ForEach(options.indices, id: \.self) { q in + Button { + selection = q + isSelected = true + } label: { + RoundedRectangle(cornerRadius: 15) + .stroke(lineWidth: 1.0) + .foregroundStyle(q == selection ? .primary01 : .basicWhite) + .frame(height: 200) + .overlay { + Text(options[q]) + .font(.title) + .foregroundStyle(.basicWhite) + } + } + } + } + + Spacer() + } + .onAppear(perform: { + question = viewModel.questions[process].question + options = viewModel.questions[process].options + }) + .onChange(of: process) { + if process < 6 && process > 1 { + question = viewModel.questions[process].question + options = viewModel.questions[process].options + selection = -1 + isSelected = false + } else if process == 6 { + viewModel.hasDoneTest = true + } + } + } +} + +private struct NextButtonView: View { + @Binding var process: Int + @Binding var isSelected: Bool + + fileprivate var body: some View { + Button { + process += 1 + } label: { + RoundedRectangle(cornerRadius: 15) + .frame(height: 80) + .foregroundStyle(isSelected ? .primary01 : .gray05) + + .overlay { + Text("확인") + .foregroundStyle(.basicWhite) + } + } + .disabled(!isSelected) + } +} + +#Preview { + SignUpQuestionView() +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpView.swift b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpView.swift new file mode 100644 index 00000000..e0576c6a --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpView.swift @@ -0,0 +1,45 @@ +// +// SignUpView.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import SwiftUI + +struct SignUpView: View { + var body: some View { + ZStack { + Color.backgroundDark.ignoresSafeArea(.all) + VStack { + Spacer() + + Button { + + } label: { + Image(.appleLoginBtn) + } + + Button { + + } label: { + Image(.kakaoLoginBtn) + } + Spacer() + .frame(height: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/) + } + } + } +} + +private struct LoginView: View { + fileprivate var body: some View { + VStack { + + } + } +} + +#Preview { + SignUpView() +} diff --git a/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpViewModel.swift b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpViewModel.swift new file mode 100644 index 00000000..05e5386a --- /dev/null +++ b/iOS/RollTheDice/RollTheDice/Source/View/SignUp/SignUpViewModel.swift @@ -0,0 +1,36 @@ +// +// SignUpViewModel.swift +// RollTheDice +// +// Created by Subeen on 3/10/24. +// + +import Foundation + +class SignUpViewModel: ObservableObject { + @Published var hasDoneTest = false + @Published var questions : [Question] = [ + .init(question: "닉네임을 입력해주세요.", page: 0), + .init(question: "연령대를 선택해주세요.", options: ["10대", "20대", "30대", "40대", "50대 이상"], page: 1), + .init(question: "성별을 입력해주세요", options: ["여성", "남성", "밝히지 않음"], page: 2), + .init(question: "선호하는 기사 주제를 선택해주세요. (최대 3개)", options: ["정치", "자연과학", "뭐있지", "여러뉴스", "웅냥", "정성찬", "RIIZE", "흠냐링"], page: 3), + .init(question: "뉴스 보는 주기를 선택해주세요.", options: ["여성", "남성", "밝히지 않음"], page: 4), + .init(question: "앱을 사용하는 목적을 선택해주세요.", options: ["여성", "남성", "밝히지 않음"], page: 6), + ] +} + + + +struct Question: Hashable { + + var question: String + var options: [String] = [] + var page: Int + +// struct Option { +// var text: String +// var isSelected: Bool = false +// var nickname: String = "" +// } +} +