diff --git a/HongikYeolgong2.xcodeproj/project.pbxproj b/HongikYeolgong2.xcodeproj/project.pbxproj index d3f5065..a697307 100644 --- a/HongikYeolgong2.xcodeproj/project.pbxproj +++ b/HongikYeolgong2.xcodeproj/project.pbxproj @@ -42,11 +42,10 @@ 475B86DF2CA1AF1E000534B2 /* RecordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475B86DE2CA1AF1E000534B2 /* RecordView.swift */; }; 475B86E12CA1AF31000534B2 /* RankingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475B86E02CA1AF31000534B2 /* RankingView.swift */; }; 475B86E32CA1AF40000534B2 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475B86E22CA1AF40000534B2 /* SettingView.swift */; }; - 475B86E72CA1CC20000534B2 /* HY2TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475B86E62CA1CC20000534B2 /* HY2TextField.swift */; }; 4763FFAF2CB90AE000990336 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFAE2CB90AE000990336 /* AppState.swift */; }; 4763FFB12CB90C1500990336 /* DependencyInjector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB02CB90C1500990336 /* DependencyInjector.swift */; }; 4763FFB32CB90E3800990336 /* InteractorsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB22CB90E3800990336 /* InteractorsContainer.swift */; }; - 4763FFB52CB90EBD00990336 /* InitialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB42CB90EBD00990336 /* InitialView.swift */; }; + 4763FFB52CB90EBD00990336 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB42CB90EBD00990336 /* RootView.swift */; }; 4763FFB82CB9134D00990336 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB72CB9134D00990336 /* Store.swift */; }; 4763FFBA2CB913FF00990336 /* UserDataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFB92CB913FF00990336 /* UserDataInteractor.swift */; }; 4763FFBC2CB9228B00990336 /* AppEnviroment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4763FFBB2CB9228B00990336 /* AppEnviroment.swift */; }; @@ -66,6 +65,9 @@ 4786A1102CCA351A008635A4 /* WeeklyStudySessionDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4786A10F2CCA351A008635A4 /* WeeklyStudySessionDTO.swift */; }; 478933792CADBDA500E1D89E /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478933782CADBDA500E1D89E /* SignUpView.swift */; }; 4789337B2CADC43D00E1D89E /* DropDownPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4789337A2CADC43D00E1D89E /* DropDownPicker.swift */; }; + 478B75442CE65594000190EF /* DuplicateCheckButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478B75432CE65594000190EF /* DuplicateCheckButton.swift */; }; + 478B75462CE659C3000190EF /* BaseTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478B75452CE659C3000190EF /* BaseTextField.swift */; }; + 478B75502CE679A8000190EF /* SubmitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478B754F2CE679A8000190EF /* SubmitButton.swift */; }; 478F84372CD30A8F0097CAA1 /* IOSBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478F84362CD30A8F0097CAA1 /* IOSBackground.swift */; }; 478F84392CD3176E0097CAA1 /* RankingDataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478F84382CD3176E0097CAA1 /* RankingDataInteractor.swift */; }; 478F843C2CD326DD0097CAA1 /* WeeklyRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478F843B2CD326DD0097CAA1 /* WeeklyRepositoryImpl.swift */; }; @@ -78,7 +80,7 @@ 478FBCA32CD13A1E00256012 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478FBCA22CD13A1E00256012 /* UserInfo.swift */; }; 479821D42CA24CFF002357EB /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 479821D32CA24CFF002357EB /* .swiftlint.yml */; }; 479821D72CA27BE1002357EB /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 479821D62CA27BE1002357EB /* Launch Screen.storyboard */; }; - 479BF66C2CDD1A8D009D9E44 /* StudyNotificationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479BF66B2CDD1A8D009D9E44 /* StudyNotificationType.swift */; }; + 479BF66C2CDD1A8D009D9E44 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479BF66B2CDD1A8D009D9E44 /* LocalNotification.swift */; }; 47A1474E2CA144EC00A91F66 /* StudyRoomUsage+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A1474D2CA144EC00A91F66 /* StudyRoomUsage+Mock.swift */; }; 47A147632CA147A600A91F66 /* Pretendard-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = 47A147512CA147A600A91F66 /* Pretendard-Black.otf */; }; 47A147642CA147A600A91F66 /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 47A147522CA147A600A91F66 /* Pretendard-Bold.otf */; }; @@ -125,7 +127,7 @@ 47CA17252CC9336100CBB251 /* StudyPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */; }; 47CA17272CC9340800CBB251 /* StudyTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17262CC9340800CBB251 /* StudyTimerView.swift */; }; 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */; }; - 47D5EDCF2CCBD10300ACA469 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCE2CCBD10300ACA469 /* ActionButton.swift */; }; + 47D5EDCF2CCBD10300ACA469 /* BaseButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCE2CCBD10300ACA469 /* BaseButton.swift */; }; 47DF041F2CBE7A29007E58A7 /* Encodable+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47DF041E2CBE7A29007E58A7 /* Encodable+.swift */; }; 47E250712CCF274400267897 /* WeeklyStudyInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E250702CCF274400267897 /* WeeklyStudyInteractor.swift */; }; 47E250732CCF2BED00267897 /* WiseSaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E250722CCF2BED00267897 /* WiseSaying.swift */; }; @@ -145,13 +147,13 @@ 47F71B632CDC813A0044DEC5 /* FirebaseFunctionsCombine-Community in Frameworks */ = {isa = PBXBuildFile; productRef = 47F71B622CDC813A0044DEC5 /* FirebaseFunctionsCombine-Community */; }; 47F79B2E2CCCB7AA00DD0899 /* TimePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F79B2D2CCCB7AA00DD0899 /* TimePickerView.swift */; }; 47F79B302CCCB7FC00DD0899 /* TimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F79B2F2CCCB7FC00DD0899 /* TimePicker.swift */; }; + 47FDF21F2CE712A3005CCB74 /* Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47FDF21E2CE712A3005CCB74 /* Form.swift */; }; 5E847A6C2CDBD49A0034C2A7 /* CalendarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E847A6B2CDBD49A0034C2A7 /* CalendarCell.swift */; }; 5E847A6E2CDBD4B60034C2A7 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E847A6D2CDBD4B60034C2A7 /* CalendarView.swift */; }; 5E847A702CDBD4F10034C2A7 /* CalendarMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E847A6F2CDBD4F10034C2A7 /* CalendarMapper.swift */; }; 5E847A742CDBD52E0034C2A7 /* CalendarDataInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E847A732CDBD52E0034C2A7 /* CalendarDataInteractor.swift */; }; 5E847A772CDBD5590034C2A7 /* AllStudyRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E847A762CDBD5590034C2A7 /* AllStudyRecord.swift */; }; 5EAE88722CDF9B4500DCBA31 /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EAE88712CDF9B4500DCBA31 /* UserProfile.swift */; }; - 5ED93CD72CE2311D004A7931 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5ED93CD62CE2311D004A7931 /* GoogleService-Info.plist */; }; 5ED93CDB2CE3690D004A7931 /* Secrets-prod.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 5ED93CDA2CE3690D004A7931 /* Secrets-prod.xcconfig */; }; 98B5F00A2CDB362C007CF5FA /* Secrets-dev.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 98B5F0092CDB362C007CF5FA /* Secrets-dev.xcconfig */; }; 98B5F00C2CDB6226007CF5FA /* StudyTimeResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98B5F00B2CDB6226007CF5FA /* StudyTimeResponseDTO.swift */; }; @@ -223,11 +225,10 @@ 475B86DE2CA1AF1E000534B2 /* RecordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordView.swift; sourceTree = ""; }; 475B86E02CA1AF31000534B2 /* RankingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingView.swift; sourceTree = ""; }; 475B86E22CA1AF40000534B2 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = ""; }; - 475B86E62CA1CC20000534B2 /* HY2TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HY2TextField.swift; sourceTree = ""; }; 4763FFAE2CB90AE000990336 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = ""; }; 4763FFB02CB90C1500990336 /* DependencyInjector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyInjector.swift; sourceTree = ""; }; 4763FFB22CB90E3800990336 /* InteractorsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractorsContainer.swift; sourceTree = ""; }; - 4763FFB42CB90EBD00990336 /* InitialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialView.swift; sourceTree = ""; }; + 4763FFB42CB90EBD00990336 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; 4763FFB72CB9134D00990336 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; 4763FFB92CB913FF00990336 /* UserDataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDataInteractor.swift; sourceTree = ""; }; 4763FFBB2CB9228B00990336 /* AppEnviroment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppEnviroment.swift; sourceTree = ""; }; @@ -247,6 +248,9 @@ 4786A10F2CCA351A008635A4 /* WeeklyStudySessionDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeeklyStudySessionDTO.swift; sourceTree = ""; }; 478933782CADBDA500E1D89E /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; 4789337A2CADC43D00E1D89E /* DropDownPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownPicker.swift; sourceTree = ""; }; + 478B75432CE65594000190EF /* DuplicateCheckButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuplicateCheckButton.swift; sourceTree = ""; }; + 478B75452CE659C3000190EF /* BaseTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTextField.swift; sourceTree = ""; }; + 478B754F2CE679A8000190EF /* SubmitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitButton.swift; sourceTree = ""; }; 478F84362CD30A8F0097CAA1 /* IOSBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOSBackground.swift; sourceTree = ""; }; 478F84382CD3176E0097CAA1 /* RankingDataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingDataInteractor.swift; sourceTree = ""; }; 478F843B2CD326DD0097CAA1 /* WeeklyRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeeklyRepositoryImpl.swift; sourceTree = ""; }; @@ -259,7 +263,7 @@ 478FBCA22CD13A1E00256012 /* UserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfo.swift; sourceTree = ""; }; 479821D32CA24CFF002357EB /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 479821D62CA27BE1002357EB /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; - 479BF66B2CDD1A8D009D9E44 /* StudyNotificationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyNotificationType.swift; sourceTree = ""; }; + 479BF66B2CDD1A8D009D9E44 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = ""; }; 47A147322CA136CC00A91F66 /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; 47A1474D2CA144EC00A91F66 /* StudyRoomUsage+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StudyRoomUsage+Mock.swift"; sourceTree = ""; }; 47A147502CA147A600A91F66 /* .gitkeep */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitkeep; sourceTree = ""; }; @@ -310,7 +314,7 @@ 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPeriodView.swift; sourceTree = ""; }; 47CA17262CC9340800CBB251 /* StudyTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerView.swift; sourceTree = ""; }; 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBackground.swift; sourceTree = ""; }; - 47D5EDCE2CCBD10300ACA469 /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; }; + 47D5EDCE2CCBD10300ACA469 /* BaseButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseButton.swift; sourceTree = ""; }; 47DF041E2CBE7A29007E58A7 /* Encodable+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+.swift"; sourceTree = ""; }; 47E250702CCF274400267897 /* WeeklyStudyInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeeklyStudyInteractor.swift; sourceTree = ""; }; 47E250722CCF2BED00267897 /* WiseSaying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiseSaying.swift; sourceTree = ""; }; @@ -322,13 +326,13 @@ 47F71B5C2CDC77C60044DEC5 /* UserDataInteractor+Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDataInteractor+Migration.swift"; sourceTree = ""; }; 47F79B2D2CCCB7AA00DD0899 /* TimePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePickerView.swift; sourceTree = ""; }; 47F79B2F2CCCB7FC00DD0899 /* TimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePicker.swift; sourceTree = ""; }; + 47FDF21E2CE712A3005CCB74 /* Form.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Form.swift; sourceTree = ""; }; 5E847A6B2CDBD49A0034C2A7 /* CalendarCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarCell.swift; sourceTree = ""; }; 5E847A6D2CDBD4B60034C2A7 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = ""; }; 5E847A6F2CDBD4F10034C2A7 /* CalendarMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarMapper.swift; sourceTree = ""; }; 5E847A732CDBD52E0034C2A7 /* CalendarDataInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarDataInteractor.swift; sourceTree = ""; }; 5E847A762CDBD5590034C2A7 /* AllStudyRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllStudyRecord.swift; sourceTree = ""; }; 5EAE88712CDF9B4500DCBA31 /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = ""; }; - 5ED93CD62CE2311D004A7931 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../Downloads/GoogleService-Info.plist"; sourceTree = ""; }; 5ED93CDA2CE3690D004A7931 /* Secrets-prod.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Secrets-prod.xcconfig"; sourceTree = ""; }; 98B5F0092CDB362C007CF5FA /* Secrets-dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Secrets-dev.xcconfig"; sourceTree = ""; }; 98B5F00B2CDB6226007CF5FA /* StudyTimeResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimeResponseDTO.swift; sourceTree = ""; }; @@ -442,8 +446,8 @@ 471940D42CAFEE7800426D30 /* Component */ = { isa = PBXGroup; children = ( - 47D5EDCE2CCBD10300ACA469 /* ActionButton.swift */, - 475B86E62CA1CC20000534B2 /* HY2TextField.swift */, + 47D5EDCE2CCBD10300ACA469 /* BaseButton.swift */, + 478B75452CE659C3000190EF /* BaseTextField.swift */, 4789337A2CADC43D00E1D89E /* DropDownPicker.swift */, 47F79B2F2CCCB7FC00DD0899 /* TimePicker.swift */, 473E8EBB2CCEBEF3000F102C /* ModalView.swift */, @@ -557,6 +561,16 @@ path = StudySession; sourceTree = ""; }; + 478B75402CE65464000190EF /* Component */ = { + isa = PBXGroup; + children = ( + 478B75432CE65594000190EF /* DuplicateCheckButton.swift */, + 478B754F2CE679A8000190EF /* SubmitButton.swift */, + 47FDF21E2CE712A3005CCB74 /* Form.swift */, + ); + path = Component; + sourceTree = ""; + }; 478F843A2CD326C30097CAA1 /* Weekly */ = { isa = PBXGroup; children = ( @@ -760,7 +774,7 @@ children = ( 47B1D4AB2C9CB1740071B62B /* HongikYeolgong2App.swift */, 47A147782CA158E800A91F66 /* MainTabView.swift */, - 4763FFB42CB90EBD00990336 /* InitialView.swift */, + 4763FFB42CB90EBD00990336 /* RootView.swift */, ); path = Root; sourceTree = ""; @@ -829,6 +843,7 @@ 47A5406F2CD0B8B500DC40D0 /* SignUp */ = { isa = PBXGroup; children = ( + 478B75402CE65464000190EF /* Component */, 478933782CADBDA500E1D89E /* SignUpView.swift */, ); path = SignUp; @@ -996,7 +1011,7 @@ 47F635042CC3EC1A0034EAA9 /* Department.swift */, 4780044B2CCAAD4F00FFAF00 /* WeekDay.swift */, 47A540722CD0D90F00DC40D0 /* Nickname.swift */, - 479BF66B2CDD1A8D009D9E44 /* StudyNotificationType.swift */, + 479BF66B2CDD1A8D009D9E44 /* LocalNotification.swift */, 47BB5BE22CE52858002BBEE1 /* Page.swift */, ); path = Models; @@ -1018,14 +1033,6 @@ path = Record; sourceTree = ""; }; - 98EC95372CE4B88000278DBD /* Recovered References */ = { - isa = PBXGroup; - children = ( - 5ED93CDA2CE3690D004A7931 /* Secrets-prod.xcconfig */, - ); - name = "Recovered References"; - sourceTree = ""; - }; 98F9D8422CDA2DE100DE12BB /* Component */ = { isa = PBXGroup; children = ( @@ -1258,6 +1265,7 @@ 98EC953B2CE4B97000278DBD /* StudyRecordView.swift in Sources */, 4786A1102CCA351A008635A4 /* WeeklyStudySessionDTO.swift in Sources */, 5E847A742CDBD52E0034C2A7 /* CalendarDataInteractor.swift in Sources */, + 47FDF21F2CE712A3005CCB74 /* Form.swift in Sources */, 47C8155A2CE27EB00017EA24 /* AppleLoginButton.swift in Sources */, 47C815482CE227E50017EA24 /* ASTokenRequestDTO.swift in Sources */, 470722FA2CBC0A870046469F /* Constants.swift in Sources */, @@ -1289,6 +1297,7 @@ 473671A52CB13D8100527896 /* SafeArea.swift in Sources */, 471940C82CAFCB1B00426D30 /* SizeCheckModifier.swift in Sources */, 473E8EB82CCEA5FB000F102C /* StudySessionRequestDTO.swift in Sources */, + 478B75442CE65594000190EF /* DuplicateCheckButton.swift in Sources */, 47A540732CD0D90F00DC40D0 /* Nickname.swift in Sources */, 5E847A6C2CDBD49A0034C2A7 /* CalendarCell.swift in Sources */, 47F634FE2CC3E8A40034EAA9 /* NicknameCheckDTO.swift in Sources */, @@ -1300,12 +1309,11 @@ 4786A1072CCA33DF008635A4 /* WeeklyEndpoint.swift in Sources */, 4763FFBA2CB913FF00990336 /* UserDataInteractor.swift in Sources */, 98B5F0112CDB9990007CF5FA /* StudyTimeInteractor.swift in Sources */, - 475B86E72CA1CC20000534B2 /* HY2TextField.swift in Sources */, 98D144112CDFAC4D0092708E /* NetworkingErrorView.swift in Sources */, 47F635022CC3E98D0034EAA9 /* UserEndpoint.swift in Sources */, 478933792CADBDA500E1D89E /* SignUpView.swift in Sources */, 47F71B5D2CDC77C60044DEC5 /* UserDataInteractor+Migration.swift in Sources */, - 4763FFB52CB90EBD00990336 /* InitialView.swift in Sources */, + 4763FFB52CB90EBD00990336 /* RootView.swift in Sources */, 47E250712CCF274400267897 /* WeeklyStudyInteractor.swift in Sources */, 478589FE2CE4973B0027ED32 /* MenuItem.swift in Sources */, 47C815462CE223DA0017EA24 /* ASTokenResponseDTO.swift in Sources */, @@ -1318,6 +1326,7 @@ 4774C7BB2CDF632300CDD479 /* WithdrawResponseDTO.swift in Sources */, 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */, 473E8EB42CCE6827000F102C /* TimeInterval+.swift in Sources */, + 478B75502CE679A8000190EF /* SubmitButton.swift in Sources */, 4763FFB32CB90E3800990336 /* InteractorsContainer.swift in Sources */, 470723182CBC29510046469F /* AppleLoginManager.swift in Sources */, 5E847A6E2CDBD4B60034C2A7 /* CalendarView.swift in Sources */, @@ -1336,16 +1345,17 @@ 470723012CBC16680046469F /* AuthEndpoint.swift in Sources */, 98EC953D2CE4B9CC00278DBD /* NetworkStateView.swift in Sources */, 4786A1052CCA3352008635A4 /* StudySessionInteractor.swift in Sources */, - 479BF66C2CDD1A8D009D9E44 /* StudyNotificationType.swift in Sources */, + 479BF66C2CDD1A8D009D9E44 /* LocalNotification.swift in Sources */, 473E8EB22CCE5267000F102C /* SystemOverlay.swift in Sources */, 475B86DF2CA1AF1E000534B2 /* RecordView.swift in Sources */, 98F9D8462CDA2E6D00DE12BB /* RecordCell.swift in Sources */, 47C815552CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift in Sources */, 47A147792CA158E800A91F66 /* MainTabView.swift in Sources */, + 478B75462CE659C3000190EF /* BaseTextField.swift in Sources */, 47F79B302CCCB7FC00DD0899 /* TimePicker.swift in Sources */, 478FBCA32CD13A1E00256012 /* UserInfo.swift in Sources */, 98B5F0132CDBB905007CF5FA /* StudyTimeMapper.swift in Sources */, - 47D5EDCF2CCBD10300ACA469 /* ActionButton.swift in Sources */, + 47D5EDCF2CCBD10300ACA469 /* BaseButton.swift in Sources */, 478F844B2CD35AF40097CAA1 /* RankingMapper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1513,7 +1523,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"HongikYeolgong2/Resources/Preview Content\""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = P4D4ZQC4YF; @@ -1560,7 +1570,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 7; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"HongikYeolgong2/Resources/Preview Content\""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = P4D4ZQC4YF; diff --git a/HongikYeolgong2/Data/Repositories/Auth/AuthRepositoryImpl.swift b/HongikYeolgong2/Data/Repositories/Auth/AuthRepositoryImpl.swift index ee138f9..52ccd33 100644 --- a/HongikYeolgong2/Data/Repositories/Auth/AuthRepositoryImpl.swift +++ b/HongikYeolgong2/Data/Repositories/Auth/AuthRepositoryImpl.swift @@ -19,7 +19,7 @@ final class AuthRepositoryImpl: AuthRepository { do { let response: BaseResponse = try await NetworkService.shared.request(endpoint: AuthEndpoint.login(loginReqDto: loginReqDto)) promise(.success(response.data)) - } catch let error as NetworkError { + } catch let error as NetworkError { promise(.failure(error)) } } @@ -33,7 +33,7 @@ final class AuthRepositoryImpl: AuthRepository { return Future { promise in Task { do { - let response: BaseResponse = try await NetworkService.shared.request(endpoint: UserEndpoint.checkUserNickname(nickname: nickname)) + let response: BaseResponse = try await NetworkService.shared.request(endpoint: UserEndpoint.checkUserNickname(nickname: nickname)) promise(.success(response.data.duplicate)) } catch let error as NetworkError { promise(.failure(error)) diff --git a/HongikYeolgong2/Domain/Interactors/StudySessionInteractor.swift b/HongikYeolgong2/Domain/Interactors/StudySessionInteractor.swift index 1e1ab40..b5241d1 100644 --- a/HongikYeolgong2/Domain/Interactors/StudySessionInteractor.swift +++ b/HongikYeolgong2/Domain/Interactors/StudySessionInteractor.swift @@ -139,7 +139,7 @@ final class StudySessionInteractorImpl: StudySessionInteractor { } /// 열람실 이용종료 Notification을 등록합니다. - func registerNotification(for type: StudyNotificationType, endTimeInMinute: TimeInterval) { + func registerNotification(for type: LocalNotification, endTimeInMinute: TimeInterval) { guard appState.value.userData.isOnAlarm else { return } let content = configuredNotificationContent(for: type) let trigger = configuredNotificationTrigger(for: type, endTime: endTimeInMinute) @@ -153,7 +153,7 @@ final class StudySessionInteractorImpl: StudySessionInteractor { } /// Notification Content 설정 - func configuredNotificationContent(for type: StudyNotificationType) -> UNMutableNotificationContent { + func configuredNotificationContent(for type: LocalNotification) -> UNMutableNotificationContent { let content = UNMutableNotificationContent() content.body = type.message content.sound = .default @@ -161,7 +161,7 @@ final class StudySessionInteractorImpl: StudySessionInteractor { } /// Notification Trigger 설정 - func configuredNotificationTrigger(for type: StudyNotificationType, endTime: TimeInterval) -> UNTimeIntervalNotificationTrigger? { + func configuredNotificationTrigger(for type: LocalNotification, endTime: TimeInterval) -> UNTimeIntervalNotificationTrigger? { let triggerTime = endTime - type.timeOffset assert(endTime - triggerTime == type.timeOffset, "잘못된 시간이 등록되었습니다.") diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 6a4c12d..6992246 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -78,8 +78,8 @@ final class UserDataMigrationInteractor: UserDataInteractor { /// - Parameter authorization: ASAuthorization func requestAppleLogin(_ authorization: ASAuthorization) { guard let appleIDCredential = appleLoginService.requestAppleLogin(authorization), - let idTokenData = appleIDCredential.identityToken, - let idToken = String(data: idTokenData, encoding: .utf8) else { + let idTokenData = appleIDCredential.identityToken, + let idToken = String(data: idTokenData, encoding: .utf8) else { return } @@ -89,15 +89,14 @@ final class UserDataMigrationInteractor: UserDataInteractor { return Fail(error: NetworkError.decodingError("")).eraseToAnyPublisher() } let loginReqDto: LoginRequestDTO = .init(email: userID, idToken: idToken) - + return authRepository.signIn(loginReqDto: loginReqDto) } .receive(on: DispatchQueue.main) .sink( receiveCompletion: { _ in}, receiveValue: { [weak self] loginResDto in - - guard let self = self else { return } + guard let self = self else { return } let isAlreadyExists = loginResDto.alreadyExist @@ -127,7 +126,7 @@ final class UserDataMigrationInteractor: UserDataInteractor { guard let self = self else { return } appState[\.userSession] = .authenticated appState[\.routing.onboarding.signUp] = false - KeyChainManager.addItem(key: .accessToken, value: signUpResDto.accessToken) + KeyChainManager.addItem(key: .accessToken, value: signUpResDto.accessToken) } ) .store(in: cancleBag) @@ -143,12 +142,12 @@ final class UserDataMigrationInteractor: UserDataInteractor { /// - Parameters: /// - nickname: 닉네임 /// - isValidate: 중복여부 - func checkUserNickname(nickname: String, nicknameCheckSubject: CurrentValueSubject) { + func checkUserNickname(inputNickname: String, nickname: Binding) { authRepository - .checkUserNickname(nickname: nickname) + .checkUserNickname(nickname: inputNickname) .replaceError(with: true) .receive(on: DispatchQueue.main) - .sink { nicknameCheckSubject.send($0) } + .sink { $0 ? (nickname.wrappedValue = .alreadyUse) : (nickname.wrappedValue = .available) } .store(in: cancleBag) } @@ -191,6 +190,40 @@ final class UserDataMigrationInteractor: UserDataInteractor { .store(in: cancleBag) } + func validateUserNickname(inputNickname: String, nickname: Binding) { + if inputNickname.isEmpty { + nickname.wrappedValue = .none + } else if inputNickname.count < 2 || inputNickname.count > 8 { + nickname.wrappedValue = .notAllowedLength + } else if inputNickname.contains(" ") || checkSpecialCharacter(inputNickname) { + nickname.wrappedValue = .specialCharactersAndSpaces + } else if checkKoreanLang(inputNickname) { + nickname.wrappedValue = .checkAvailable + } else { + nickname.wrappedValue = .unknown + } + } + + func checkSpecialCharacter(_ input: String) -> Bool { + let pattern: String = "[!\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~€£¥₩¢₹©®™§¶°•※≡∞≠≈‽✓✔✕✖←→↑↓↔↕↩↪↖↗↘↙ñ¡¿éèêëçäöüßàìòùåøæ]" + + if let _ = input.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + + func checkKoreanLang(_ input: String) -> Bool { + let pattern = "^[가-힣a-zA-Z0-9\\s]*$" + + if let _ = input.range(of: pattern, options: .regularExpression) { + return true + } else { + return false + } + } + /// 회원 탈퇴 func withdraw() { let clientSecret = makeJWT() diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift index 72c2a82..d79ec13 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift @@ -15,12 +15,17 @@ protocol UserDataInteractor: AnyObject { func signUp(nickname: String, department: Department) func logout() func checkAuthentication() - func checkUserNickname(nickname: String, nicknameCheckSubject: CurrentValueSubject) + func checkUserNickname(inputNickname: String, nickname: Binding) + func validateUserNickname(inputNickname: String, nickname: Binding) func getUserProfile() func withdraw() } final class UserDataInteractorImpl: UserDataInteractor { + func validateUserNickname(inputNickname: String, nickname: Binding) { + + } + private let cancleBag = CancelBag() private let appState: Store @@ -98,12 +103,14 @@ final class UserDataInteractorImpl: UserDataInteractor { /// - Parameters: /// - nickname: 닉네임 /// - isValidate: 중복여부 - func checkUserNickname(nickname: String, nicknameCheckSubject: CurrentValueSubject) { + func checkUserNickname(inputNickname: String, nickname: Binding) { authRepository - .checkUserNickname(nickname: nickname) + .checkUserNickname(nickname: inputNickname) .replaceError(with: true) .receive(on: DispatchQueue.main) - .sink { nicknameCheckSubject.send($0) } + .filter { !$0 } + .map { _ in } + .sink { nickname.wrappedValue = .available} .store(in: cancleBag) } diff --git a/HongikYeolgong2/Models/Department.swift b/HongikYeolgong2/Models/Department.swift index f526e50..16bfa33 100644 --- a/HongikYeolgong2/Models/Department.swift +++ b/HongikYeolgong2/Models/Department.swift @@ -46,7 +46,7 @@ enum Department: String, CaseIterable { case frenchStudies = "프랑스어문학과" case painting = "회화과" - static func allDepartments() -> [String] { + static var allDepartments: [String] { Self.allCases.filter { $0 != .none }.map { $0.rawValue } } } diff --git a/HongikYeolgong2/Models/StudyNotificationType.swift b/HongikYeolgong2/Models/LocalNotification.swift similarity index 96% rename from HongikYeolgong2/Models/StudyNotificationType.swift rename to HongikYeolgong2/Models/LocalNotification.swift index 63f190e..8ffc211 100644 --- a/HongikYeolgong2/Models/StudyNotificationType.swift +++ b/HongikYeolgong2/Models/LocalNotification.swift @@ -7,7 +7,7 @@ import Foundation -enum StudyNotificationType { +enum LocalNotification { case extensionAvailable case urgent diff --git a/HongikYeolgong2/Models/Nickname.swift b/HongikYeolgong2/Models/Nickname.swift index 1fc3d96..4635dce 100644 --- a/HongikYeolgong2/Models/Nickname.swift +++ b/HongikYeolgong2/Models/Nickname.swift @@ -45,48 +45,7 @@ enum Nickname { } } - static func validate(_ nickname: String) -> Nickname { - var status: Nickname = .none - status.validateUserNickname(nickname: nickname) - return status - } - - private enum Constant { - static let minLength = 2 - static let maxLength = 8 - } - - mutating func validateUserNickname(nickname: String) { - if nickname.isEmpty { - self = .none - } else if nickname.count < Constant.minLength || nickname.count > Constant.maxLength { - self = .notAllowedLength - } else if nickname.contains(" ") || checkSpecialCharacter(nickname) { - self = .specialCharactersAndSpaces - } else if checkKoreanLang(nickname) { - self = .checkAvailable - } else { - self = .unknown - } - } - - func checkSpecialCharacter(_ input: String) -> Bool { - let pattern: String = "[!\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~€£¥₩¢₹©®™§¶°•※≡∞≠≈‽✓✔✕✖←→↑↓↔↕↩↪↖↗↘↙ñ¡¿éèêëçäöüßàìòùåøæ]" - - if let _ = input.range(of: pattern, options: .regularExpression) { - return true - } else { - return false - } - } - - func checkKoreanLang(_ input: String) -> Bool { - let pattern = "^[가-힣a-zA-Z\\s]*$" - - if let _ = input.range(of: pattern, options: .regularExpression) { - return true - } else { - return false - } + var isCheckable: Bool { + self == .checkAvailable } } diff --git a/HongikYeolgong2/Presentation/Auth/SignUp/Component/DuplicateCheckButton.swift b/HongikYeolgong2/Presentation/Auth/SignUp/Component/DuplicateCheckButton.swift new file mode 100644 index 0000000..eea1db0 --- /dev/null +++ b/HongikYeolgong2/Presentation/Auth/SignUp/Component/DuplicateCheckButton.swift @@ -0,0 +1,28 @@ +// +// DuplicateCheckButton.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/15/24. +// + +import SwiftUI + +struct DuplicateCheckButton: View { + let action: () -> Void + let disabled: Bool + + var body: some View { + Button(action: action, label: { + Text("중복확인") + .foregroundStyle(disabled ? Color.gray200 : Color.white) + .frame(maxWidth: 88.adjustToScreenWidth, maxHeight: 48.adjustToScreenHeight) + }) + .background(disabled ? Color.blue400 : Color.blue100) + .disabled(disabled) + .cornerRadius(8) + } +} + +#Preview { + DuplicateCheckButton(action: {}, disabled: false) +} diff --git a/HongikYeolgong2/Presentation/Auth/SignUp/Component/Form.swift b/HongikYeolgong2/Presentation/Auth/SignUp/Component/Form.swift new file mode 100644 index 0000000..180cc17 --- /dev/null +++ b/HongikYeolgong2/Presentation/Auth/SignUp/Component/Form.swift @@ -0,0 +1,46 @@ +// +// Form.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/15/24. +// + +import SwiftUI + +struct FormTitle: View { + let title: String + + var body: some View { + Text(title) + .font(.suite(size: 18, weight: .bold)) + .foregroundStyle(.gray100) + .frame( + maxWidth: .infinity, + maxHeight: 52.adjustToScreenHeight, + alignment: .leading + ) + .background(Color.black) + } +} + +struct FormLabel: View { + let title: String + + var body: some View { + Text(title) + .font(.pretendard(size: 16, weight: .bold), lineHeight: 26) + .foregroundStyle(.gray200) + } +} + +struct FormDescription: View { + let message: String + let color: Color + + var body: some View { + Text(message) + .font(.pretendard(size: 12, weight: .regular)) + .foregroundStyle(color) + .padding(.top, 4.adjustToScreenHeight) + } +} diff --git a/HongikYeolgong2/Presentation/Auth/SignUp/Component/SubmitButton.swift b/HongikYeolgong2/Presentation/Auth/SignUp/Component/SubmitButton.swift new file mode 100644 index 0000000..c3e9345 --- /dev/null +++ b/HongikYeolgong2/Presentation/Auth/SignUp/Component/SubmitButton.swift @@ -0,0 +1,23 @@ +// +// SubmitButton.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/15/24. +// + +import SwiftUI + +struct SubmitButton: View { + let action: () -> () + let disabled: Bool + + var body: some View { + Button(action: action) { + Image(.submitButtonEnable) + .resizable() + .frame(height: 50.adjustToScreenHeight) + } + .disabled(disabled) + .overlay(disabled ? Color.dark.opacity(0.5) : nil) + } +} diff --git a/HongikYeolgong2/Presentation/Auth/SignUp/SignUpView.swift b/HongikYeolgong2/Presentation/Auth/SignUp/SignUpView.swift index 3aa9c60..5550751 100644 --- a/HongikYeolgong2/Presentation/Auth/SignUp/SignUpView.swift +++ b/HongikYeolgong2/Presentation/Auth/SignUp/SignUpView.swift @@ -6,198 +6,90 @@ // import SwiftUI -import Combine -// MARK: - SignUpView struct SignUpView: View { - // MARK: - Environment - @Environment(\.injected.appState) var appState @Environment(\.injected.interactors.userDataInteractor) var userDataInteractor - @Environment(\.presentationMode) var presentationMode - // MARK: - State - @State private var userInfo = UserInfo() - @State private var isSubmitButtonEnable = false - @State private var isCheckButtonEnable = false - @State private var keyboardOffset: CGFloat = 23 - let nicknameCheckSubject = CurrentValueSubject(false) + @State private var nickname: Nickname = .none + @State private var department: Department = .none + @State private var inputNickname = "" + @State private var inputDepartment = "" + + @FocusState private var focused - // MARK: - Body var body: some View { - ZStack { - Color.dark.edgesIgnoringSafeArea(.all) - - VStack { - NavigationHeaderView(title: "회원가입") + VStack(spacing: 0) { + VStack(spacing: 0) { + Spacer() + .frame(maxHeight: 52 + 23.adjustToScreenHeight) - VStack(spacing: 12.adjustToScreenWidth) { - VStack(alignment: .leading, spacing: 8.adjustToScreenHeight) { - TitleView(title: "닉네임") - - HStack { - HY2TextField( - text: $userInfo.inputNickname, - placeholder: "닉네임을 입력해주세요", - isError: userInfo.nickname.isError - ) - - DuplicateCheckButton( - nicknameStatus: userInfo.nickname, - action: checkUserNickname - ) - } - - DescriptionView( - message: userInfo.nickname.message, - color: userInfo.nickname.textColor - ) - } + VStack(alignment: .leading, spacing: 0) { + FormLabel(title: "닉네임") + + Spacer() + .frame(height: 8.adjustToScreenHeight) - VStack(alignment: .leading, spacing: 8.adjustToScreenHeight) { - TitleView(title: "학과") + HStack(spacing: 10.adjustToScreenHeight) { + BaseTextField( + text: $inputNickname, + placeholder: "닉네임을 입력해주세요.", + isError: nickname.isError + ) - DropDownPicker( - text: $userInfo.inputDepartment, - seletedItem: Binding( - get: { userInfo.department.rawValue }, - set: { userInfo.department = .init(rawValue: $0) ?? .none } - ), - placeholder: "학과를 입력해주세요", - items: Department.allDepartments() + DuplicateCheckButton( + action: { userDataInteractor.checkUserNickname(inputNickname: inputNickname, nickname: $nickname) }, + disabled: !nickname.isCheckable ) } + + Spacer() + .frame(height: 4.adjustToScreenHeight) + + FormDescription( + message: nickname.message, + color: nickname.textColor + ) } - .offset(y: keyboardOffset) - .animation(.easeIn(duration: 0.2), value: keyboardOffset) + .layoutPriority(1) Spacer() + .frame(height: 12.adjustToScreenHeight) - SubmitButton( - isSubmitButtonEnable: isSubmitButtonEnable, - submitButtonTapped: performSignUp - ) - } - .onTapGesture { - UIApplication.shared.hideKeyboard() + VStack(alignment: .leading, spacing: 8.adjustToScreenHeight) { + FormLabel(title: "학과") + + DropDownPicker( + text: $inputDepartment, + seletedItem: Binding( + get: { department.rawValue }, + set: { department = .init(rawValue: $0) ?? .none } + ), + placeholder: "", + items: Department.allDepartments + ) + } + .layoutPriority(2) } - .padding(.horizontal, 32.adjustToScreenWidth) + + Spacer() + SubmitButton( + action: { userDataInteractor.signUp(nickname: inputNickname, department: department) }, + disabled: !(nickname == .available && + (Department.allDepartments.contains(department.rawValue) || + Department.allDepartments.contains(inputDepartment))) + ) + .padding(.bottom, 20.adjustToScreenHeight) } + .overlay(alignment: .topLeading, content: { + FormTitle(title: "회원가입") + }) .toolbar(.hidden, for: .navigationBar) - .onChange(of: userInfo.inputNickname) { inputNickname in - userInfo.nickname.validateUserNickname(nickname: inputNickname) - } - .onChange(of: userInfo) { userInfo in - isSubmitButtonEnable = userInfo.nickname == .available && - userInfo.department != .none - } - .onReceive(nicknameCheckSubject.dropFirst()) { isAlreadyInUse in - userInfo.nickname = isAlreadyInUse ? .alreadyUse : .available - } - .onReceive(keyboardVisibilityUpdated) { isKeyboardVisibility in - if isKeyboardVisibility { - keyboardOffset = 0 - } else { - keyboardOffset = 23 - } - } - } -} - -// MARK: - Components -private extension SignUpView { - // MARK: - Header - struct NavigationHeaderView: View { - let title: String - - var body: some View { - Text(title) - .font(.suite(size: 18, weight: .bold)) - .foregroundStyle(.gray100) - .frame( - maxWidth: .infinity, - minHeight: 52.adjustToScreenHeight, - alignment: .leading - ) - } - } - - // MARK: - Title - struct TitleView: View { - let title: String - - var body: some View { - Text(title) - .font(.pretendard(size: 16, weight: .bold), lineHeight: 26) - .foregroundStyle(.gray200) - } - } - - // MARK: - Description - struct DescriptionView: View { - let message: String - let color: Color - - var body: some View { - Text(message) - .font(.pretendard(size: 12, weight: .regular)) - .foregroundStyle(color) - .padding(.top, 4.adjustToScreenHeight) + .padding(.horizontal, 32.adjustToScreenWidth) + .onTapGesture { + UIApplication.shared.hideKeyboard() } - } - - // MARK: - Buttons - struct SubmitButton: View { - let isSubmitButtonEnable: Bool - let submitButtonTapped: () -> Void - - var body: some View { - Button(action: submitButtonTapped) { - Image(isSubmitButtonEnable ? .submitButtonEnable : .submitButtonDisable) - .resizable() - .frame(height: 50.adjustToScreenHeight) - } - .padding(.bottom, 20.adjustToScreenHeight) - .disabled(!isSubmitButtonEnable) + .onChange(of: inputNickname) { + userDataInteractor.validateUserNickname(inputNickname: $0, nickname: $nickname) } } - - struct DuplicateCheckButton: View { - let nicknameStatus: Nickname - let action: () -> Void - - var body: some View { - ActionButton( - title: "중복확인", - font: .pretendard(size: 16, weight: .regular), - height: 48.adjustToScreenHeight, - width: 88.adjustToScreenWidth, - backgroundColor: nicknameStatus == .available ? .blue400 : .blue100, - foregroundColor: .white, - action: action - ) - .cornerRadius(8) - .disabled(!(nicknameStatus == .checkAvailable)) - } - } -} - -// MARK: - Interactor -private extension SignUpView { - func checkUserNickname() { - userDataInteractor.checkUserNickname( - nickname: userInfo.inputNickname, - nicknameCheckSubject: nicknameCheckSubject - ) - } - - func performSignUp() { - userDataInteractor.signUp( - nickname: userInfo.inputNickname, - department: userInfo.department - ) - } - - var keyboardVisibilityUpdated: AnyPublisher { - appState.updates(for: \.system.isKeyboardActive) - } } diff --git a/HongikYeolgong2/Presentation/Home/HomeView.swift b/HongikYeolgong2/Presentation/Home/HomeView.swift index 9da7e56..c62bdea 100644 --- a/HongikYeolgong2/Presentation/Home/HomeView.swift +++ b/HongikYeolgong2/Presentation/Home/HomeView.swift @@ -173,14 +173,14 @@ struct ActionButtonControllerView: View { if studySession.isStudying { VStack(spacing: 12.adjustToScreenWidth) { if studySession.isAddTime { - ActionButton( + BaseButton( title: "열람실 이용 연장", backgroundColor: .blue100, radius: 4, action: { actions.addButtonTapped() } ) } - ActionButton( + BaseButton( title: "열람실 이용 종료", backgroundColor: .gray600, radius: 4, @@ -189,14 +189,14 @@ struct ActionButtonControllerView: View { } } else { HStack(spacing: 12.adjustToScreenWidth) { - ActionButton( + BaseButton( width: 69.adjustToScreenWidth, backgroundColor: .clear, action: { actions.seatButtonTapped() } ) .modifier(ImageBackground(imageName: .seatButton)) - ActionButton( + BaseButton( backgroundColor: .clear, action: { actions.startButtonTapped() } ) diff --git a/HongikYeolgong2/Presentation/Other/Launch Screen.storyboard b/HongikYeolgong2/Presentation/Other/Launch Screen.storyboard index edd6a70..fa193d6 100644 --- a/HongikYeolgong2/Presentation/Other/Launch Screen.storyboard +++ b/HongikYeolgong2/Presentation/Other/Launch Screen.storyboard @@ -50,7 +50,7 @@ - + @@ -62,7 +62,7 @@ - + diff --git a/HongikYeolgong2/Presentation/Other/SplashView.swift b/HongikYeolgong2/Presentation/Other/SplashView.swift index 75e1f65..621afcd 100644 --- a/HongikYeolgong2/Presentation/Other/SplashView.swift +++ b/HongikYeolgong2/Presentation/Other/SplashView.swift @@ -11,8 +11,9 @@ struct SplashView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { let storyboard = UIStoryboard(name: "Launch Screen", bundle: nil) let viewController = storyboard.instantiateInitialViewController()! + return viewController } - + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } diff --git a/HongikYeolgong2/Presentation/Root/HongikYeolgong2App.swift b/HongikYeolgong2/Presentation/Root/HongikYeolgong2App.swift index ceeb5f1..423eeef 100644 --- a/HongikYeolgong2/Presentation/Root/HongikYeolgong2App.swift +++ b/HongikYeolgong2/Presentation/Root/HongikYeolgong2App.swift @@ -15,7 +15,7 @@ struct HongikYeolgong2App: App { WindowGroup { let container: DIContainer = enviroment.container - InitialView() + RootView() .inject(container) } } diff --git a/HongikYeolgong2/Presentation/Root/InitialView.swift b/HongikYeolgong2/Presentation/Root/RootView.swift similarity index 91% rename from HongikYeolgong2/Presentation/Root/InitialView.swift rename to HongikYeolgong2/Presentation/Root/RootView.swift index fc16452..d003697 100644 --- a/HongikYeolgong2/Presentation/Root/InitialView.swift +++ b/HongikYeolgong2/Presentation/Root/RootView.swift @@ -1,5 +1,5 @@ // -// InitialView.swift +// RootView.swift // HongikYeolgong2 // // Created by 권석기 on 10/11/24. @@ -8,7 +8,7 @@ import SwiftUI import Combine -struct InitialView: View { +struct RootView: View { @Environment(\.injected.appState) var appState @Environment(\.injected.interactors.userDataInteractor) var userDataInteractor @Environment(\.injected.interactors.userPermissionsInteractor) var userPermissionsInteractor @@ -33,7 +33,7 @@ struct InitialView: View { } } } - .onAppear { + .onAppear { resolveUserPermissions() } .onReceive(canRequestFirstPushPermissions) { _ in @@ -45,7 +45,7 @@ struct InitialView: View { } } -private extension InitialView { +private extension RootView { var userSessionUpdated: AnyPublisher { appState.updates(for: \.userSession) } @@ -58,7 +58,7 @@ private extension InitialView { } } -private extension InitialView { +private extension RootView { func checkUserSession() { userDataInteractor.checkAuthentication() } @@ -73,5 +73,5 @@ private extension InitialView { } #Preview { - InitialView() + RootView() } diff --git a/HongikYeolgong2/UI/Component/ActionButton.swift b/HongikYeolgong2/UI/Component/BaseButton.swift similarity index 97% rename from HongikYeolgong2/UI/Component/ActionButton.swift rename to HongikYeolgong2/UI/Component/BaseButton.swift index d589144..74b6f2d 100644 --- a/HongikYeolgong2/UI/Component/ActionButton.swift +++ b/HongikYeolgong2/UI/Component/BaseButton.swift @@ -7,7 +7,7 @@ import SwiftUI -struct ActionButton: View { +struct BaseButton: View { // MARK: - Properties let title: String let font: UIFont diff --git a/HongikYeolgong2/UI/Component/HY2TextField.swift b/HongikYeolgong2/UI/Component/BaseTextField.swift similarity index 93% rename from HongikYeolgong2/UI/Component/HY2TextField.swift rename to HongikYeolgong2/UI/Component/BaseTextField.swift index 23b9338..43b4cae 100644 --- a/HongikYeolgong2/UI/Component/HY2TextField.swift +++ b/HongikYeolgong2/UI/Component/BaseTextField.swift @@ -1,13 +1,13 @@ // -// HY2TextField.swift +// BaseTextField.swift // HongikYeolgong2 // -// Created by 권석기 on 9/24/24. +// Created by 권석기 on 11/15/24. // import SwiftUI -struct HY2TextField: View { +struct BaseTextField: View { @Binding var text: String @FocusState var isFocused: Bool @@ -48,3 +48,5 @@ struct HY2TextField: View { ) } } + + diff --git a/HongikYeolgong2/UI/Component/DropDownPicker.swift b/HongikYeolgong2/UI/Component/DropDownPicker.swift index 5161a3e..cb99e9b 100644 --- a/HongikYeolgong2/UI/Component/DropDownPicker.swift +++ b/HongikYeolgong2/UI/Component/DropDownPicker.swift @@ -58,6 +58,7 @@ struct DropDownPicker: View { .padding(.trailing, 14) .onTapGesture { text = "" + seletedItem = "" } } } @@ -69,27 +70,34 @@ struct DropDownPicker: View { ) // scrollView - if (isFocused && isEmpty) || (isFocused && text.isEmpty) { - ScrollView { - LazyVStack(alignment: .leading, spacing: 0) { - ForEach(isFocused && text.isEmpty ? items : filterdItem, id: \.self) { item in - Button(action: { - seletedItem = item - text = item - isFocused = false - }, label: { - Text("\(item)") - .font(.pretendard(size: 16, weight: .regular)) - .foregroundStyle(.gray200) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(12)}) - .checkSize(in: $contentSize) + if isFocused { + VStack(spacing: 0) { + if (isFocused && isEmpty) || (isFocused && text.isEmpty) { + ScrollView { + LazyVStack(alignment: .leading, spacing: 0) { + ForEach(isFocused && text.isEmpty ? items : filterdItem, id: \.self) { item in + Button(action: { + seletedItem = item + text = item + isFocused = false + }, label: { + Text("\(item)") + .font(.pretendard(size: 16, weight: .regular)) + .foregroundStyle(.gray200) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(12)}) + .checkSize(in: $contentSize) + } + } } + .frame(maxHeight: !filterdItem.isEmpty ? min(contentHeight, maxHeight) : maxHeight) + .background(.gray800) + .cornerRadius(8) + .layoutPriority(3) } + } - .frame(maxHeight: !filterdItem.isEmpty ? min(contentHeight, maxHeight) : maxHeight) - .background(.gray800) - .cornerRadius(8) + .frame(maxHeight: maxHeight, alignment: .top) } } } diff --git a/HongikYeolgong2/Util/API/Base/NetworkService.swift b/HongikYeolgong2/Util/API/Base/NetworkService.swift index 15e7f3a..c9c3028 100644 --- a/HongikYeolgong2/Util/API/Base/NetworkService.swift +++ b/HongikYeolgong2/Util/API/Base/NetworkService.swift @@ -21,9 +21,11 @@ struct NetworkService: NetworkProtocol { let request = configRequest(url: url, endpoint: endpoint) let (data, response) = try await session.data(for: request) - + if let responseString = String(data: data, encoding: .utf8) { + print("📍 Response body: \(responseString)") + } return try processResponse(data: data, response: response) - } + } func plainRequest(endpoint: EndpointProtocol) async throws { guard let url = configUrl(endpoint: endpoint) else { @@ -83,7 +85,7 @@ extension NetworkService { guard (200...299).contains(httpResponse.statusCode) else { throw NetworkError.serverError(statusCode: httpResponse.statusCode) } - + do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase