├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── 🐞-bug-template.md │ └── 💡-issue-template.md ├── pull_request_template.md ├── secrets │ ├── Secrets.xcconfig.gpg │ ├── layoverProfile │ └── mobileprovision └── workflows │ └── ios-pr-test.yml ├── .gitignore ├── .idea ├── .gitignore ├── iOS09-Layover.iml ├── inspectionProfiles │ └── Project_Default.xml ├── jsLinters │ └── eslint.xml ├── modules.xml ├── prettier.xml └── vcs.xml ├── BE ├── .gitkeep └── layover │ ├── .eslintrc.js │ ├── .prettierrc │ ├── Dockerfile │ ├── Jenkinsfile_CD │ ├── Jenkinsfile_CI │ ├── README.md │ ├── docker-compose.yml │ ├── dockerignore │ ├── nest-cli.json │ ├── package-lock.json │ ├── package.json │ ├── prometheus.yml │ ├── public │ ├── apps │ │ ├── 0.2.0 │ │ │ ├── Layover.ipa │ │ │ └── manifest.plist │ │ ├── 0.3.0 │ │ │ ├── Layover.ipa │ │ │ └── manifest.plist │ │ ├── 0.4.0 │ │ │ ├── Layover.ipa │ │ │ └── manifest.plist │ │ ├── Layover.ipa │ │ ├── images │ │ │ ├── layover_1.png │ │ │ └── layover_2.png │ │ └── manifest.plist │ ├── iOS.html │ └── images │ │ └── kong.jpeg │ ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── board │ │ ├── board.controller.spec.ts │ │ ├── board.controller.ts │ │ ├── board.entity.ts │ │ ├── board.fixture.ts │ │ ├── board.module.ts │ │ ├── board.provider.ts │ │ ├── board.repository.ts │ │ ├── board.service.ts │ │ ├── board.swagger.ts │ │ └── dtos │ │ │ ├── board-pre-signed-url.dto.ts │ │ │ ├── board-res-dto.ts │ │ │ ├── boards-res.dto.ts │ │ │ ├── create-board-res.dto.ts │ │ │ ├── create-board.dto.ts │ │ │ ├── encoding-callback.dto.ts │ │ │ └── upload-callback.dto.ts │ ├── config.ts │ ├── database │ │ ├── database.module.ts │ │ └── database.provider.ts │ ├── main.ts │ ├── member │ │ ├── dtos │ │ │ ├── check-username-res.dto.ts │ │ │ ├── check-username.dto.ts │ │ │ ├── delete-member-res.dto.ts │ │ │ ├── introduce-res.dto.ts │ │ │ ├── introduce.dto.ts │ │ │ ├── member-infos-res.dto.ts │ │ │ ├── profile-pre-signed-url.dto.ts │ │ │ ├── username-res.dto.ts │ │ │ └── username.dto.ts │ │ ├── member.controller.ts │ │ ├── member.entity.ts │ │ ├── member.module.ts │ │ ├── member.providers.ts │ │ ├── member.repository.ts │ │ ├── member.service.ts │ │ └── member.swagger.ts │ ├── oauth │ │ ├── dtos │ │ │ ├── apple-login.dto.ts │ │ │ ├── apple-signup.dto.ts │ │ │ ├── check-signup-res-dto.ts │ │ │ ├── kakao-login.dto.ts │ │ │ ├── kakao-signup.dto.ts │ │ │ └── token-res.dto.ts │ │ ├── oauth.controller.spec.ts │ │ ├── oauth.controller.ts │ │ ├── oauth.module.ts │ │ ├── oauth.repository.ts │ │ ├── oauth.service.spec.ts │ │ ├── oauth.service.ts │ │ └── oauth.swagger.ts │ ├── pipes │ │ ├── custom-header.decorator.ts │ │ └── jwt.validation.pipe.ts │ ├── redis │ │ ├── redis.module.ts │ │ └── redis.provider.ts │ ├── report │ │ ├── dtos │ │ │ ├── report-res.dto.ts │ │ │ └── report.dto.ts │ │ ├── report.controller.ts │ │ ├── report.entity.ts │ │ ├── report.module.ts │ │ ├── report.provider.ts │ │ ├── report.service.ts │ │ └── report.swagger.ts │ ├── response │ │ ├── custom-response.ts │ │ └── ecustom-code.jenum.ts │ ├── tag │ │ ├── tag.entity.ts │ │ ├── tag.module.ts │ │ ├── tag.provider.ts │ │ ├── tag.repository.ts │ │ └── tag.service.ts │ └── utils │ │ ├── hashUtils.ts │ │ ├── interfaces │ │ └── token.payload.d.ts │ │ ├── jwtUtils.ts │ │ ├── logging-interceptor.ts │ │ ├── my-logger.ts │ │ ├── pre-signed-url-res.dto.ts │ │ ├── s3Utils.ts │ │ └── swaggerUtils.ts │ ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json │ ├── tsconfig.build.json │ └── tsconfig.json ├── README.md └── iOS └── Layover ├── .swiftlint.yml ├── Layover.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── Layover.xcscheme ├── Layover ├── AppDelegate.swift ├── Base.lproj │ └── LaunchScreen.storyboard ├── DesignSystem │ ├── LOButton.swift │ ├── LOCircleButton.swift │ ├── LODescriptionView.swift │ ├── LOImageLabel.swift │ ├── LOPopUpView.swift │ ├── LOReportContentView.swift │ ├── LOReportStackView.swift │ ├── LOSlider.swift │ ├── LOTagStackView.swift │ ├── LOTextField.swift │ ├── LOTextLabel.swift │ └── LOTextView.swift ├── Extensions │ ├── AVFileType+.swift │ ├── Notification.Name+.swift │ ├── OSLog+.swift │ ├── Sequence+.swift │ ├── UICollectionViewLayout+.swift │ ├── UIFont+.swift │ ├── UIView+.swift │ ├── UIViewController+.swift │ ├── URL+.swift │ └── URLSession+.swift ├── Info.plist ├── LOImageCacher │ ├── .gitignore │ ├── .swiftpm │ │ └── xcode │ │ │ └── package.xcworkspace │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Package.swift │ ├── Sources │ │ └── LOImageCacher │ │ │ ├── ImageView + LOImageCacherWrapper.swift │ │ │ ├── LOCacheStorage.swift │ │ │ ├── LOImageCacher.swift │ │ │ └── LOImageFetcher.swift │ └── Tests │ │ └── LOImageCacherTests │ │ └── LOImageCacherTests.swift ├── Layover.entitlements ├── Models │ ├── Board.swift │ ├── Member.swift │ ├── Post.swift │ ├── PostsPage.swift │ └── UploadPost.swift ├── Network │ ├── AuthManager.swift │ ├── Common │ │ └── NetworkError.swift │ ├── DTOs │ │ ├── BoardDTO.swift │ │ ├── CheckSignUpDTO.swift │ │ ├── CheckUserNameDTO.swift │ │ ├── IntroduceDTO.swift │ │ ├── LoginDTO.swift │ │ ├── MemberDTO.swift │ │ ├── NicknameDTO.swift │ │ ├── PostDTO.swift │ │ ├── PostsPageDTO.swift │ │ ├── PresignedURLDTO.swift │ │ ├── ProfileImageDTO.swift │ │ ├── ReportDTO.swift │ │ ├── Response.swift │ │ ├── UploadPostDTO.swift │ │ └── UploadVideoDTO.swift │ ├── EndPoint │ │ ├── EndPoint.swift │ │ ├── Factories │ │ │ ├── DefaultPostManagerEndPointFactory.swift │ │ │ ├── LoginEndPointFactory.swift │ │ │ ├── PostEndPointFactory.swift │ │ │ ├── SignUpEndPointFactory.swift │ │ │ ├── UploadPostEndPointFactory.swift │ │ │ └── UserEndPointFactory.swift │ │ ├── HTTPMethod.swift │ │ ├── Requestable.swift │ │ └── Responsable.swift │ ├── Mock │ │ ├── MockData │ │ │ ├── CheckSignUp.json │ │ │ ├── CheckUserName.json │ │ │ ├── DeleteUser.json │ │ │ ├── DeleteVideo.json │ │ │ ├── GetMember.json │ │ │ ├── LoginData.json │ │ │ ├── PatchIntroduce.json │ │ │ ├── PatchProfileImage.json │ │ │ ├── PatchUserName.json │ │ │ ├── PostList.json │ │ │ ├── PostListEnd.json │ │ │ ├── PostListMore.json │ │ │ └── ReportPlaybackVideo.json │ │ └── MockURLProtocol.swift │ ├── Prefetcher │ │ └── Prefetcher.swift │ └── Provider │ │ └── Provider.swift ├── Resources │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ └── LayoverIcon.png │ │ ├── Colors │ │ │ ├── Background.colorset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Correct.colorset │ │ │ │ └── Contents.json │ │ │ ├── DarkGrey.colorset │ │ │ │ └── Contents.json │ │ │ ├── Error.colorset │ │ │ │ └── Contents.json │ │ │ ├── Grey100.colorset │ │ │ │ └── Contents.json │ │ │ ├── Grey200.colorset │ │ │ │ └── Contents.json │ │ │ ├── Grey300.colorset │ │ │ │ └── Contents.json │ │ │ ├── Grey400.colorset │ │ │ │ └── Contents.json │ │ │ ├── Grey500.colorset │ │ │ │ └── Contents.json │ │ │ ├── Kakao.colorset │ │ │ │ └── Contents.json │ │ │ ├── LayoverWhite.colorset │ │ │ │ └── Contents.json │ │ │ └── PrimaryPurple.colorset │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── LONormalThumb.imageset │ │ │ ├── Contents.json │ │ │ ├── normalImage 1.png │ │ │ ├── normalImage 2.png │ │ │ └── normalImage.png │ │ ├── LOSelectedThumb.imageset │ │ │ ├── Contents.json │ │ │ ├── selectedImage 1.png │ │ │ ├── selectedImage 2.png │ │ │ └── selectedImage.png │ │ ├── LocationPin.imageset │ │ │ ├── Contents.json │ │ │ ├── LocationPin.png │ │ │ ├── LocationPin@2x.png │ │ │ └── LocationPin@3x.png │ │ ├── Setting.imageset │ │ │ ├── Contents.json │ │ │ ├── Setting.png │ │ │ ├── Setting@2x.png │ │ │ └── Setting@3x.png │ │ ├── Title.imageset │ │ │ ├── Contents.json │ │ │ ├── Title.png │ │ │ ├── Title@2x.png │ │ │ └── Title@3x.png │ │ ├── appleLogo.imageset │ │ │ ├── Contents.json │ │ │ ├── appleLogo 1.png │ │ │ ├── appleLogo 2.png │ │ │ └── appleLogo.png │ │ ├── close.imageset │ │ │ ├── Close Button.png │ │ │ ├── Close Button@2x.png │ │ │ ├── Close Button@3x.png │ │ │ └── Contents.json │ │ ├── content.imageset │ │ │ ├── Contents.json │ │ │ ├── Document.png │ │ │ ├── Document@2x.png │ │ │ └── Document@3x.png │ │ ├── down.imageset │ │ │ ├── Contents.json │ │ │ ├── leading-icon.png │ │ │ ├── leading-icon@2x.png │ │ │ └── leading-icon@3x.png │ │ ├── icon_tab_back.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_tab_back.png │ │ │ ├── icon_tab_back@2x.png │ │ │ └── icon_tab_back@3x.png │ │ ├── kakaoLogo.imageset │ │ │ ├── Contents.json │ │ │ ├── kakaoLogo 1.png │ │ │ ├── kakaoLogo 2.png │ │ │ └── kakaoLogo.png │ │ ├── loLogo.imageset │ │ │ ├── Contents.json │ │ │ ├── LayoverLogo 1.png │ │ │ ├── LayoverLogo 2.png │ │ │ └── LayoverLogo.png │ │ ├── map.imageset │ │ │ ├── Contents.json │ │ │ ├── map.png │ │ │ ├── map@2x.png │ │ │ └── map@3x.png │ │ ├── mapPin.imageset │ │ │ ├── Contents.json │ │ │ ├── map_pin_highlighted.png │ │ │ ├── map_pin_highlighted@2x.png │ │ │ └── map_pin_highlighted@3x.png │ │ ├── mute.imageset │ │ │ ├── Contents.json │ │ │ ├── mute_icon.png │ │ │ ├── mute_icon@2x.png │ │ │ └── mute_icon@3x.png │ │ ├── myLocation.imageset │ │ │ ├── Contents.json │ │ │ ├── location_icon.png │ │ │ ├── location_icon@2x.png │ │ │ └── location_icon@3x.png │ │ ├── pause.imageset │ │ │ ├── Contents.json │ │ │ ├── pause 1.png │ │ │ ├── pause 2.png │ │ │ └── pause.png │ │ ├── photo.imageset │ │ │ ├── Contents.json │ │ │ ├── photo_icon.png │ │ │ ├── photo_icon@2x.png │ │ │ └── photo_icon@3x.png │ │ ├── planet.imageset │ │ │ ├── Contents.json │ │ │ ├── planet.png │ │ │ ├── planet@2x.png │ │ │ └── planet@3x.png │ │ ├── play.imageset │ │ │ ├── Contents.json │ │ │ ├── play 1.png │ │ │ ├── play 2.png │ │ │ └── play.png │ │ ├── plus.imageset │ │ │ ├── Contents.json │ │ │ ├── plus_icon.png │ │ │ ├── plus_icon@2x.png │ │ │ └── plus_icon@3x.png │ │ ├── profile.imageset │ │ │ ├── Contents.json │ │ │ ├── profile.png │ │ │ ├── profile@2x.png │ │ │ └── profile@3x.png │ │ ├── retry.imageset │ │ │ ├── Contents.json │ │ │ ├── interface-arrows-round-right--diagram-round-arrow-right.png │ │ │ ├── interface-arrows-round-right--diagram-round-arrow-right@2x.png │ │ │ └── interface-arrows-round-right--diagram-round-arrow-right@3x.png │ │ ├── scissors.imageset │ │ │ ├── Contents.json │ │ │ ├── scissors_icon.png │ │ │ ├── scissors_icon@2x.png │ │ │ └── scissors_icon@3x.png │ │ ├── smallPlus.imageset │ │ │ ├── Contents.json │ │ │ ├── smallPlus.png │ │ │ ├── smallPlus@2x.png │ │ │ └── smallPlus@3x.png │ │ ├── star.imageset │ │ │ ├── 3.png │ │ │ ├── 3@2x.png │ │ │ ├── 3@3x.png │ │ │ └── Contents.json │ │ ├── tag.imageset │ │ │ ├── Contents.json │ │ │ ├── box.png │ │ │ ├── box@2x.png │ │ │ └── box@3x.png │ │ └── volume.imageset │ │ │ ├── Contents.json │ │ │ ├── volume_icon.png │ │ │ ├── volume_icon@2x.png │ │ │ └── volume_icon@3x.png │ └── Fonts │ │ ├── Dashboard-Regular.ttf │ │ ├── Pretendard-Bold.ttf │ │ ├── Pretendard-Regular.ttf │ │ └── Pretendard-SemiBold.ttf ├── SceneDelegate.swift ├── Scenes │ ├── BaseViewController.swift │ ├── Configurator.swift │ ├── EditProfile │ │ ├── EditProfileConfigurator.swift │ │ ├── EditProfileInteractor.swift │ │ ├── EditProfileModels.swift │ │ ├── EditProfilePresenter.swift │ │ ├── EditProfileRouter.swift │ │ └── EditProfileViewController.swift │ ├── EditTag │ │ ├── EditTagConfigurator.swift │ │ ├── EditTagInteractor.swift │ │ ├── EditTagModels.swift │ │ ├── EditTagPresenter.swift │ │ ├── EditTagRouter.swift │ │ └── EditTagViewController.swift │ ├── EditVideo │ │ ├── EditVideoConfigurator.swift │ │ ├── EditVideoInteractor.swift │ │ ├── EditVideoModels.swift │ │ ├── EditVideoPresenter.swift │ │ ├── EditVideoRouter.swift │ │ ├── EditVideoViewController.swift │ │ └── EditVideoWorker.swift │ ├── Home │ │ ├── Cell │ │ │ └── HomeCarouselCollectionViewCell.swift │ │ ├── HomeConfigurator.swift │ │ ├── HomeInteractor.swift │ │ ├── HomeModels.swift │ │ ├── HomePresenter.swift │ │ ├── HomeRouter.swift │ │ ├── HomeViewController.swift │ │ └── HomeWorker.swift │ ├── Login │ │ ├── LoginConfigurator.swift │ │ ├── LoginInteractor.swift │ │ ├── LoginModels.swift │ │ ├── LoginPresenter.swift │ │ ├── LoginRouter.swift │ │ ├── LoginViewController.swift │ │ └── LoginWorker.swift │ ├── Map │ │ ├── LOAnnotation.swift │ │ ├── MapConfigurator.swift │ │ ├── MapInteractor.swift │ │ ├── MapModels.swift │ │ ├── MapPresenter.swift │ │ ├── MapRouter.swift │ │ ├── MapViewController.swift │ │ ├── MapWorker.swift │ │ └── Views │ │ │ ├── LOAnnotationView.swift │ │ │ └── MapCarouselCollectionViewCell.swift │ ├── Playback │ │ ├── Cell │ │ │ └── PlaybackCell.swift │ │ ├── PlaybackConfigurator.swift │ │ ├── PlaybackInteractor.swift │ │ ├── PlaybackModels.swift │ │ ├── PlaybackPresenter.swift │ │ ├── PlaybackRouter.swift │ │ ├── PlaybackView.swift │ │ ├── PlaybackViewController.swift │ │ └── PlaybackWorker.swift │ ├── Profile │ │ ├── ProfileConfigurator.swift │ │ ├── ProfileInteractor.swift │ │ ├── ProfileModels.swift │ │ ├── ProfilePresenter.swift │ │ ├── ProfileRouter.swift │ │ ├── ProfileViewController.swift │ │ └── Views │ │ │ ├── ProfileCollectionViewCell.swift │ │ │ └── ThumbnailCollectionViewCell.swift │ ├── Report │ │ ├── ReportConfigurator.swift │ │ ├── ReportInteractor.swift │ │ ├── ReportModels.swift │ │ ├── ReportPresenter.swift │ │ ├── ReportRouter.swift │ │ ├── ReportViewController.swift │ │ └── ReportWorker.swift │ ├── Setting │ │ ├── SettingConfigurator.swift │ │ ├── SettingInteractor.swift │ │ ├── SettingModels.swift │ │ ├── SettingPresenter.swift │ │ ├── SettingRouter.swift │ │ ├── SettingViewController.swift │ │ └── SettingWorker.swift │ ├── SignUpScene │ │ ├── SignUpConfigurator.swift │ │ ├── SignUpInteractor.swift │ │ ├── SignUpModels.swift │ │ ├── SignUpPresenter.swift │ │ ├── SignUpRouter.swift │ │ └── SignUpWorker.swift │ ├── SignUpViewController.swift │ ├── Tabbar │ │ ├── MainTabBarConfigurator.swift │ │ └── MainTabBarViewController.swift │ ├── TagPlayList │ │ ├── Cell │ │ │ └── TagPlayListCollectionViewCell.swift │ │ ├── TagPlayListConfigurator.swift │ │ ├── TagPlayListInteractor.swift │ │ ├── TagPlayListModels.swift │ │ ├── TagPlayListPresenter.swift │ │ ├── TagPlayListRouter.swift │ │ ├── TagPlayListViewController.swift │ │ └── TagPlayListWorker.swift │ ├── UIComponents │ │ ├── LoopingPlayerView.swift │ │ ├── PlayerView.swift │ │ └── Toast.swift │ └── UploadPost │ │ ├── UploadPostConfigurator.swift │ │ ├── UploadPostInteractor.swift │ │ ├── UploadPostModels.swift │ │ ├── UploadPostPresenter.swift │ │ ├── UploadPostRouter.swift │ │ ├── UploadPostViewController.swift │ │ ├── UploadPostWorker.swift │ │ └── VideoPickerManager.swift ├── Services │ ├── HLSResourceLoader │ │ ├── HLSAssetResourceLoaderDelegate.swift │ │ └── HLSSliceResourceLoader.swift │ ├── Keychain │ │ └── KeychainStored.swift │ ├── Location │ │ ├── CurrentLocationManager.swift │ │ └── LocationFetcher.swift │ ├── System.swift │ └── UserDefaults │ │ └── UserDefaultStored.swift ├── Workers │ ├── Mocks │ │ ├── MockHomeWorker.swift │ │ ├── MockLoginWorker.swift │ │ ├── MockPlaybackWorker.swift │ │ ├── MockReportWorker.swift │ │ ├── MockSignUpWorker.swift │ │ ├── MockTagPlayListWorker.swift │ │ ├── MockUserWorker.swift │ │ ├── StubAuthManager.swift │ │ └── sample.jpeg │ ├── UserWorker.swift │ └── VideoFileWorker.swift └── en.lproj │ └── LaunchScreen.strings └── LayoverTests ├── Mocks ├── LocationFetcher │ └── MockLocationFetcher.swift ├── MockDatas │ ├── CheckSignUp.json │ ├── CheckUserName.json │ ├── DeleteUser.json │ ├── DeleteVideo.json │ ├── GetMember.json │ ├── LoginData.json │ ├── PatchIntroduce.json │ ├── PatchProfileImage.json │ ├── PatchUserName.json │ ├── PostBoard.json │ ├── PostList.json │ ├── PostListEnd.json │ ├── PostListMore.json │ ├── PostsPage.json │ ├── ReportPlaybackVideo.json │ └── sample.jpeg └── Workers │ ├── MockHomeWorker.swift │ ├── MockPlaybackWorker.swift │ ├── MockSettingWorker.swift │ ├── MockSignUpWorker.swift │ ├── MockTagPlayListWorker.swift │ ├── MockUploadPostWorker.swift │ └── MockUserWorker.swift ├── Scenes ├── EditProfile │ ├── EditProfileInteractorTests.swift │ └── EditProfilePresenterTests.swift ├── Home │ ├── HomeInteractorTests.swift │ ├── HomePresenterTests.swift │ ├── HomeViewControllerTests.swift │ └── HomeWorkerTests.swift ├── Playback │ ├── PlaybackInteractorTests.swift │ └── PlaybackPresenterTests.swift ├── Profile │ ├── ProfileInteractorTests.swift │ ├── ProfilePresenterTests.swift │ └── ProfileViewControllerTests.swift ├── Setting │ ├── SettingInteractorTests.swift │ ├── SettingPresenterTests.swift │ └── SettingViewControllerTests.swift ├── SignUp │ ├── SignUpInteractorTests.swift │ ├── SignUpPresenterTests.swift │ └── SignUpWorkerTests.swift ├── TagPlayList │ ├── TagPlayListInteractorTests.swift │ ├── TagPlayListPresenterTests.swift │ ├── TagPlayListViewControllerTests.swift │ └── TagPlayListWorkerTests.swift └── UploadPost │ ├── UploadPostInteractorTests.swift │ ├── UploadPostPresenterTests.swift │ ├── UploadPostViewControllerTests.swift │ └── UploadPostWorkerTests.swift ├── Seeds.swift └── Stubs └── StubAuthManager.swift /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj binary merge=union 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/🐞-bug-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug Template" 3 | about: Create a report to help us improve 4 | title: 'bug: ...' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 🐞 버그 설명 11 | 스크린 샷, 작동 환경 (OS, device 등)을 적어주세요. 12 | 13 | ## 📝 todo 14 | - [ ] todo ! 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/💡-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1 Issue Template" 3 | about: Suggest an idea for this project 4 | title: 'feat: ...' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## 💡 issue 11 | 이슈에 대한 내용을 설명해주세요. 12 | 13 | ## 📝 todo 14 | - [ ] todo ! 15 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## 🧑‍🚀 PR 요약 2 | 해당 pr에서 작업한 내역을 적어주세요. 3 | 4 | #### 📌 변경 사항 5 | 변경사항 및 주의 사항 (모듈 설치 등)을 적어주세요. 6 | 7 | ##### 📸 ScreenShot 8 | 작동, 구현화면 9 | 10 | ##### ✅ PR check list 11 | ``` 12 | - commit message가 적절한지 확인해주세요. 13 | - 마지막으로 Coding Convention을 준수했는지 확인해주세요. 14 | - 적절한 branch로 요청했는지 확인해주세요. 15 | - Assignees, Label을 붙여주세요. 16 | - 가능한 이슈를 Link 해주세요. 17 | - PR이 승인된 경우 해당 브랜치는 삭제해주세요. 18 | ``` 19 | 20 | #### Linked Issue 21 | close # 22 | -------------------------------------------------------------------------------- /.github/secrets/Secrets.xcconfig.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/.github/secrets/Secrets.xcconfig.gpg -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /.idea/iOS09-Layover.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/jsLinters/eslint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BE/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/.gitkeep -------------------------------------------------------------------------------- /BE/layover/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 10 | root: true, 11 | env: { 12 | node: true, 13 | jest: true, 14 | }, 15 | ignorePatterns: ['.eslintrc.js'], 16 | rules: { 17 | 'prettier/prettier': ['error', { endOfLine: 'auto', printWidth: 120 }], 18 | '@typescript-eslint/interface-name-prefix': 'off', 19 | '@typescript-eslint/explicit-function-return-type': 'off', 20 | '@typescript-eslint/explicit-module-boundary-types': 'off', 21 | '@typescript-eslint/no-explicit-any': 'off', 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /BE/layover/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 120, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /BE/layover/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base Image 2 | FROM node:21 3 | 4 | # Create app directory 5 | WORKDIR /usr/src/app 6 | 7 | COPY package*.json ./ 8 | 9 | RUN npm install 10 | 11 | COPY . . 12 | 13 | RUN npm run build 14 | 15 | RUN npm test 16 | 17 | CMD [ "node", "dist/main.js" ] 18 | 19 | -------------------------------------------------------------------------------- /BE/layover/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | image: ${DOCKER_IMG_NAME}:${COMMIT_HASH} 5 | container_name: layover-container 6 | ports: 7 | - 3000:3000 8 | volumes: 9 | - ~/envs/.env:/usr/src/app/.env 10 | - ~/envs/ca_bundle.crt:/usr/src/app/ca_bundle.crt 11 | - ~/envs/certificate.crt:/usr/src/app/certificate.crt 12 | - ~/envs/private.key:/usr/src/app/private.key 13 | - /var/log/logs.log:/usr/src/app/logs.log 14 | restart: on-failure 15 | 16 | cadvisor: 17 | image: gcr.io/google-containers/cadvisor:latest 18 | container_name: cadvisor 19 | volumes: 20 | - /:/rootfs:ro 21 | - /var/run:/var/run:rw 22 | - /sys:/sys:ro 23 | - /var/lib/docker/:/var/lib/docker:ro 24 | restart: always 25 | ports: 26 | - 8090:8080 27 | 28 | prometheus: 29 | image: prom/prometheus 30 | container_name: prometheus 31 | volumes: 32 | - ./prometheus.yml:/etc/prometheus/prometheus.yml 33 | command: 34 | - '--config.file=/etc/prometheus/prometheus.yml' 35 | restart: always 36 | ports: 37 | - 9090:9090 38 | 39 | grafana: 40 | image: grafana/grafana 41 | container_name: grafana 42 | restart: always 43 | ports: 44 | - 3001:3000 -------------------------------------------------------------------------------- /BE/layover/dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | dist 6 | -------------------------------------------------------------------------------- /BE/layover/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BE/layover/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | 5 | scrape_configs: 6 | - job_name: 'prometheus' 7 | static_configs: 8 | - targets: ['localhost:9090'] 9 | 10 | - job_name: 'cadvisor' 11 | static_configs: 12 | - targets: ['cadvisor:8080'] 13 | -------------------------------------------------------------------------------- /BE/layover/public/apps/0.2.0/Layover.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/0.2.0/Layover.ipa -------------------------------------------------------------------------------- /BE/layover/public/apps/0.2.0/manifest.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | assets 9 | 10 | 11 | kind 12 | software-package 13 | url 14 | https://layoverapi.shop:3000/apps/0.2.0/Layover.ipa 15 | 16 | 17 | kind 18 | display-image 19 | url 20 | https://layoverapi.shop/apps/images/layover_1.png 21 | 22 | 23 | kind 24 | full-size-image 25 | url 26 | https://layoverapi.shop/apps/images/layover_2.png 27 | 28 | 29 | metadata 30 | 31 | bundle-identifier 32 | kr.codesquad.boostcamp8.Layover 33 | bundle-version 34 | 1.0 35 | kind 36 | software 37 | platform-identifier 38 | com.apple.platform.iphoneos 39 | title 40 | Layover 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BE/layover/public/apps/0.3.0/Layover.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/0.3.0/Layover.ipa -------------------------------------------------------------------------------- /BE/layover/public/apps/0.3.0/manifest.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | assets 9 | 10 | 11 | kind 12 | software-package 13 | url 14 | https://layoverapi.shop:3000/apps/0.3.0/Layover.ipa 15 | 16 | 17 | kind 18 | display-image 19 | url 20 | https://layoverapi.shop/apps/images/layover_1.png 21 | 22 | 23 | kind 24 | full-size-image 25 | url 26 | https://layoverapi.shop/apps/images/layover_2.png 27 | 28 | 29 | metadata 30 | 31 | bundle-identifier 32 | kr.codesquad.boostcamp8.Layover 33 | bundle-version 34 | 1.0 35 | kind 36 | software 37 | platform-identifier 38 | com.apple.platform.iphoneos 39 | title 40 | Layover 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BE/layover/public/apps/0.4.0/Layover.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/0.4.0/Layover.ipa -------------------------------------------------------------------------------- /BE/layover/public/apps/0.4.0/manifest.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | assets 9 | 10 | 11 | kind 12 | software-package 13 | url 14 | https://layoverapi.shop:3000/apps/0.4.0/Layover.ipa 15 | 16 | 17 | kind 18 | display-image 19 | url 20 | https://layoverapi.shop/apps/images/layover_1.png 21 | 22 | 23 | kind 24 | full-size-image 25 | url 26 | https://layoverapi.shop/apps/images/layover_2.png 27 | 28 | 29 | metadata 30 | 31 | bundle-identifier 32 | kr.codesquad.boostcamp8.Layover 33 | bundle-version 34 | 1.0 35 | kind 36 | software 37 | platform-identifier 38 | com.apple.platform.iphoneos 39 | title 40 | Layover 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BE/layover/public/apps/Layover.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/Layover.ipa -------------------------------------------------------------------------------- /BE/layover/public/apps/images/layover_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/images/layover_1.png -------------------------------------------------------------------------------- /BE/layover/public/apps/images/layover_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/apps/images/layover_2.png -------------------------------------------------------------------------------- /BE/layover/public/apps/manifest.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | items 6 | 7 | 8 | assets 9 | 10 | 11 | kind 12 | software-package 13 | url 14 | https://layoverapi.shop:3000/apps/Layover.ipa 15 | 16 | 17 | kind 18 | display-image 19 | url 20 | https://layoverapi.shop/apps/images/layover_1.png 21 | 22 | 23 | kind 24 | full-size-image 25 | url 26 | https://layoverapi.shop/apps/images/layover_2.png 27 | 28 | 29 | metadata 30 | 31 | bundle-identifier 32 | kr.codesquad.boostcamp8.Layover 33 | bundle-version 34 | 1.0 35 | kind 36 | software 37 | platform-identifier 38 | com.apple.platform.iphoneos 39 | title 40 | Layover 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BE/layover/public/iOS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Layover IPA deploy page 7 | 8 | 9 |

Layover IPA deploy page

10 | cat kong 11 |

Merry Christmas

12 |
13 | 0.1.0
18 | 0.2.0
23 | 0.3.0
28 | 0.4.0 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /BE/layover/public/images/kong.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/BE/layover/public/images/kong.jpeg -------------------------------------------------------------------------------- /BE/layover/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /BE/layover/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BE/layover/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { DatabaseModule } from './database/database.module'; 5 | import { OauthModule } from './oauth/oauth.module'; 6 | import { ServeStaticModule } from '@nestjs/serve-static'; 7 | import { join } from 'path'; 8 | import { BoardModule } from './board/board.module'; 9 | import { TagModule } from './tag/tag.module'; 10 | import { ReportModule } from './report/report.module'; 11 | import { LoggingInterceptor } from './utils/logging-interceptor'; 12 | import { APP_INTERCEPTOR } from '@nestjs/core'; 13 | 14 | @Module({ 15 | imports: [ 16 | DatabaseModule, 17 | OauthModule, 18 | ServeStaticModule.forRoot({ rootPath: join(__dirname, '..', 'public') }), 19 | BoardModule, 20 | TagModule, 21 | ReportModule, 22 | ], 23 | controllers: [AppController], 24 | providers: [ 25 | AppService, 26 | { 27 | provide: APP_INTERCEPTOR, 28 | useClass: LoggingInterceptor, 29 | }, 30 | ], 31 | }) 32 | export class AppModule {} 33 | -------------------------------------------------------------------------------- /BE/layover/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BE/layover/src/board/board.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { BoardController } from './board.controller'; 3 | import { BoardService } from './board.service'; 4 | import { DatabaseModule } from '../database/database.module'; 5 | import { boardProvider } from './board.provider'; 6 | import { MemberModule } from '../member/member.module'; 7 | import { TagModule } from '../tag/tag.module'; 8 | import { BoardRepository } from './board.repository'; 9 | 10 | @Module({ 11 | imports: [DatabaseModule, forwardRef(() => MemberModule), TagModule], 12 | providers: [BoardService, BoardRepository, ...boardProvider], 13 | exports: [BoardService], 14 | controllers: [BoardController], 15 | }) 16 | export class BoardModule {} 17 | -------------------------------------------------------------------------------- /BE/layover/src/board/board.provider.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm'; 2 | import { Board } from './board.entity'; 3 | 4 | export const boardProvider = [ 5 | { 6 | provide: 'BOARD_REPOSITORY', 7 | useFactory: (dataSource: DataSource) => dataSource.getRepository(Board), 8 | inject: ['DATA_SOURCE'], 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/board-pre-signed-url.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsNotEmpty, IsNumber, IsString } from 'class-validator'; 3 | 4 | export class BoardPreSignedUrlDto { 5 | @ApiProperty({ 6 | example: 1, 7 | description: '업로드할 동영상의 게시글 id', 8 | }) 9 | @IsNumber() 10 | @IsNotEmpty() 11 | readonly boardId: number; 12 | 13 | @ApiProperty({ 14 | example: 'mp4', 15 | description: '업로드할 동영상 타입', 16 | }) 17 | @IsString() 18 | @IsNotEmpty() 19 | readonly filetype: string; 20 | } 21 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/boards-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { MemberInfosResDto } from '../../member/dtos/member-infos-res.dto'; 2 | import { BoardResDto } from './board-res-dto'; 3 | import { ApiProperty } from '@nestjs/swagger'; 4 | 5 | export class BoardsResDto { 6 | @ApiProperty({ type: () => MemberInfosResDto }) 7 | member: MemberInfosResDto; 8 | 9 | @ApiProperty({ type: () => BoardResDto }) 10 | board: BoardResDto; 11 | 12 | @ApiProperty({ 13 | example: ['tag1', 'tag2'], 14 | description: '게시글의 태그', 15 | }) 16 | tag: string[]; 17 | 18 | constructor(member: MemberInfosResDto, board: BoardResDto, tag: string[]) { 19 | this.member = member; 20 | this.board = board; 21 | this.tag = tag; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/create-board-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CreateBoardResDto { 4 | @ApiProperty({ 5 | example: 1, 6 | description: '게시글 id', 7 | }) 8 | readonly id: number; 9 | 10 | @ApiProperty({ 11 | example: '제목', 12 | description: '게시글 제목', 13 | }) 14 | readonly title: string; 15 | 16 | @ApiProperty({ 17 | example: '내용', 18 | description: '게시글 내용', 19 | }) 20 | readonly content: string; 21 | 22 | @ApiProperty({ 23 | example: '37.12310530', 24 | description: '위도', 25 | }) 26 | readonly latitude: number; 27 | 28 | @ApiProperty({ 29 | example: '127.12310530', 30 | description: '경도', 31 | }) 32 | readonly longitude: number; 33 | 34 | @ApiProperty({ 35 | example: ['부산', '광안리', '바다'], 36 | description: '사용자가 작성한 태그들', 37 | }) 38 | readonly tag: string[]; 39 | 40 | constructor(id: number, title: string, content: string, latitude: number, longitude: number, tag: string[]) { 41 | this.id = id; 42 | this.title = title; 43 | this.content = content; 44 | this.latitude = latitude; 45 | this.longitude = longitude; 46 | this.tag = tag; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/create-board.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsOptional, IsString } from 'class-validator'; 3 | 4 | export class CreateBoardDto { 5 | @ApiProperty({ 6 | example: '부산 광안리', 7 | description: '제목', 8 | }) 9 | readonly title: string; 10 | 11 | @ApiProperty({ 12 | example: 'chilling at the beach~', 13 | description: '내용', 14 | required: false, 15 | }) 16 | @IsString() 17 | @IsOptional() 18 | content?: string; 19 | 20 | @ApiProperty({ 21 | example: '37.0532156213', 22 | description: '게시글 위도', 23 | }) 24 | latitude: number; 25 | 26 | @ApiProperty({ 27 | example: '37.0532156213', 28 | description: '게시글 경도', 29 | }) 30 | longitude: number; 31 | 32 | @ApiProperty({ 33 | example: ['부산', '광안리', '바다'], 34 | description: '사용자가 작성한 태그들', 35 | required: false, 36 | }) 37 | @IsOptional() 38 | tag?: string[]; 39 | 40 | constructor(title: string, content: string, latitude: number, longitude: number, tag: string[]) { 41 | this.title = title; 42 | this.content = content; 43 | this.latitude = latitude; 44 | this.longitude = longitude; 45 | this.tag = tag; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/encoding-callback.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class EncodingCallbackDto { 5 | @ApiProperty({ 6 | example: 'uusu123135131-123143ads-213', 7 | description: '동영상 파일 이름', 8 | }) 9 | @IsString() 10 | readonly filename: string; 11 | 12 | @ApiProperty({ 13 | example: 'RUNNING', 14 | description: '입력 파일의 인코딩 상태', 15 | }) 16 | @IsString() 17 | readonly status: string; 18 | 19 | @ApiProperty({ 20 | example: 'ncloud.video.download.com/sfsdsdsdsfsd', 21 | description: 'm3u8 master 링크', 22 | }) 23 | readonly filePath?: string; 24 | } 25 | -------------------------------------------------------------------------------- /BE/layover/src/board/dtos/upload-callback.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class UploadCallbackDto { 5 | @ApiProperty({ 6 | example: 'sample.mp4', 7 | description: '업로드된 원본 파일 이름', 8 | }) 9 | @IsString() 10 | readonly filename: string; 11 | } 12 | -------------------------------------------------------------------------------- /BE/layover/src/config.ts: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN_EXP_IN_SECOND = 60 * 60 * 24 * 7; 2 | export const REFRESH_TOKEN_EXP_IN_SECOND = 60 * 60 * 24 * 30; 3 | export const MAX_USERNAME = 20; 4 | -------------------------------------------------------------------------------- /BE/layover/src/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { databaseProviders } from './database.provider'; 3 | import { ConfigModule } from '@nestjs/config'; 4 | 5 | @Module({ 6 | imports: [ConfigModule.forRoot()], 7 | providers: [...databaseProviders], 8 | exports: [...databaseProviders], 9 | }) 10 | export class DatabaseModule {} 11 | -------------------------------------------------------------------------------- /BE/layover/src/database/database.provider.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm'; 2 | 3 | export const databaseProviders = [ 4 | { 5 | provide: 'DATA_SOURCE', // 둘 이상의 provider가 있으면 얘로 구분됨. 따로 작성하지 않으면 class 이름이 provider 키가 됨. 6 | useFactory: async () => { 7 | const dataSource = new DataSource({ 8 | type: 'mysql', 9 | host: process.env.MYSQL_HOST, 10 | port: Number(process.env.MYSQL_PORT), 11 | username: process.env.MYSQL_USERNAME, 12 | password: process.env.MYSQL_PASSWORD, 13 | database: 'layover_test', 14 | entities: [__dirname + '/../**/*.entity{.ts,.js}'], 15 | synchronize: true, 16 | charset: 'utf8mb4_0900_ai_ci', 17 | extra: { 18 | charset: 'utf8mb4_0900_ai_ci', 19 | }, 20 | }); 21 | 22 | return dataSource.initialize(); 23 | }, 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/check-username-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CheckUsernameResDto { 4 | @ApiProperty({ 5 | example: 'true', 6 | description: '닉네임 검증 결과 bool 값', 7 | }) 8 | isValid: boolean; 9 | 10 | constructor(isValid: boolean) { 11 | this.isValid = isValid; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/check-username.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CheckUsernameDto { 4 | @ApiProperty({ 5 | example: 'hwani', 6 | description: '유효성을 검증하거나 변경하고자 하는 닉네임', 7 | }) 8 | readonly username: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/delete-member-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class DeleteMemberResDto { 4 | @ApiProperty({ 5 | example: 'hooni', 6 | description: '삭제된 멤버의 닉네임', 7 | }) 8 | username: string; 9 | 10 | constructor(username: string) { 11 | this.username = username; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/introduce-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class IntroduceResDto { 4 | @ApiProperty({ 5 | example: 'Hi, my name is hwani', 6 | description: '새로운 자기소개', 7 | }) 8 | introduce: string; 9 | 10 | constructor(introduce: string) { 11 | this.introduce = introduce; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/introduce.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class IntroduceDto { 4 | @ApiProperty({ 5 | example: 'Hi, my name is hwani', 6 | description: '새로운 자기소개', 7 | }) 8 | readonly introduce: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/member-infos-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class MemberInfosResDto { 4 | @ApiProperty({ 5 | example: 221, 6 | description: '요청한 회원의 id 값(해당 회원을 유일하게 구분하는 값)', 7 | }) 8 | id: number; 9 | 10 | @ApiProperty({ 11 | example: 'hwani', 12 | description: '요청한 회원의 닉네임', 13 | }) 14 | username: string; 15 | 16 | @ApiProperty({ 17 | example: 'Hi, my name is hwani', 18 | description: '요청한 회원의 자기소개', 19 | }) 20 | introduce: string; 21 | 22 | @ApiProperty({ 23 | example: 'https://layover-profile-image.kr.obj...', 24 | description: '요청한 회원의 프로필 이미지를 다운받을 수 있는 url', 25 | }) 26 | profile_image_url: string | null; 27 | 28 | constructor(id: number, username: string, introduce: string, profile_image_url: string | null) { 29 | this.id = id; 30 | this.username = username; 31 | this.introduce = introduce; 32 | this.profile_image_url = profile_image_url; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/profile-pre-signed-url.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class ProfilePreSignedUrlDto { 4 | @ApiProperty({ 5 | example: 'jpeg', 6 | description: '업로드할 영상 이름과 타입', 7 | }) 8 | readonly filetype: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/username-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class UsernameResDto { 4 | @ApiProperty({ 5 | example: 'hwani2', 6 | description: '변경된 닉네임 값', 7 | }) 8 | username: string; 9 | 10 | constructor(username: string) { 11 | this.username = username; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/src/member/dtos/username.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class UsernameDto { 4 | @ApiProperty({ 5 | example: 'hwani1', 6 | description: '변경 변경하고자 하는 닉네임', 7 | }) 8 | readonly username: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/member/member.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; 2 | 3 | export type memberStatus = 'EXIST' | 'DELETED'; 4 | 5 | @Entity() 6 | export class Member { 7 | @PrimaryGeneratedColumn('increment') 8 | id: number; 9 | 10 | @Column({ nullable: false, length: 20 }) 11 | username: string; 12 | 13 | @Column({ nullable: false, length: 255 }) 14 | profile_image_key: string; 15 | 16 | @Column({ nullable: false, length: 100 }) 17 | introduce: string; 18 | 19 | @Column({ nullable: false, length: 255 }) 20 | provider: string; 21 | 22 | @Column({ nullable: false, length: 255 }) 23 | hash: string; 24 | 25 | @Column({ 26 | nullable: false, 27 | type: 'datetime', 28 | default: () => 'CURRENT_TIMESTAMP', 29 | }) 30 | date_created: Date; 31 | 32 | @Column({ 33 | nullable: false, 34 | length: 20, 35 | }) 36 | status: memberStatus; 37 | 38 | constructor( 39 | id: number, 40 | username: string, 41 | profile_image_key: string, 42 | introduce: string, 43 | provider: string, 44 | hash: string, 45 | date_created: Date, 46 | status: memberStatus, 47 | ) { 48 | this.id = id; 49 | this.username = username; 50 | this.profile_image_key = profile_image_key; 51 | this.introduce = introduce; 52 | this.provider = provider; 53 | this.hash = hash; 54 | this.date_created = date_created; 55 | this.status = status; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /BE/layover/src/member/member.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { DatabaseModule } from '../database/database.module'; 3 | import { memberProviders } from './member.providers'; 4 | import { MemberService } from './member.service'; 5 | import { MemberController } from './member.controller'; 6 | import { MemberRepository } from './member.repository'; 7 | import { BoardModule } from 'src/board/board.module'; 8 | import { ReportModule } from 'src/report/report.module'; 9 | import { RedisModule } from 'src/redis/redis.module'; 10 | 11 | @Module({ 12 | imports: [DatabaseModule, forwardRef(() => BoardModule), forwardRef(() => ReportModule), RedisModule], 13 | providers: [...memberProviders, MemberService, MemberRepository], 14 | exports: [MemberService], 15 | controllers: [MemberController], 16 | }) 17 | export class MemberModule {} 18 | -------------------------------------------------------------------------------- /BE/layover/src/member/member.providers.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm'; 2 | import { Member } from './member.entity'; 3 | 4 | export const memberProviders = [ 5 | { 6 | provide: 'MEMBER_REPOSITORY', 7 | useFactory: (dataSource: DataSource) => dataSource.getRepository(Member), 8 | inject: ['DATA_SOURCE'], 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/apple-login.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class AppleLoginDto { 4 | @ApiProperty({ 5 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 6 | description: '애플 아이덴티티 토큰', 7 | }) 8 | readonly identityToken: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/apple-signup.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class AppleSignupDto { 4 | @ApiProperty({ 5 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 6 | description: '애플 아이덴티티 토큰', 7 | }) 8 | readonly identityToken: string; 9 | 10 | @ApiProperty({ 11 | example: 'myUsername', 12 | description: '설정할 유저 닉네임', 13 | }) 14 | readonly username: string; 15 | } 16 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/check-signup-res-dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class CheckSignupResDto { 4 | @ApiProperty({ 5 | example: 'true', 6 | description: '해당 계정이 이미 존재하는지 여부 boolean 값', 7 | }) 8 | isAlreadyExist: boolean; 9 | 10 | constructor(isAlreadyExist: boolean) { 11 | this.isAlreadyExist = isAlreadyExist; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/kakao-login.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class KakaoLoginDto { 4 | @ApiProperty({ 5 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 6 | description: '카카오 액세스 토큰', 7 | }) 8 | readonly accessToken: string; 9 | } 10 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/kakao-signup.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class KakaoSignupDto { 4 | @ApiProperty({ 5 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 6 | description: '카카오 액세스 토큰', 7 | }) 8 | readonly accessToken: string; 9 | 10 | @ApiProperty({ 11 | example: 'myUsername', 12 | description: '설정할 유저 닉네임', 13 | }) 14 | readonly username: string; 15 | } 16 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/dtos/token-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class TokenResDto { 4 | @ApiProperty({ 5 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 6 | description: 'JWT Access 토큰', 7 | }) 8 | accessToken: string; 9 | 10 | @ApiProperty({ 11 | example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 12 | description: 'JWT Refresh 토큰', 13 | }) 14 | refreshToken: string; 15 | 16 | constructor(accessToken: string, refreshToken: string) { 17 | this.accessToken = accessToken; 18 | this.refreshToken = refreshToken; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/oauth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { OauthController } from './oauth.controller'; 3 | import { HttpModule } from '@nestjs/axios'; 4 | import { OauthService } from './oauth.service'; 5 | import { MemberModule } from 'src/member/member.module'; 6 | import { JwtModule } from '@nestjs/jwt'; 7 | import { RedisModule } from 'src/redis/redis.module'; 8 | import { ConfigModule } from '@nestjs/config'; 9 | import { BoardModule } from 'src/board/board.module'; 10 | 11 | @Module({ 12 | imports: [ 13 | ConfigModule.forRoot(), 14 | HttpModule, 15 | MemberModule, 16 | BoardModule, 17 | JwtModule.register({ 18 | signOptions: { algorithm: 'HS256' }, 19 | secret: process.env.JWT_SECRET_KEY, 20 | }), 21 | RedisModule, 22 | ], 23 | controllers: [OauthController], 24 | providers: [OauthService], 25 | }) 26 | export class OauthModule {} 27 | -------------------------------------------------------------------------------- /BE/layover/src/oauth/oauth.repository.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class OauthRepository {} 5 | -------------------------------------------------------------------------------- /BE/layover/src/pipes/custom-header.decorator.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext, createParamDecorator } from '@nestjs/common'; 2 | 3 | export const CustomHeader = createParamDecorator((data: string, ctx: ExecutionContext) => { 4 | const req = ctx.switchToHttp().getRequest(); 5 | return data ? req.headers[data] : req.headers; 6 | }); 7 | -------------------------------------------------------------------------------- /BE/layover/src/redis/redis.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { redisProvider } from './redis.provider'; 3 | 4 | @Global() 5 | @Module({ 6 | providers: [...redisProvider], 7 | exports: [...redisProvider], 8 | }) 9 | export class RedisModule {} 10 | -------------------------------------------------------------------------------- /BE/layover/src/redis/redis.provider.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from 'redis'; 2 | 3 | export const redisProvider = [ 4 | { 5 | provide: 'REDIS_CLIENT', 6 | useFactory: async (): Promise> => { 7 | const client = await createClient({ 8 | url: `redis://${process.env.REDIS_USERNAME}:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`, 9 | }) 10 | .on('error', (err) => console.log('Redis Client Error', err)) 11 | .connect(); 12 | return client; 13 | }, 14 | }, 15 | { 16 | provide: 'REDIS_FOR_BLACKLIST_CLIENT', 17 | useFactory: async (): Promise> => { 18 | const client = await createClient({ 19 | url: `redis://${process.env.REDIS_FOR_BLACKLIST_USERNAME}:${process.env.REDIS_FOR_BLACKLIST_PASSWORD}@${process.env.REDIS_FOR_BLACKLIST_HOST}:${process.env.REDIS_FOR_BLACKLIST_PORT}`, 20 | }) 21 | .on('error', (err) => console.log('Redis Client Error', err)) 22 | .connect(); 23 | return client; 24 | }, 25 | }, 26 | ]; 27 | -------------------------------------------------------------------------------- /BE/layover/src/report/dtos/report-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class ReportResDto { 4 | @ApiProperty({ 5 | example: 221, 6 | description: '신고한 멤버의 id', 7 | }) 8 | memberId: number; 9 | 10 | @ApiProperty({ 11 | example: 5, 12 | description: '신고 게시글 id', 13 | }) 14 | boardId: number; 15 | 16 | @ApiProperty({ 17 | example: '청소년에게 유해한 내용이에요', 18 | description: '신고 유형', 19 | }) 20 | reportType: string; 21 | 22 | constructor(memberId: number, boardId: number, reportType: string) { 23 | this.memberId = memberId; 24 | this.boardId = boardId; 25 | this.reportType = reportType; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BE/layover/src/report/dtos/report.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class ReportDto { 4 | @ApiProperty({ 5 | example: 5, 6 | description: '신고 게시글 id', 7 | }) 8 | readonly boardId: number; 9 | 10 | @ApiProperty({ 11 | example: '청소년에게 유해한 내용이에요', 12 | description: '신고 유형', 13 | }) 14 | readonly reportType: string; 15 | } 16 | -------------------------------------------------------------------------------- /BE/layover/src/report/report.entity.ts: -------------------------------------------------------------------------------- 1 | import { Board } from 'src/board/board.entity'; 2 | import { Member } from 'src/member/member.entity'; 3 | import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; 4 | 5 | @Entity() 6 | export class Report { 7 | @PrimaryGeneratedColumn('increment') 8 | id: number; 9 | 10 | @ManyToOne(() => Member, (member) => member.id) 11 | @JoinColumn({ name: 'member_id' }) 12 | member: Member; 13 | 14 | @ManyToOne(() => Board, (board) => board.id) 15 | @JoinColumn({ name: 'board_id' }) 16 | board: Board; 17 | 18 | @Column({ nullable: false, length: 255 }) 19 | report_type: string; 20 | } 21 | -------------------------------------------------------------------------------- /BE/layover/src/report/report.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { ReportController } from './report.controller'; 3 | import { reportProviders } from './report.provider'; 4 | import { ReportService } from './report.service'; 5 | import { MemberModule } from 'src/member/member.module'; 6 | import { DatabaseModule } from 'src/database/database.module'; 7 | import { BoardModule } from 'src/board/board.module'; 8 | 9 | @Module({ 10 | imports: [DatabaseModule, forwardRef(() => MemberModule), BoardModule], 11 | controllers: [ReportController], 12 | providers: [...reportProviders, ReportService], 13 | exports: [ReportService], 14 | }) 15 | export class ReportModule {} 16 | -------------------------------------------------------------------------------- /BE/layover/src/report/report.provider.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm'; 2 | import { Report } from './report.entity'; 3 | 4 | export const reportProviders = [ 5 | { 6 | provide: 'REPORT_REPOSITORY', 7 | useFactory: (dataSource: DataSource) => dataSource.getRepository(Report), 8 | inject: ['DATA_SOURCE'], 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /BE/layover/src/report/report.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { MemberService } from 'src/member/member.service'; 3 | import { Report } from './report.entity'; 4 | import { Repository } from 'typeorm'; 5 | import { BoardService } from 'src/board/board.service'; 6 | import { ReportResDto } from './dtos/report-res.dto'; 7 | 8 | @Injectable() 9 | export class ReportService { 10 | constructor( 11 | @Inject('REPORT_REPOSITORY') private readonly reportRepository: Repository, 12 | private readonly memberService: MemberService, 13 | private readonly boardService: BoardService, 14 | ) {} 15 | 16 | async createReport(memberId: number, boardId: number, reportType: string): Promise { 17 | const member = await this.memberService.getMemberById(memberId); 18 | const board = await this.boardService.getBoardById(boardId); 19 | await this.reportRepository.insert({ 20 | member: member, 21 | board: board, 22 | report_type: reportType, 23 | }); 24 | return new ReportResDto(member.id, board.id, reportType); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BE/layover/src/report/report.swagger.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus } from '@nestjs/common'; 2 | import { getSchemaPath } from '@nestjs/swagger'; 3 | import { ReportResDto } from './dtos/report-res.dto'; 4 | 5 | export const REPORT_SWAGGER = { 6 | RECEIVE_REPORT_SUCCESS: { 7 | status: HttpStatus.OK, 8 | description: '신고 요청 성공', 9 | schema: { 10 | type: 'object', 11 | properties: { 12 | message: { type: 'string', example: '요청이 성공적으로 처리되었습니다.' }, 13 | statusCode: { type: 'number', example: HttpStatus.OK }, 14 | data: { $ref: getSchemaPath(ReportResDto) }, 15 | }, 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /BE/layover/src/response/custom-response.ts: -------------------------------------------------------------------------------- 1 | import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common'; 2 | import { Response } from 'express'; 3 | import { ECustomCode } from './ecustom-code.jenum'; 4 | import { ApiProperty } from '@nestjs/swagger'; 5 | 6 | export class CustomResponse extends HttpException { 7 | @ApiProperty({ 8 | description: '응답 데이터', 9 | }) 10 | data?: any; 11 | 12 | constructor(customException: ECustomCode, data?: any) { 13 | super(customException.message, customException.statusCode); 14 | this.data = data; 15 | } 16 | } 17 | 18 | @Catch() 19 | export class GlobalExceptionFilter implements ExceptionFilter { 20 | catch(exception: any, host: ArgumentsHost) { 21 | const ctx = host.switchToHttp(); 22 | const response: Response = ctx.getResponse(); 23 | 24 | let statusCode: HttpStatus; 25 | 26 | if (exception instanceof CustomResponse) { 27 | statusCode = exception.getStatus(); 28 | } else if (exception instanceof HttpException) { 29 | statusCode = exception.getStatus(); 30 | } else { 31 | statusCode = HttpStatus.INTERNAL_SERVER_ERROR; 32 | } 33 | 34 | response.status(statusCode).json({ 35 | message: exception.message, 36 | statusCode: statusCode, 37 | data: exception.data, 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BE/layover/src/tag/tag.entity.ts: -------------------------------------------------------------------------------- 1 | import { Board } from '../board/board.entity'; 2 | import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; 3 | 4 | @Entity() 5 | export class Tag { 6 | @PrimaryGeneratedColumn('increment') 7 | id: number; 8 | 9 | @ManyToOne(() => Board, (board) => board.id) 10 | @JoinColumn({ name: 'board_id' }) 11 | board: Board; 12 | 13 | @Column({ nullable: false }) 14 | tagname: string; 15 | } 16 | -------------------------------------------------------------------------------- /BE/layover/src/tag/tag.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TagService } from './tag.service'; 3 | import { tagProvider } from './tag.provider'; 4 | import { DatabaseModule } from '../database/database.module'; 5 | import { TagRepository } from './tag.repository'; 6 | 7 | @Module({ 8 | imports: [DatabaseModule], 9 | providers: [...tagProvider, TagService, TagRepository], 10 | exports: [TagService], 11 | }) 12 | export class TagModule {} 13 | -------------------------------------------------------------------------------- /BE/layover/src/tag/tag.provider.ts: -------------------------------------------------------------------------------- 1 | import { DataSource } from 'typeorm'; 2 | import { Tag } from './tag.entity'; 3 | 4 | export const tagProvider = [ 5 | { 6 | provide: 'TAG_REPOSITORY', 7 | useFactory: (dataSource: DataSource) => dataSource.getRepository(Tag), 8 | inject: ['DATA_SOURCE'], 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /BE/layover/src/tag/tag.repository.ts: -------------------------------------------------------------------------------- 1 | import { Inject } from '@nestjs/common'; 2 | import { Repository } from 'typeorm'; 3 | import { Tag } from './tag.entity'; 4 | import { Board } from '../board/board.entity'; 5 | 6 | export class TagRepository { 7 | constructor(@Inject('TAG_REPOSITORY') private tagRepository: Repository) {} 8 | 9 | async saveTag(board: Board, tagname: string) { 10 | await this.tagRepository.save({ board: board, tagname: tagname }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BE/layover/src/tag/tag.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Board } from '../board/board.entity'; 3 | import { TagRepository } from './tag.repository'; 4 | 5 | @Injectable() 6 | export class TagService { 7 | constructor(private tagRepository: TagRepository) {} 8 | 9 | async createTag(board: Board, tagname: string): Promise { 10 | await this.tagRepository.saveTag(board, tagname); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BE/layover/src/utils/hashUtils.ts: -------------------------------------------------------------------------------- 1 | import { createHash, createHmac } from 'crypto'; 2 | 3 | export function hashSHA256(input: string): string { 4 | const hash = createHash('sha256'); 5 | hash.update(input); 6 | return hash.digest('hex'); 7 | } 8 | 9 | export function hashHMACSHA256(message: string, secret: string): string { 10 | const hmac = createHmac('sha256', secret); 11 | hmac.update(message); 12 | 13 | const signature = hmac.digest('base64url'); 14 | return signature; 15 | } 16 | -------------------------------------------------------------------------------- /BE/layover/src/utils/interfaces/token.payload.d.ts: -------------------------------------------------------------------------------- 1 | export interface tokenPayload { 2 | iss: string; 3 | exp: number; 4 | sub: string; 5 | aud: string; 6 | nbf: number; 7 | iat: number; 8 | jti: string; 9 | memberHash: string; 10 | memberId: number; 11 | } 12 | -------------------------------------------------------------------------------- /BE/layover/src/utils/my-logger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { createLogger, format, transports } from 'winston'; 3 | import * as moment from 'moment-timezone'; 4 | 5 | export class MyLogger extends Logger { 6 | private winstonLogger = createLogger({ 7 | format: format.combine( 8 | format.timestamp({ 9 | format: () => moment().tz('Asia/Seoul').format(), 10 | }), 11 | format.printf(({ level, message, timestamp }) => `${timestamp} ${level}: ${message}`), 12 | ), 13 | transports: [new transports.File({ filename: 'logs.log' })], 14 | }); 15 | 16 | log(message: string) { 17 | super.log(message); 18 | this.winstonLogger.info(message); 19 | } 20 | 21 | error(message: string) { 22 | super.error(message); 23 | this.winstonLogger.error(message); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BE/layover/src/utils/pre-signed-url-res.dto.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from '@nestjs/swagger'; 2 | 3 | export class PreSignedUrlResDto { 4 | @ApiProperty({ 5 | example: 'https://layover-original-video.s3.amazonaws.com/14bb1a79093f5b9443e9b8105.....', 6 | description: '동영상을 업로드 하기 위해 요청할 링크', 7 | }) 8 | readonly preSignedUrl: string; 9 | 10 | constructor(preSignedUrl: string) { 11 | this.preSignedUrl = preSignedUrl; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BE/layover/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /BE/layover/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": "../", 4 | "modulePaths": [""], 5 | "testEnvironment": "node", 6 | "testRegex": ".e2e-spec.ts$", 7 | "transform": { 8 | "^.+\\.(t|j)s$": "ts-jest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BE/layover/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /BE/layover/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "ES2021", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iOS/Layover/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - todo 3 | - nesting 4 | 5 | line_length: 200 6 | excluded: 7 | - Layover/AppDelegate.swift 8 | - Layover/SceneDelegate.swift 9 | 10 | identifier_name: 11 | excluded: 12 | - ui 13 | - id 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/DesignSystem/LOSlider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LOSlider.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 11/15/23. 6 | // 7 | 8 | import UIKit 9 | 10 | final class LOSlider: UISlider { 11 | static let loSliderHeight: CGFloat = 10 12 | 13 | override init(frame: CGRect) { 14 | super.init(frame: frame) 15 | setUI() 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | super.init(coder: coder) 20 | setUI() 21 | } 22 | 23 | override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { 24 | setThumbImage(UIImage.loSelectedThumb, for: .normal) 25 | return true 26 | } 27 | 28 | override func endTracking(_ touch: UITouch?, with event: UIEvent?) { 29 | setThumbImage(UIImage.loNormalThumb, for: .normal) 30 | } 31 | 32 | private func setUI() { 33 | self.minimumTrackTintColor = .primaryPurple 34 | setThumbImage(UIImage.loNormalThumb, for: .normal) 35 | self.minimumValue = 0 36 | self.maximumValue = 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/DesignSystem/LOTextLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LOTextLabel.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 1/25/24. 6 | // Copyright © 2024 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class LOTextLabel: UILabel { 12 | 13 | private var padding = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0) 14 | 15 | convenience init(padding: UIEdgeInsets) { 16 | self.init() 17 | self.padding = padding 18 | setUI() 19 | } 20 | 21 | override func drawText(in rect: CGRect) { 22 | super.drawText(in: rect.inset(by: padding)) 23 | } 24 | 25 | override var intrinsicContentSize: CGSize { 26 | var contentSize = super.intrinsicContentSize 27 | contentSize.height += padding.top + padding.bottom 28 | contentSize.width += padding.left + padding.right 29 | 30 | return contentSize 31 | } 32 | 33 | private func setUI() { 34 | layer.cornerRadius = 8 35 | layer.borderWidth = 1 36 | layer.borderColor = UIColor.grey500.cgColor 37 | backgroundColor = UIColor.clear 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/AVFileType+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AVFileType+.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/14. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | 11 | extension AVFileType { 12 | static func from(_ url: URL) -> AVFileType? { 13 | let pathExtension = url.pathExtension 14 | switch pathExtension { 15 | case "mp4": 16 | return .mp4 17 | case "mov": 18 | return .mov 19 | case "m4v": 20 | return .m4v 21 | default: 22 | return nil 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/Notification.Name+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notification.Name+.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/27/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Notification.Name { 12 | static let refreshTokenDidExpired = Notification.Name("refreshTokenDidExpired") 13 | static let uploadTaskStart = Notification.Name("uploadTaskStart") 14 | static let progressChanged = Notification.Name("progressChanged") 15 | static let uploadTaskDidComplete = Notification.Name("uploadTaskDidComplete") 16 | static let uploadTaskDidFail = Notification.Name("uploadTaskDidFail") 17 | } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/OSLog+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OSLog+.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/14/23. 6 | // 7 | 8 | import OSLog 9 | 10 | extension OSLog { 11 | private static var subsystem = Bundle.main.bundleIdentifier ?? "kr.codesquad.boostcamp8.Layover" 12 | 13 | // add more OSLog if you need. 14 | static let ui = OSLog(subsystem: subsystem, category: "UI") 15 | static let data = OSLog(subsystem: subsystem, category: "Data") 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/Sequence+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sequence+.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/30/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | extension Sequence { 10 | func asyncMap( 11 | _ transform: (Element) async throws -> T 12 | ) async rethrows -> [T] { 13 | var values = [T]() 14 | 15 | for element in self { 16 | try await values.append(transform(element)) 17 | } 18 | 19 | return values 20 | } 21 | 22 | func asyncCompactMap( 23 | _ transform: (Element) async throws -> T? 24 | ) async rethrows -> [T] { 25 | var values = [T]() 26 | 27 | for element in self { 28 | if let value = try await transform(element) { 29 | values.append(value) 30 | } 31 | } 32 | 33 | return values 34 | } 35 | 36 | func concurrentMap( 37 | _ transform: @escaping (Element) async throws -> T 38 | ) async throws -> [T] { 39 | let tasks = map { element in 40 | Task { 41 | try await transform(element) 42 | } 43 | } 44 | 45 | return try await tasks.asyncMap { task in 46 | try await task.value 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/UICollectionViewLayout+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewLayout+.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/19. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension NSCollectionLayoutSection { 12 | static func makeCarouselSection(groupWidthDimension: CGFloat) -> NSCollectionLayoutSection { 13 | let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), 14 | heightDimension: .fractionalHeight(1.0)) 15 | let item = NSCollectionLayoutItem(layoutSize: itemSize) 16 | let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(groupWidthDimension), 17 | heightDimension: .fractionalHeight(1.0)) 18 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) 19 | return NSCollectionLayoutSection(group: group) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/UIView+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/15/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | static var identifier: String { 14 | return String(describing: self) 15 | } 16 | 17 | func addSubviews(_ views: UIView...) { 18 | views.forEach { addSubview($0) } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/UIViewController+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/26. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | var screenSize: CGRect { 13 | guard let window = UIApplication.shared.connectedScenes.first as? UIWindowScene else { 14 | return view.window?.windowScene?.screen.bounds ?? CGRect(x: 0, y: 0, width: 375, height: 667) 15 | } 16 | return window.screen.bounds 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/URL+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL+.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/14/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URL { 12 | func changeScheme(to: String) -> URL { 13 | var components = URLComponents(url: self, resolvingAgainstBaseURL: false) 14 | components?.scheme = to 15 | return components?.url ?? self 16 | } 17 | 18 | var customHLSURL: URL { 19 | changeScheme(to: "lhls") 20 | } 21 | 22 | var originHLSURL: URL { 23 | changeScheme(to: "https") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Extensions/URLSession+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSession+.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/24. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension URLSession { 12 | static func initMockSession(configuration: URLSessionConfiguration = .ephemeral) -> URLSession { 13 | let configuration = URLSessionConfiguration.default 14 | configuration.protocolClasses = [MockURLProtocol.self] 15 | let urlSession = URLSession.init(configuration: configuration) 16 | return urlSession 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BASE_URL 6 | $(BASE_URL) 7 | CFBundleURLTypes 8 | 9 | 10 | CFBundleTypeRole 11 | Editor 12 | CFBundleURLSchemes 13 | 14 | kakao$(KAKAO_APP_KEY) 15 | 16 | 17 | 18 | KAKAO_APP_KEY 19 | $(KAKAO_APP_KEY) 20 | LSApplicationQueriesSchemes 21 | 22 | kakaoplus 23 | kakaokompassauth 24 | kakaolink 25 | 26 | UIAppFonts 27 | 28 | Dashboard-Regular.ttf 29 | Pretendard-Bold.ttf 30 | Pretendard-Regular.ttf 31 | Pretendard-SemiBold.ttf 32 | 33 | UIApplicationSceneManifest 34 | 35 | UIApplicationSupportsMultipleScenes 36 | 37 | UISceneConfigurations 38 | 39 | UIWindowSceneSessionRoleApplication 40 | 41 | 42 | UISceneConfigurationName 43 | Default Configuration 44 | UISceneDelegateClassName 45 | $(PRODUCT_MODULE_NAME).SceneDelegate 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "LOImageCacher", 8 | platforms: [ 9 | .iOS(.v13) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, making them visible to other packages. 13 | .library( 14 | name: "LOImageCacher", 15 | targets: ["LOImageCacher"]) 16 | ], 17 | targets: [ 18 | // Targets are the basic building blocks of a package, defining a module or a test suite. 19 | // Targets can depend on other targets in this package and products from dependencies. 20 | .target( 21 | name: "LOImageCacher"), 22 | .testTarget( 23 | name: "LOImageCacherTests", 24 | dependencies: ["LOImageCacher"]) 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/Sources/LOImageCacher/ImageView + LOImageCacherWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageView + LOImageCacherWrapper.swift 3 | // 4 | // 5 | // Created by kong on 2024/01/17. 6 | // 7 | 8 | import UIKit 9 | 10 | // MARK: - Extensions 11 | 12 | extension UIImageView: LOImageCacherCompatible { } 13 | 14 | extension LOImageCacherWrapper where Base: UIImageView { 15 | 16 | public func setImage(with url: URL) { 17 | Task { 18 | if let data = await LOImageFetcher.shared.fetchImage(from: url) { 19 | await updateImage(data) 20 | } 21 | } 22 | } 23 | 24 | @MainActor 25 | private func updateImage(_ data: Data) { 26 | self.base.image = UIImage(data: data) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/Sources/LOImageCacher/LOImageCacher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LOImageCacher.swift 3 | // 4 | // 5 | // Created by kong on 2024/01/17. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct LOImageCacherWrapper { 11 | 12 | // MARK: - Properties 13 | 14 | public let base: Base 15 | 16 | // MARK: - Initializers 17 | 18 | public init(base: Base) { 19 | self.base = base 20 | } 21 | } 22 | 23 | public protocol LOImageCacherCompatible: AnyObject { } 24 | 25 | extension LOImageCacherCompatible { 26 | 27 | // swiftlint:disable identifier_name 28 | public var lo: LOImageCacherWrapper { 29 | LOImageCacherWrapper(base: self) 30 | } 31 | // swiftlint:enable identifier_name 32 | 33 | } 34 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/Sources/LOImageCacher/LOImageFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LOImageFetcher.swift 3 | // 4 | // 5 | // Created by kong on 2024/01/17. 6 | // 7 | 8 | import UIKit 9 | 10 | final class LOImageFetcher { 11 | 12 | typealias ImageCacheStorage = Storage 13 | 14 | // MARK: - Properties 15 | 16 | static let shared = LOImageFetcher() 17 | private let storage: any ImageCacheStorage 18 | private let session: URLSession 19 | 20 | // MARK: - Initializer 21 | 22 | private init(storage: any ImageCacheStorage = LOCacheStorage(), 23 | session: URLSession = .shared) { 24 | self.storage = storage 25 | self.session = session 26 | } 27 | 28 | // MARK: - Methods 29 | 30 | func fetchImage(from url: URL) async -> Data? { 31 | let key = url.lastPathComponent 32 | guard let storageData = storage.retrieve(forKey: key) else { 33 | if let (fetchedData, _) = try? await session.data(from: url) { 34 | storage.store(fetchedData, forKey: key) 35 | storage.storeToDisk(fetchedData, forKey: key) 36 | return fetchedData 37 | } else { 38 | return nil 39 | } 40 | } 41 | return storageData 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/LOImageCacher/Tests/LOImageCacherTests/LOImageCacherTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import LOImageCacher 3 | 4 | final class LOImageCacherTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Layover.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.applesignin 6 | 7 | Default 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Models/Board.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Board.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/30/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Board { 12 | let identifier: Int 13 | let title: String 14 | let description: String? 15 | let thumbnailImageURL: URL? 16 | let videoURL: URL? 17 | let latitude: Double 18 | let longitude: Double 19 | let status: BoardStatus 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Models/Member.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Member.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/30/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Member { 12 | let identifier: Int 13 | let username: String 14 | let introduce: String? 15 | let profileImageURL: URL? 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Models/Post.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Post.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/29/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Post { 12 | let member: Member 13 | let board: Board 14 | let tag: [String] 15 | var thumbnailImageData: Data? 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Models/PostsPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostsPage.swift 3 | // Layover 4 | // 5 | // Created by kong on 2024/02/07. 6 | // Copyright © 2024 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct PostsPage { 12 | let cursor: Int 13 | let posts: [Post] 14 | } 15 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Models/UploadPost.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadPost.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/05. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UploadPost { 12 | let title: String 13 | let content: String? 14 | let latitude, longitude: Double 15 | let tag: [String] 16 | let videoURL: URL 17 | } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Common/NetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkError.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/13. 6 | // 7 | 8 | import Foundation 9 | 10 | enum NetworkError: LocalizedError { 11 | case unknown 12 | case components 13 | case urlRequest 14 | case server(ServerError) 15 | case emptyData 16 | case parsing 17 | case decoding(Error) 18 | 19 | var errorDescription: String? { 20 | switch self { 21 | case .unknown: "Unknown Error" 22 | case .components: "URL Components Error" 23 | case .urlRequest: "URL Request Error" 24 | case .server(let serverError): "Server Error: \(serverError)" 25 | case .emptyData: "Empty Data Error" 26 | case .parsing: "Error While Parsing" 27 | case .decoding(let error): "Error While Decoding: \(error)" 28 | } 29 | } 30 | } 31 | 32 | enum ServerError: Int { 33 | case unknown 34 | case badRequest = 400 35 | case unauthorized = 401 36 | case forbidden = 403 37 | case notFound = 404 38 | } 39 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/CheckSignUpDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckSignUpDTO.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/6/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CheckSignUpDTO: Decodable { 12 | let isAlreadyExist: Bool 13 | } 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/CheckUserNameDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckUserNameDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/24. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CheckUserNameDTO: Decodable { 12 | let isValid: Bool 13 | } 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/IntroduceDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntroduceDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/25. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct IntroduceDTO: Codable { 12 | let introduce: String 13 | } 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/LoginDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginDTO.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/23/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct LoginDTO: Decodable { 12 | let accessToken: String 13 | let refreshToken: String 14 | } 15 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/MemberDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberDTO.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/30/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct MemberDTO: Decodable { 12 | let id: Int 13 | let username, introduce: String 14 | let profileImageURL: String? 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case id, username, introduce 18 | case profileImageURL = "profile_image_url" 19 | } 20 | } 21 | 22 | extension MemberDTO { 23 | func toDomain() -> Member { 24 | var imageURL: URL? 25 | if let profileImageURL { 26 | imageURL = URL(string: profileImageURL) 27 | } 28 | 29 | return Member( 30 | identifier: id, 31 | username: username, 32 | introduce: introduce, 33 | profileImageURL: imageURL 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/NicknameDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NicknameDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/25. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct NicknameDTO: Codable { 12 | let username: String 13 | } 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/PostDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostDTO.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/30/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct PostDTO: Decodable { 12 | let member: MemberDTO 13 | let board: BoardDTO 14 | let tag: [String] 15 | } 16 | 17 | extension PostDTO { 18 | func toDomain() -> Post { 19 | return Post( 20 | member: member.toDomain(), 21 | board: board.toDomain(), 22 | tag: tag 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/PostsPageDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostsPageDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2024/02/04. 6 | // Copyright © 2024 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct PostsPageDTO: Decodable { 12 | let lastID: Int 13 | let posts: [PostDTO] 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case lastID = "lastId" 17 | case posts = "boardsResDto" 18 | } 19 | } 20 | 21 | struct PostRequestDTO: Encodable { 22 | let cursor: Int? 23 | let memberId: String? 24 | } 25 | 26 | extension PostsPageDTO { 27 | func toDomain() -> PostsPage { 28 | return PostsPage( 29 | cursor: lastID, 30 | posts: posts.map { $0.toDomain() } 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/PresignedURLDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresignedURLDTO.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/7/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct PresignedURLDTO: Decodable { 12 | let preSignedURL: String 13 | 14 | enum CodingKeys: String, CodingKey { 15 | case preSignedURL = "preSignedUrl" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/ProfileImageDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileImageDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/25. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ProfileImageDTO: Decodable { 12 | let profileImage: URL 13 | 14 | enum CodingKeys: String, CodingKey { 15 | case profileImage = "profile-Image" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/ReportDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportDTO.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/5/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ReportDTO: Codable { 12 | let memberID: Int? 13 | let boardID: Int 14 | let reportType: String 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case memberID = "memberId" 18 | case boardID = "boardId" 19 | case reportType 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/Response.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Response.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/23/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Response: Decodable { 12 | let statusCode: Int 13 | let message: String 14 | let data: Data? 15 | } 16 | 17 | struct EmptyData: Decodable { } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/UploadPostDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadPostDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/05. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UploadPostDTO: Decodable { 12 | let id: Int 13 | let title: String 14 | let content: String? 15 | let latitude, longitude: Double 16 | let tag: [String] 17 | } 18 | 19 | struct UploadPostRequestDTO: Encodable { 20 | let title: String 21 | let content: String? 22 | let latitude, longitude: Double 23 | let tag: [String] 24 | } 25 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/DTOs/UploadVideoDTO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadVideoDTO.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/05. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UploadVideoRequestDTO: Encodable { 12 | let boardID: Int 13 | let filetype: String 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case boardID = "boardId" 17 | case filetype 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/EndPoint/EndPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EndPoint.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/13. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RequestResponsable: Requestable, Responsable { } 11 | 12 | final class EndPoint: RequestResponsable { 13 | typealias Response = R 14 | 15 | var baseURL: String 16 | var path: String 17 | var method: HTTPMethod 18 | var queryParameters: Encodable? 19 | var bodyParameters: Encodable? 20 | var headers: [String: String]? 21 | 22 | init(baseURL: String = Bundle.main.infoDictionary?["BASE_URL"] as? String ?? "", 23 | path: String, 24 | method: HTTPMethod, 25 | queryParameters: Encodable? = nil, 26 | bodyParameters: Encodable? = nil, 27 | headers: [String: String]? = ["Content-Type": "application/json"]) { 28 | self.baseURL = baseURL 29 | self.path = path 30 | self.method = method 31 | self.queryParameters = queryParameters 32 | self.bodyParameters = bodyParameters 33 | self.headers = headers 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/EndPoint/Factories/DefaultPostManagerEndPointFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostManagerEndPointFactory.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/5/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol PostManagerEndPointFactory { 12 | func reportPlaybackVideoEndpoint(boardID: Int, reportType: String) -> EndPoint> 13 | func deletePlaybackVideoEndpoint(boardID: Int) -> EndPoint> 14 | } 15 | 16 | struct DefaultPostManagerEndPointFactory: PostManagerEndPointFactory { 17 | func reportPlaybackVideoEndpoint(boardID: Int, reportType: String) -> EndPoint> { 18 | let bodyParmeters: ReportDTO = ReportDTO( 19 | memberID: nil, 20 | boardID: boardID, 21 | reportType: reportType) 22 | 23 | return EndPoint( 24 | path: "/report", 25 | method: .POST, 26 | bodyParameters: bodyParmeters) 27 | } 28 | 29 | func deletePlaybackVideoEndpoint(boardID: Int) -> EndPoint> { 30 | let queryParameters = ["boardId": boardID] 31 | return EndPoint( 32 | path: "/board", 33 | method: .DELETE, 34 | queryParameters: queryParameters 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/EndPoint/Factories/SignUpEndPointFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpEndPointsFactory.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/27/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol SignUpEndPointFactory { 12 | func makeKakaoSignUpEndPoint(socialToken: String, username: String) -> EndPoint> 13 | func makeAppleSignUpEndPoint(identityToken: String, username: String) -> EndPoint> 14 | } 15 | 16 | final class DefaultSignUpEndPointFactory: SignUpEndPointFactory { 17 | func makeKakaoSignUpEndPoint(socialToken: String, username: String) -> EndPoint> { 18 | var bodyParameters = [String: String]() 19 | bodyParameters.updateValue(socialToken, forKey: "accessToken") 20 | bodyParameters.updateValue(username, forKey: "username") 21 | 22 | return EndPoint( 23 | path: "/oauth/signup/kakao", 24 | method: .POST, 25 | bodyParameters: bodyParameters 26 | ) 27 | } 28 | 29 | func makeAppleSignUpEndPoint(identityToken: String, username: String) -> EndPoint> { 30 | var bodyParameters: [String: String] = [:] 31 | bodyParameters.updateValue(identityToken, forKey: "identityToken") 32 | bodyParameters.updateValue(username, forKey: "username") 33 | 34 | return EndPoint( 35 | path: "/oauth/signup/apple", 36 | method: .POST, 37 | bodyParameters: bodyParameters) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/EndPoint/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTTPMethod.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/13. 6 | // 7 | 8 | import Foundation 9 | 10 | enum HTTPMethod: String { 11 | case GET 12 | case POST 13 | case PUT 14 | case PATCH 15 | case DELETE 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/EndPoint/Responsable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Responsable.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/13. 6 | // 7 | 8 | protocol Responsable { 9 | associatedtype Response 10 | } 11 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/CheckSignUp.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "isAlreadyExist": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/CheckUserName.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "isValid": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/DeleteUser.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "username": "hwani" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/DeleteVideo.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/GetMember.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "id": 221, 6 | "username": "hwani", 7 | "introduce": "Hi, my name is hwani", 8 | "profile_image_url": "https://qi-o.qoo10cdn.com/goods_image_big/3/3/1/6/8645113316_l.jpg" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/LoginData.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "accessToken": "mockAccessToken", 6 | "refreshToken": "mockRefreshToken" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/PatchIntroduce.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "introduce": "야자타임을 좋아하고 더운걸 싫어하는 화니라고해요" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/PatchProfileImage.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "profile-image": "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/PatchUserName.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "username": "hwani" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/PostListEnd.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": [ 5 | 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockData/ReportPlaybackVideo.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "memberId": 221, 6 | "boardId": 5, 7 | "reportType": "청소년에게 유해한 내용이에요" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Network/Mock/MockURLProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockURLProtocol.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/24. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class MockURLProtocol: URLProtocol { 12 | static var requestHandler: ((URLRequest) -> (HTTPURLResponse?, Data?, Error?))? 13 | 14 | override class func canInit(with request: URLRequest) -> Bool { 15 | return true 16 | } 17 | 18 | override class func canonicalRequest(for request: URLRequest) -> URLRequest { 19 | return request 20 | } 21 | 22 | override func startLoading() { 23 | guard let handler = MockURLProtocol.requestHandler else { return } 24 | let (response, data, error) = handler(request) 25 | 26 | if let error = error { 27 | client?.urlProtocol(self, didFailWithError: error) 28 | } 29 | 30 | if let response = response { 31 | client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) 32 | } 33 | 34 | if let data = data { 35 | client?.urlProtocol(self, didLoad: data) 36 | } 37 | 38 | client?.urlProtocolDidFinishLoading(self) 39 | } 40 | 41 | override func stopLoading() { } 42 | } 43 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LayoverIcon.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/AppIcon.appiconset/LayoverIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/AppIcon.appiconset/LayoverIcon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x06", 9 | "green" : "0x06", 10 | "red" : "0x06" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Correct.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0x84", 10 | "red" : "0x0A" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/DarkGrey.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x13", 9 | "green" : "0x13", 10 | "red" : "0x13" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Error.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x41", 9 | "green" : "0x41", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Grey100.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xF6", 9 | "green" : "0xF6", 10 | "red" : "0xF6" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Grey200.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xE7", 9 | "green" : "0xE7", 10 | "red" : "0xE7" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Grey300.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xC4", 9 | "green" : "0xC4", 10 | "red" : "0xC4" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Grey400.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xA3", 9 | "green" : "0xA3", 10 | "red" : "0xA3" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Grey500.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x4B", 9 | "green" : "0x4B", 10 | "red" : "0x4B" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/Kakao.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.231", 9 | "green" : "0.922", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/LayoverWhite.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "1.000", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Colors/PrimaryPurple.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xEE", 9 | "green" : "0x4F", 10 | "red" : "0x81" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "normalImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "normalImage 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "normalImage 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LONormalThumb.imageset/normalImage.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "selectedImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "selectedImage 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "selectedImage 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LOSelectedThumb.imageset/selectedImage.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LocationPin.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LocationPin@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LocationPin@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/LocationPin.imageset/LocationPin@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Setting.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Setting@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Setting@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Setting.imageset/Setting@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Title.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Title@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Title@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/Title.imageset/Title@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "appleLogo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "appleLogo 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "appleLogo 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/appleLogo.imageset/appleLogo.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Close Button@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Close Button.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Close Button@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Close Button@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Document.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "Document@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "Document@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/content.imageset/Document@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "leading-icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "leading-icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "leading-icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/down.imageset/leading-icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_tab_back.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "icon_tab_back@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "icon_tab_back@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/icon_tab_back.imageset/icon_tab_back@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "kakaoLogo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "kakaoLogo 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "kakaoLogo 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/kakaoLogo.imageset/kakaoLogo.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LayoverLogo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LayoverLogo 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LayoverLogo 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/loLogo.imageset/LayoverLogo.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "map.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "map@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "map@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/map.imageset/map@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "map_pin_highlighted.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "map_pin_highlighted@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "map_pin_highlighted@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mapPin.imageset/map_pin_highlighted@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "mute_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "mute_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "mute_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/mute.imageset/mute_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "location_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "location_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "location_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/myLocation.imageset/location_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pause.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "pause 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "pause 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/pause.imageset/pause.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "photo_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "photo_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "photo_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/photo.imageset/photo_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "planet.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "planet@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "planet@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/planet.imageset/planet@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "play.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "play 1.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "play 2.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play 1.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play 2.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/play.imageset/play.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "plus_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "plus_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "plus_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/plus.imageset/plus_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "profile.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "profile@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "profile@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/profile.imageset/profile@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "interface-arrows-round-right--diagram-round-arrow-right.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "interface-arrows-round-right--diagram-round-arrow-right@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "interface-arrows-round-right--diagram-round-arrow-right@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/retry.imageset/interface-arrows-round-right--diagram-round-arrow-right@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "scissors_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "scissors_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "scissors_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/scissors.imageset/scissors_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "smallPlus.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "smallPlus@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "smallPlus@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/smallPlus.imageset/smallPlus@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/3@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "3.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "3@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "3@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "box.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "box@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "box@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/tag.imageset/box@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "volume_icon.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "volume_icon@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "volume_icon@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "original" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon@2x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Assets.xcassets/volume.imageset/volume_icon@3x.png -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Fonts/Dashboard-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Fonts/Dashboard-Regular.ttf -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Fonts/Pretendard-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Fonts/Pretendard-Bold.ttf -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Fonts/Pretendard-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Fonts/Pretendard-Regular.ttf -------------------------------------------------------------------------------- /iOS/Layover/Layover/Resources/Fonts/Pretendard-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Resources/Fonts/Pretendard-SemiBold.ttf -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Configurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Configurator.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/14/23. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol Configurator { 11 | associatedtype ViewController: UIViewController 12 | func configure(_ viewController: ViewController) 13 | } 14 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditProfile/EditProfileConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditProfileConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/27. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class EditProfileConfigurator: Configurator { 12 | 13 | typealias ViewController = EditProfileViewController 14 | 15 | static let shared = EditProfileConfigurator() 16 | 17 | private init() { } 18 | 19 | func configure(_ viewController: ViewController) { 20 | let viewController = viewController 21 | let interactor = EditProfileInteractor() 22 | let presenter = EditProfilePresenter() 23 | let worker = UserWorker() 24 | let router = EditProfileRouter() 25 | 26 | router.viewController = viewController 27 | router.dataStore = interactor 28 | viewController.interactor = interactor 29 | viewController.router = router 30 | interactor.presenter = presenter 31 | interactor.userWorker = worker 32 | presenter.viewController = viewController 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditProfile/EditProfileRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditProfileRouter.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/27. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EditProfileRoutingLogic { 12 | 13 | } 14 | 15 | protocol EditProfileDataPassing { 16 | var dataStore: EditProfileDataStore? { get } 17 | } 18 | 19 | final class EditProfileRouter: EditProfileRoutingLogic, EditProfileDataPassing { 20 | 21 | // MARK: - Properties 22 | 23 | weak var viewController: EditProfileViewController? 24 | var dataStore: EditProfileDataStore? 25 | 26 | // MARK: - Routing 27 | 28 | } 29 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditTag/EditTagConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditTagConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/03. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class EditTagConfigurator: Configurator { 12 | 13 | typealias ViewController = EditTagViewController 14 | 15 | static let shared = EditTagConfigurator() 16 | 17 | private init() { } 18 | 19 | func configure(_ viewController: ViewController) { 20 | let viewController = viewController 21 | let interactor = EditTagInteractor() 22 | let presenter = EditTagPresenter() 23 | let router = EditTagRouter() 24 | 25 | router.viewController = viewController 26 | router.dataStore = interactor 27 | viewController.interactor = interactor 28 | viewController.router = router 29 | interactor.presenter = presenter 30 | presenter.viewController = viewController 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditTag/EditTagModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditTagModels.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/03. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum EditTagModels { 12 | 13 | static let maxTagCount: Int = 3 14 | static let maxTagLength: Int = 5 15 | 16 | enum FetchTags { 17 | struct Request { 18 | 19 | } 20 | struct Response { 21 | let tags: [String] 22 | } 23 | struct ViewModel { 24 | let tags: [String] 25 | var tagCountDescription: String 26 | } 27 | } 28 | 29 | enum AddTag { 30 | struct Request { 31 | let tags: [String] 32 | let newTag: String 33 | } 34 | struct Response { 35 | let tags: [String] 36 | let addedTag: String? 37 | } 38 | struct ViewModel { 39 | let addedTag: String? 40 | let tagCountDescription: String 41 | } 42 | } 43 | 44 | enum EditTags { 45 | struct Request { 46 | let editedTags: [String] 47 | } 48 | struct Response { 49 | let editedTags: [String] 50 | } 51 | struct ViewModel { 52 | let tagCountDescription: String 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditVideo/EditVideoConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditVideoConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/29. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class EditVideoConfigurator: Configurator { 12 | 13 | typealias ViewController = EditVideoViewController 14 | 15 | static let shared = EditVideoConfigurator() 16 | 17 | private init() { } 18 | 19 | func configure(_ viewController: ViewController) { 20 | let viewController = viewController 21 | let videoFileWorker = VideoFileWorker() 22 | let interactor = EditVideoInteractor() 23 | let presenter = EditVideoPresenter() 24 | let router = EditVideoRouter() 25 | 26 | router.viewController = viewController 27 | router.dataStore = interactor 28 | viewController.interactor = interactor 29 | viewController.router = router 30 | interactor.presenter = presenter 31 | interactor.videoFileWorker = videoFileWorker 32 | presenter.viewController = viewController 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditVideo/EditVideoModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditVideoModels.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/29. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum EditVideoModels { 12 | 13 | enum FetchVideo { 14 | struct Request { 15 | var editedVideoURL: URL? 16 | var videoRange: ClosedRange = 3.0...60.0 17 | } 18 | struct Response { 19 | let isEdited: Bool 20 | let videoURL: URL 21 | let duration: Double 22 | var isWithinRange: Bool 23 | } 24 | struct ViewModel { 25 | let isEdited: Bool 26 | let videoURL: URL 27 | let duration: Double 28 | let canNext: Bool 29 | } 30 | } 31 | 32 | enum DidFinishViedoEditing { 33 | struct Request { 34 | let isMuted: Bool 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditVideo/EditVideoPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditVideoPresenter.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/29. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EditVideoPresentationLogic { 12 | func presentVideo(with response: EditVideoModels.FetchVideo.Response) 13 | } 14 | 15 | final class EditVideoPresenter: EditVideoPresentationLogic { 16 | 17 | // MARK: - Properties 18 | 19 | typealias Models = EditVideoModels 20 | weak var viewController: EditVideoDisplayLogic? 21 | 22 | func presentVideo(with response: EditVideoModels.FetchVideo.Response) { 23 | let viewModel = Models.FetchVideo.ViewModel(isEdited: response.isEdited, 24 | videoURL: response.videoURL, 25 | duration: response.duration, 26 | canNext: response.isWithinRange) 27 | viewController?.displayVideo(viewModel: viewModel) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditVideo/EditVideoRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditVideoRouter.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/29. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol EditVideoRoutingLogic { 12 | func routeToNext() 13 | } 14 | 15 | protocol EditVideoDataPassing { 16 | var dataStore: EditVideoDataStore? { get } 17 | } 18 | 19 | final class EditVideoRouter: NSObject, EditVideoRoutingLogic, EditVideoDataPassing { 20 | 21 | // MARK: - Properties 22 | 23 | weak var viewController: EditVideoViewController? 24 | var dataStore: EditVideoDataStore? 25 | 26 | // MARK: - Routing 27 | 28 | func routeToNext() { 29 | let nextViewController = UploadPostViewController() 30 | guard let source = dataStore, 31 | var destination = nextViewController.router?.dataStore 32 | else { return } 33 | 34 | // Data Passing 35 | destination.videoURL = source.videoURL 36 | destination.isMuted = source.isMuted 37 | nextViewController.hidesBottomBarWhenPushed = true 38 | viewController?.navigationController?.pushViewController(nextViewController, animated: true) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/EditVideo/EditVideoWorker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditVideoWorker.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/29. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class EditVideoWorker { 12 | 13 | // MARK: - Properties 14 | 15 | typealias Models = EditVideoModels 16 | 17 | } 18 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Home/HomeConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/16/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class HomeConfigurator: Configurator { 12 | 13 | static let shared = HomeConfigurator() 14 | 15 | private init() { } 16 | 17 | func configure(_ viewController: HomeViewController) { 18 | let router = HomeRouter() 19 | let presenter = HomePresenter() 20 | let interactor = HomeInteractor() 21 | let homeWorker = HomeWorker() 22 | let videoFileWorker = VideoFileWorker() 23 | let locationManager = CurrentLocationManager() 24 | 25 | router.viewController = viewController 26 | router.dataStore = interactor 27 | presenter.viewController = viewController 28 | interactor.presenter = presenter 29 | interactor.homeWorker = homeWorker 30 | interactor.videoFileWorker = videoFileWorker 31 | interactor.locationManager = locationManager 32 | viewController.router = router 33 | viewController.interactor = interactor 34 | 35 | router.dataStore = interactor 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Login/LoginConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 11/20/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class LoginConfigurator: Configurator { 12 | typealias ViewController = LoginViewController 13 | 14 | static let shared = LoginConfigurator() 15 | 16 | private init() { } 17 | 18 | func configure(_ viewController: LoginViewController) { 19 | let viewController = viewController 20 | let interactor = LoginInteractor() 21 | let presenter = LoginPresenter() 22 | // TODO: 실행 전 Worker Mock인지 확인 23 | let worker = LoginWorker() 24 | let router = LoginRouter() 25 | 26 | router.viewController = viewController 27 | router.dataStore = interactor 28 | viewController.interactor = interactor 29 | viewController.router = router 30 | interactor.presenter = presenter 31 | interactor.worker = worker 32 | presenter.viewController = viewController 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Login/LoginModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginModels.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/14/23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum LoginModels { 11 | 12 | // MARK: - Use Cases 13 | 14 | enum PerformKakaoLogin { 15 | struct Request { 16 | } 17 | 18 | struct Response { 19 | 20 | } 21 | 22 | struct ViewModel { 23 | 24 | } 25 | } 26 | 27 | enum PerformAppleLogin { 28 | struct Request { 29 | let loginViewController: LoginViewController 30 | } 31 | 32 | struct Response { 33 | 34 | } 35 | 36 | struct ViewModel { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Login/LoginPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginPresenter.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/14/23. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol LoginPresentationLogic { 11 | func presentPerformLogin() 12 | func presentSignUp(with response: LoginModels.PerformKakaoLogin.Response) 13 | func presentSignUp(with response: LoginModels.PerformAppleLogin.Response) 14 | } 15 | 16 | final class LoginPresenter { 17 | 18 | // MARK: - Properties 19 | 20 | typealias Models = LoginModels 21 | weak var viewController: LoginDisplayLogic? 22 | 23 | } 24 | 25 | // MARK: - Use Case - Login 26 | 27 | extension LoginPresenter: LoginPresentationLogic { 28 | func presentPerformLogin() { 29 | viewController?.navigateToMain() 30 | } 31 | 32 | func presentSignUp(with response: LoginModels.PerformKakaoLogin.Response) { 33 | viewController?.routeToSignUp(with: LoginModels.PerformKakaoLogin.ViewModel()) 34 | } 35 | 36 | func presentSignUp(with response: LoginModels.PerformAppleLogin.Response) { 37 | viewController?.routeToSignUp(with: LoginModels.PerformAppleLogin.ViewModel()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Map/LOAnnotation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LOAnnotation.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/26. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import MapKit 10 | 11 | final class LOAnnotation: NSObject, MKAnnotation { 12 | 13 | @objc dynamic var coordinate: CLLocationCoordinate2D 14 | let boardID: Int 15 | let thumbnailImageURL: URL? 16 | 17 | init(coordinate: CLLocationCoordinate2D, 18 | boardID: Int, 19 | thumbnailImageURL: URL?) { 20 | self.coordinate = coordinate 21 | self.boardID = boardID 22 | self.thumbnailImageURL = thumbnailImageURL 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Map/MapConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/15. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class MapConfigurator: Configurator { 12 | typealias ViewController = MapViewController 13 | 14 | static let shared = MapConfigurator() 15 | 16 | private init() { } 17 | 18 | func configure(_ viewController: ViewController) { 19 | let viewController = viewController 20 | let interactor = MapInteractor() 21 | let presenter = MapPresenter() 22 | let router = MapRouter() 23 | let worker = MapWorker() 24 | let videoFileWorker = VideoFileWorker() 25 | let locationManager = CurrentLocationManager() 26 | 27 | router.viewController = viewController 28 | viewController.interactor = interactor 29 | interactor.presenter = presenter 30 | interactor.worker = worker 31 | interactor.videoFileWorker = videoFileWorker 32 | interactor.locationManager = locationManager 33 | presenter.viewController = viewController 34 | viewController.router = router 35 | router.dataStore = interactor 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Playback/PlaybackConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaybackConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 11/28/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class PlaybackConfigurator: Configurator { 12 | typealias ViewController = PlaybackViewController 13 | 14 | static let shared = PlaybackConfigurator() 15 | 16 | private init() { } 17 | 18 | func configure(_ viewController: PlaybackViewController) { 19 | let viewController: PlaybackViewController = viewController 20 | let interactor: PlaybackInteractor = PlaybackInteractor() 21 | let presenter: PlaybackPresenter = PlaybackPresenter() 22 | let worker: PlaybackWorkerProtocol = PlaybackWorker() 23 | let router: PlaybackRouter = PlaybackRouter() 24 | 25 | router.viewController = viewController 26 | router.dataStore = interactor 27 | viewController.interactor = interactor 28 | viewController.router = router 29 | interactor.presenter = presenter 30 | interactor.worker = worker 31 | presenter.viewController = viewController 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Profile/ProfileConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/21. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class ProfileConfigurator: Configurator { 12 | 13 | typealias ViewController = ProfileViewController 14 | 15 | static let shared = ProfileConfigurator() 16 | 17 | private init() { } 18 | 19 | func configure(_ viewController: ViewController) { 20 | let viewController = viewController 21 | let interactor = ProfileInteractor() 22 | let worker = UserWorker() 23 | let presenter = ProfilePresenter() 24 | let router = ProfileRouter() 25 | 26 | router.viewController = viewController 27 | router.dataStore = interactor 28 | viewController.interactor = interactor 29 | viewController.router = router 30 | interactor.presenter = presenter 31 | interactor.userWorker = worker 32 | presenter.viewController = viewController 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Profile/ProfileModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileModels.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/21. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum ProfileModels { 12 | 13 | struct Profile: Hashable { 14 | let username: String 15 | let introduce: String? 16 | let profileImageURL: URL? 17 | } 18 | 19 | struct DisplayedPost: Hashable { 20 | let id: Int 21 | let thumbnailImageData: URL? 22 | let status: BoardStatus 23 | } 24 | 25 | enum FetchProfile { 26 | 27 | struct Request { 28 | } 29 | 30 | struct Response { 31 | let userProfile: Profile 32 | let displayedPosts: [DisplayedPost] 33 | } 34 | 35 | struct ViewModel { 36 | let userProfile: Profile 37 | let displayedPosts: [DisplayedPost] 38 | } 39 | } 40 | 41 | enum FetchMorePosts { 42 | struct Request { 43 | } 44 | 45 | struct Response { 46 | let displayedPosts: [DisplayedPost] 47 | } 48 | 49 | struct ViewModel { 50 | let displayedPosts: [DisplayedPost] 51 | } 52 | } 53 | 54 | enum ShowPostDetail { 55 | struct Request { 56 | let startIndex: Int 57 | } 58 | 59 | struct Response { 60 | } 61 | 62 | struct ViewModel { 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Report/ReportConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/4/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class ReportConfigurator: Configurator { 12 | static let shared = ReportConfigurator() 13 | 14 | private init() { } 15 | 16 | func configure(_ viewController: ReportViewController) { 17 | let viewController: ReportViewController = viewController 18 | let interactor: ReportInteractor = ReportInteractor() 19 | let presenter: ReportPresenter = ReportPresenter() 20 | let worker: ReportWorker = ReportWorker() 21 | // let worker: ReportWorkerProtocol = MockReportWorker() 22 | let router: ReportRouter = ReportRouter() 23 | 24 | router.viewController = viewController 25 | router.dataStore = interactor 26 | viewController.interactor = interactor 27 | viewController.router = router 28 | interactor.presenter = presenter 29 | interactor.worker = worker 30 | presenter.viewController = viewController 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Report/ReportInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportInteractor.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/4/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ReportBusinessLogic { 12 | @discardableResult 13 | func reportPlaybackVideo(with request: ReportModels.ReportPlaybackVideo.Request) -> Task 14 | } 15 | 16 | protocol ReportDataStore { 17 | var boardID: Int? { get set } 18 | } 19 | 20 | final class ReportInteractor: ReportBusinessLogic, ReportDataStore { 21 | 22 | // MARK: - Properties 23 | 24 | typealias Models = ReportModels 25 | 26 | var worker: ReportWorkerProtocol? 27 | var presenter: ReportPresentationLogic? 28 | 29 | var boardID: Int? 30 | 31 | func reportPlaybackVideo(with request: ReportModels.ReportPlaybackVideo.Request) -> Task { 32 | Task { 33 | guard let boardID, 34 | let worker 35 | else { return false } 36 | let result: Bool = await worker.reportPlaybackVideo(boardID: boardID, reportContent: request.reportContent) 37 | let response: Models.ReportPlaybackVideo.Response = Models.ReportPlaybackVideo.Response(reportResult: result) 38 | await MainActor.run { 39 | presenter?.presentReportPlaybackVideo(with: response) 40 | } 41 | return result 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Report/ReportModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportModels.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/4/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum ReportModels { 12 | 13 | // MARK: - Use Cases 14 | 15 | enum ReportPlaybackVideo { 16 | enum ReportMessage { 17 | case success 18 | case fail 19 | 20 | var description: String { 21 | switch self { 22 | case .success: 23 | "신고가 접수되었습니다.\n해당 컨텐츠는 검토 후, 24시간 내에 차단 처리됩니다." 24 | case .fail: 25 | "신고 접수에 실패했습니다. 다시 시도해주세요." 26 | } 27 | } 28 | } 29 | struct Request { 30 | let reportContent: String 31 | } 32 | 33 | struct Response { 34 | let reportResult: Bool 35 | } 36 | 37 | struct ViewModel { 38 | let reportMessage: ReportMessage 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Report/ReportPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportPresenter.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/4/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ReportPresentationLogic { 12 | func presentReportPlaybackVideo(with response: ReportModels.ReportPlaybackVideo.Response) 13 | } 14 | 15 | final class ReportPresenter: ReportPresentationLogic { 16 | 17 | // MARK: - Properties 18 | 19 | typealias Models = ReportModels 20 | weak var viewController: ReportDisplayLogic? 21 | 22 | func presentReportPlaybackVideo(with response: ReportModels.ReportPlaybackVideo.Response) { 23 | let viewModel: Models.ReportPlaybackVideo.ViewModel 24 | viewModel = Models.ReportPlaybackVideo.ViewModel(reportMessage: response.reportResult ? .success : .fail) 25 | viewController?.displayReportResult(viewModel: viewModel) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Report/ReportRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportRouter.swift 3 | // Layover 4 | // 5 | // Created by 황지웅 on 12/4/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ReportRoutingLogic { 12 | func routeToNext() 13 | } 14 | 15 | protocol ReportDataPassing { 16 | var dataStore: ReportDataStore? { get } 17 | } 18 | 19 | final class ReportRouter: NSObject, ReportRoutingLogic, ReportDataPassing { 20 | 21 | // MARK: - Properties 22 | 23 | weak var viewController: ReportViewController? 24 | var dataStore: ReportDataStore? 25 | 26 | // MARK: - Routing 27 | 28 | func routeToNext() { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Setting/SettingConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/6/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SettingConfigurator: Configurator { 12 | 13 | // MARK: - Properties 14 | 15 | static let shared = SettingConfigurator() 16 | 17 | // MARK: - Intializer 18 | 19 | private init() { } 20 | 21 | // MARK: - Methods 22 | 23 | func configure(_ viewController: SettingViewController) { 24 | let interactor = SettingInteractor() 25 | let presenter = SettingPresenter() 26 | let router = SettingRouter() 27 | let settingWorker = SettingWorker() 28 | let userWorker = UserWorker() 29 | 30 | viewController.router = router 31 | viewController.interactor = interactor 32 | interactor.settingWorker = settingWorker 33 | interactor.userWorker = userWorker 34 | interactor.presenter = presenter 35 | presenter.viewController = viewController 36 | router.viewController = viewController 37 | router.dataStore = interactor 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Setting/SettingRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingRouter.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/6/23. 6 | // Copyright (c) 2023 CodeBomber. All rights reserved. 7 | // 8 | // This file was generated by the Clean Swift Xcode Templates so 9 | // you can apply clean architecture to your iOS and Mac projects, 10 | // see http://clean-swift.com 11 | // 12 | 13 | import UIKit 14 | import SafariServices 15 | 16 | protocol SettingRoutingLogic { 17 | func showSafariViewController(url: URL) 18 | func routeToLogin() 19 | } 20 | 21 | protocol SettingDataPassing { 22 | var dataStore: SettingDataStore? { get } 23 | } 24 | 25 | final class SettingRouter: SettingRoutingLogic, SettingDataPassing { 26 | 27 | // MARK: - Properties 28 | 29 | weak var viewController: SettingViewController? 30 | var dataStore: SettingDataStore? 31 | 32 | // MARK: - Methods 33 | 34 | func showSafariViewController(url: URL) { 35 | let safariViewController = SFSafariViewController(url: url) 36 | viewController?.present(safariViewController, animated: true, completion: nil) 37 | } 38 | 39 | func routeToLogin() { 40 | let loginViewController = LoginViewController() 41 | guard let rootNavigationController = viewController?.view.window?.rootViewController 42 | as? UINavigationController else { return } 43 | rootNavigationController.setNavigationBarHidden(false, animated: true) 44 | rootNavigationController.setViewControllers([loginViewController], animated: true) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Setting/SettingWorker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingWorker.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/6/23. 6 | // Copyright (c) 2023 CodeBomber. All rights reserved. 7 | // 8 | // This file was generated by the Clean Swift Xcode Templates so 9 | // you can apply clean architecture to your iOS and Mac projects, 10 | // see http://clean-swift.com 11 | // 12 | 13 | import Foundation 14 | 15 | protocol SettingWorkerProtocol { 16 | func versionNumber() -> String? 17 | } 18 | 19 | final class SettingWorker: SettingWorkerProtocol { 20 | 21 | // MARK: - Methods 22 | 23 | func versionNumber() -> String? { 24 | System.versionNumber() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/SignUpScene/SignUpConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/15. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SignUpConfigurator: Configurator { 12 | static let shared = SignUpConfigurator() 13 | 14 | private init() { } 15 | 16 | func configure(_ viewController: SignUpViewController) { 17 | let viewController = viewController 18 | let interactor = SignUpInteractor() 19 | let userWorker = UserWorker() 20 | let signUpWorker = SignUpWorker() 21 | let presenter = SignUpPresenter() 22 | let router = SignUpRouter() 23 | viewController.interactor = interactor 24 | viewController.router = router 25 | interactor.presenter = presenter 26 | interactor.userWorker = userWorker 27 | interactor.signUpWorker = signUpWorker 28 | presenter.viewController = viewController 29 | router.viewController = viewController 30 | router.dataStore = interactor 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/SignUpScene/SignUpModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpModels.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/15. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum SignUpModels { 12 | 13 | // MARK: - Validate Nickname Use Case 14 | 15 | enum ValidateNickname { 16 | struct Request { 17 | var nickname: String 18 | } 19 | struct Response { 20 | var nicknameState: NicknameState 21 | } 22 | struct ViewModel { 23 | var canCheckDuplication: Bool 24 | var alertDescription: String? 25 | } 26 | } 27 | 28 | // MARK: - Check Nickname Duplication Use Case 29 | enum CheckDuplication { 30 | struct Request { 31 | var nickname: String 32 | } 33 | struct Response { 34 | var isValid: Bool 35 | } 36 | struct ViewModel { 37 | var canSignUp: Bool 38 | var alertDescription: String { 39 | canSignUp ? "사용 가능한 닉네임입니다." : "사용 중인 닉네임입니다." 40 | } 41 | } 42 | } 43 | 44 | // MARK: - Sign Up Use Case 45 | 46 | enum SignUp { 47 | enum LoginType { 48 | case kakao 49 | case apple 50 | } 51 | 52 | struct Request { 53 | var nickname: String 54 | } 55 | 56 | struct Response { 57 | var isSuccess: Bool 58 | } 59 | 60 | struct ViewModel { 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/SignUpScene/SignUpPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpPresenter.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/11/15. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol SignUpPresentationLogic { 12 | func presentNicknameValidation(with response: SignUpModels.ValidateNickname.Response) 13 | func presentNicknameDuplication(with response: SignUpModels.CheckDuplication.Response) 14 | func presentSignUpSuccess() 15 | } 16 | 17 | final class SignUpPresenter: SignUpPresentationLogic { 18 | 19 | // MARK: - Properties 20 | 21 | typealias Models = SignUpModels 22 | weak var viewController: SignUpDisplayLogic? 23 | 24 | // MARK: - UseCase: 닉네임 유효성 검사 25 | 26 | func presentNicknameValidation(with response: Models.ValidateNickname.Response) { 27 | let viewModel = Models.ValidateNickname.ViewModel(canCheckDuplication: response.nicknameState == .valid, 28 | alertDescription: response.nicknameState.description) 29 | viewController?.displayNicknameValidation(response: viewModel) 30 | } 31 | 32 | func presentNicknameDuplication(with response: SignUpModels.CheckDuplication.Response) { 33 | let viewModel = Models.CheckDuplication.ViewModel(canSignUp: response.isValid) 34 | viewController?.displayNicknameDuplication(response: viewModel) 35 | } 36 | 37 | func presentSignUpSuccess() { 38 | viewController?.navigateToMain() 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/SignUpScene/SignUpRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpRouter.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/26/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol SignUpRoutingLogic { 12 | func routeToBack() 13 | func navigateToMain() 14 | } 15 | 16 | protocol SignUpDataPassing { 17 | var dataStore: SignUpDataStore? { get set } 18 | } 19 | 20 | final class SignUpRouter: SignUpRoutingLogic, SignUpDataPassing { 21 | 22 | // MARK: - Properties 23 | 24 | typealias Models = SignUpModels 25 | weak var viewController: SignUpViewController? 26 | var dataStore: SignUpDataStore? 27 | 28 | // MARK: - Routing 29 | 30 | func routeToBack() { 31 | viewController?.navigationController?.popViewController(animated: true) 32 | } 33 | 34 | func navigateToMain() { 35 | let mainTabBarViewController = MainTabBarViewController() 36 | viewController?.navigationController?.setNavigationBarHidden(true, animated: false) 37 | viewController?.navigationController?.setViewControllers([mainTabBarViewController], animated: true) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/Tabbar/MainTabBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTabBarViewController.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/14/23. 6 | // 7 | 8 | import UIKit 9 | 10 | final class MainTabBarViewController: UITabBarController { 11 | 12 | // MARK: - Object lifecycle 13 | 14 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 15 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 16 | setup() 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | super.init(coder: aDecoder) 21 | setup() 22 | } 23 | 24 | // MARK: - Setup 25 | 26 | private func setup() { 27 | MainTabBarConfigurator.shared.configure(self) 28 | } 29 | 30 | // MARK: ViewController Life Cycle 31 | 32 | override func viewDidLoad() { 33 | super.viewDidLoad() 34 | setUI() 35 | } 36 | 37 | // MARK: - UI 38 | 39 | private func setUI() { 40 | tabBar.tintColor = .primaryPurple 41 | tabBar.backgroundColor = .black 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagPlayListConfigurator.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/29/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class TagPlayListConfigurator: Configurator { 12 | 13 | static let shared = TagPlayListConfigurator() 14 | 15 | private init() { } 16 | 17 | func configure(_ viewController: TagPlayListViewController) { 18 | let viewController = viewController 19 | let interactor = TagPlayListInteractor() 20 | let presenter = TagPlayListPresenter() 21 | let worker = TagPlayListWorker() 22 | let router = TagPlayListRouter() 23 | 24 | router.viewController = viewController 25 | router.dataStore = interactor 26 | viewController.interactor = interactor 27 | viewController.router = router 28 | interactor.presenter = presenter 29 | interactor.worker = worker 30 | presenter.viewController = viewController 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagPlayListModels.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/29/23. 6 | // Copyright (c) 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum TagPlayListModels { 12 | // MARK: Use cases 13 | 14 | struct DisplayedPost: Hashable { 15 | let identifier: Int 16 | let thumbnailImageData: Data? 17 | let title: String 18 | } 19 | 20 | enum FetchPosts { 21 | struct Request { 22 | } 23 | 24 | struct Response { 25 | let post: [DisplayedPost] 26 | } 27 | 28 | struct ViewModel { 29 | let displayedPost: [DisplayedPost] 30 | } 31 | } 32 | 33 | enum FetchMorePosts { 34 | struct Request { 35 | } 36 | 37 | struct Response { 38 | let post: [DisplayedPost] 39 | } 40 | 41 | struct ViewModel { 42 | let displayedPost: [DisplayedPost] 43 | } 44 | } 45 | 46 | enum FetchTitleTag { 47 | struct Request { 48 | } 49 | 50 | struct Response { 51 | let titleTag: String 52 | } 53 | 54 | struct ViewModel { 55 | let title: String 56 | } 57 | } 58 | 59 | enum ShowPostsDetail { 60 | struct Request { 61 | let startIndex: Int 62 | } 63 | 64 | struct Response { 65 | } 66 | 67 | struct ViewModel { 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/TagPlayList/TagPlayListRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagPlayListRouter.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/29/23. 6 | // Copyright (c) 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol TagPlayListRoutingLogic { 12 | func routeToPlayback() 13 | } 14 | 15 | protocol TagPlayListDataPassing { 16 | var dataStore: TagPlayListDataStore? { get } 17 | } 18 | 19 | final class TagPlayListRouter: TagPlayListRoutingLogic, TagPlayListDataPassing { 20 | 21 | // MARK: - Properties 22 | 23 | weak var viewController: TagPlayListViewController? 24 | var dataStore: TagPlayListDataStore? 25 | 26 | func routeToPlayback() { 27 | let playbackViewController: PlaybackViewController = PlaybackViewController() 28 | guard let source = dataStore, 29 | var destination = playbackViewController.router?.dataStore else { return } 30 | 31 | passDataToPlayback(source: source, destination: &destination) 32 | viewController?.navigationController?.pushViewController(playbackViewController, animated: true) 33 | } 34 | 35 | private func passDataToPlayback(source: TagPlayListDataStore, destination: inout PlaybackDataStore) { 36 | destination.parentView = .tag 37 | destination.index = source.postPlayStartIndex 38 | destination.posts = source.posts 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/UploadPost/UploadPostConfigurator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadPostConfigurator.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/03. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class UploadPostConfigurator: Configurator { 12 | 13 | typealias ViewController = UploadPostViewController 14 | 15 | static let shared = UploadPostConfigurator() 16 | 17 | private init() { } 18 | 19 | func configure(_ viewController: ViewController) { 20 | let viewController = viewController 21 | let interactor = UploadPostInteractor(locationManager: CurrentLocationManager()) 22 | let presenter = UploadPostPresenter() 23 | let router = UploadPostRouter() 24 | let worker = UploadPostWorker() 25 | 26 | router.viewController = viewController 27 | router.dataStore = interactor 28 | viewController.interactor = interactor 29 | viewController.router = router 30 | interactor.presenter = presenter 31 | interactor.worker = worker 32 | presenter.viewController = viewController 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Scenes/UploadPost/UploadPostRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UploadPostRouter.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/01. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol UploadPostRoutingLogic { 12 | func routeToNext() 13 | func routeToBack() 14 | } 15 | 16 | protocol UploadPostDataPassing { 17 | var dataStore: UploadPostDataStore? { get } 18 | } 19 | 20 | final class UploadPostRouter: NSObject, UploadPostRoutingLogic, UploadPostDataPassing { 21 | 22 | // MARK: - Properties 23 | 24 | weak var viewController: UploadPostViewController? 25 | var dataStore: UploadPostDataStore? 26 | 27 | // MARK: - Routing 28 | 29 | func routeToNext() { 30 | let nextViewController = EditTagViewController() 31 | guard let source = dataStore, 32 | var destination = nextViewController.router?.dataStore 33 | else { return } 34 | 35 | destination.tags = source.tags 36 | nextViewController.modalPresentationStyle = .fullScreen 37 | viewController?.present(nextViewController, animated: true) 38 | } 39 | 40 | func routeToBack() { 41 | viewController?.navigationController?.popToRootViewController(animated: true) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Services/Location/LocationFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationFetcher.swift 3 | // Layover 4 | // 5 | // Created by kong on 2023/12/12. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | 11 | protocol LocationFetcherDelegate: AnyObject { 12 | func locationFetcher(_ fetcher: LocationFetcher, didUpdateLocations locations: [CLLocation]) 13 | } 14 | 15 | protocol LocationFetcher { 16 | var location: CLLocation? { get } 17 | var locationFetcherDelegate: LocationFetcherDelegate? { get set } 18 | var authorizationStatus: CLAuthorizationStatus { get } 19 | var desiredAccuracy: CLLocationAccuracy { get set } 20 | 21 | func requestLocation() 22 | func startUpdatingLocation() 23 | func requestWhenInUseAuthorization() 24 | } 25 | 26 | extension CLLocationManager: LocationFetcher { 27 | var locationFetcherDelegate: LocationFetcherDelegate? { 28 | get { 29 | return delegate as? LocationFetcherDelegate 30 | } 31 | set { 32 | delegate = newValue as? CLLocationManagerDelegate 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Services/System.swift: -------------------------------------------------------------------------------- 1 | // 2 | // System.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/1/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum System { 12 | 13 | @UserDefaultStored(key: UserDefaultKey.hasBeenLaunchedBefore, defaultValue: false) static var hasBeenLaunchedBefore: Bool? 14 | 15 | static func isFirstLaunch() -> Bool { 16 | if hasBeenLaunchedBefore == false { 17 | hasBeenLaunchedBefore = true 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | static func versionNumber() -> String? { 24 | guard let info = Bundle.main.infoDictionary, 25 | let currentVersion = info["CFBundleShortVersionString"] as? String else { return nil } 26 | return currentVersion 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Services/UserDefaults/UserDefaultStored.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaultStored.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 11/26/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @propertyWrapper 12 | struct UserDefaultStored { 13 | private let key: String 14 | private let defaultValue: T? 15 | 16 | init(key: String, defaultValue: T?) { 17 | self.key = key 18 | self.defaultValue = defaultValue 19 | } 20 | 21 | var wrappedValue: T? { 22 | get { 23 | if let savedData = UserDefaults.standard.object(forKey: key) as? Data { 24 | let decoder = JSONDecoder() 25 | if let loadedObject = try? decoder.decode(T.self, from: savedData) { 26 | return loadedObject 27 | } 28 | } 29 | return defaultValue 30 | } 31 | set { 32 | let encoder = JSONEncoder() 33 | if let encodedObject = try? encoder.encode(newValue) { 34 | UserDefaults.standard.set(encodedObject, forKey: key) 35 | } 36 | } 37 | } 38 | } 39 | 40 | enum UserDefaultKey { 41 | static let isLoggedIn = "isLoggedIn" 42 | static let hasBeenLaunchedBefore = "hasBeenLaunchedBefore" 43 | static let memberId = "memberId" 44 | static let loginType = "loginType" 45 | } 46 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Workers/Mocks/StubAuthManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubAuthManager.swift 3 | // Layover 4 | // 5 | // Created by 김인환 on 12/2/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class StubAuthManager: AuthManagerProtocol { 12 | 13 | // MARK: Properties 14 | 15 | var accessToken: String? = "Fake Access Token" 16 | var refreshToken: String? = "Fake Refresh Token" 17 | var isLoggedIn: Bool? = true 18 | var loginType: LoginType? = .kakao 19 | var memberID: Int? = -1 20 | } 21 | -------------------------------------------------------------------------------- /iOS/Layover/Layover/Workers/Mocks/sample.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/Layover/Workers/Mocks/sample.jpeg -------------------------------------------------------------------------------- /iOS/Layover/Layover/en.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/LocationFetcher/MockLocationFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockLocationFetcher.swift 3 | // LayoverTests 4 | // 5 | // Created by kong on 2023/12/13. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | 11 | @testable import Layover 12 | 13 | final class MockLocationFetcher: LocationFetcher { 14 | 15 | var location: CLLocation? 16 | var locationFetcherDelegate: Layover.LocationFetcherDelegate? 17 | var desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest 18 | var authorizationStatus: CLAuthorizationStatus = .authorizedWhenInUse 19 | 20 | func requestLocation() { } 21 | 22 | func startUpdatingLocation() { 23 | location = CLLocation(latitude: 37.498206, longitude: 127.02761) 24 | guard let location else { return } 25 | locationFetcherDelegate?.locationFetcher(self, didUpdateLocations: [location]) 26 | } 27 | 28 | func requestWhenInUseAuthorization() { } 29 | 30 | func requestAlwaysAuthorization() { } 31 | } 32 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/CheckSignUp.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "isAlreadyExist": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/CheckUserName.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "isValid": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/DeleteUser.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "username": "hwani" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/DeleteVideo.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/GetMember.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "id": 221, 6 | "username": "안유진", 7 | "introduce": "안녕하세요, 아이브의 안유진입니다.", 8 | "profile_image_url": "https://cdn.footballist.co.kr/news/photo/202307/170226_100422_1733.jpg" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/LoginData.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "accessToken": "mockAccessToken", 6 | "refreshToken": "mockRefreshToken" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PatchIntroduce.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "introduce": "야자타임을 좋아하고 더운걸 싫어하는 화니라고해요" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PatchProfileImage.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "profile-image": "https://i.ibb.co/qML8vdN/2023-11-25-9-08-01.png" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PatchUserName.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": { 5 | "username": "hwani" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PostBoard.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "id": 1, 6 | "title": "제목", 7 | "content": "내용", 8 | "latitude": 37.1231053, 9 | "longitude": 127.1231053, 10 | "tag": [ 11 | "부산", 12 | "광안리", 13 | "바다" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PostList.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": [ 5 | { 6 | "member" : { 7 | "id" : 1, 8 | "username" : "안유진", 9 | "introduce" : "안녕하세요, 아이브의 안유진입니다.", 10 | "profile_image_url" : "https://cdn.footballist.co.kr/news/photo/202307/170226_100422_1733.jpg" 11 | }, 12 | "board" : { 13 | "id" : 1, 14 | "encoded_video_url" : "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8", 15 | "video_thumbnail_url" : "https://think-note.com/wp-content/uploads/2023/07/eta_3.jpg", 16 | "longitude" : 37.0532156213, 17 | "latitude" : 127.060123123, 18 | "title" : "최강 아이돌", 19 | "content" : "게시글 설명", 20 | "status": "COMPLETE" 21 | }, 22 | "tag" : ["아이브", "yujin"] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PostListEnd.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": [ 5 | 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PostListMore.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "요청이 성공적으로 처리되었습니다.", 3 | "statusCode": 200, 4 | "data": [ 5 | { 6 | "member" : { 7 | "id" : 2, 8 | "username" : "장원영", 9 | "introduce" : "안녕하세요, 아이브의 장원영입니다.", 10 | "profile_image_url" : "https://www.fnnews.com/resource/media/image/2023/10/17/202310171126334525_l.jpg" 11 | }, 12 | "board" : { 13 | "id" : 2, 14 | "encoded_video_url" : "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8", 15 | "video_thumbnail_url" : "https://img.etoday.co.kr/pto_db/2022/11/600/20221108175829_1816470_1200_1800.jpg", 16 | "longitude" : 35.001828282, 17 | "latitude" : 100.060123123, 18 | "title" : "아이브의 멤버", 19 | "content" : "게시글 설명설명설명", 20 | "status": "COMPLETE" 21 | }, 22 | "tag" : ["Ive", "wonyoung"] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/PostsPage.json: -------------------------------------------------------------------------------- 1 | { 2 | "customCode": "SUCCESS", 3 | "message": "성공", 4 | "statusCode": 200, 5 | "data": { 6 | "lastId": 32, 7 | "boardsResDto": [ 8 | { 9 | "member": { 10 | "id": 221, 11 | "username": "hwani", 12 | "introduce": "Hi, my name is hwani", 13 | "profile_image_url": "https://layover-profile-image.kr.obj..." 14 | }, 15 | "board": { 16 | "id": 1, 17 | "encoded_video_url": "https://qc66zhsq1708.edge.naverncp.com/hls/fMG98Ec1UirV-awtm4qKJyhanmRFlPLZbTs_/layover-station/sv_AVC_HD, SD_1Pass_30fps.mp4/index.m3u8", 18 | "video_thumbnail_url": "https://layover-video-thumbnail.kr.obj...", 19 | "latitude": 37.0532156213, 20 | "longitude": 37.0532156213, 21 | "title": "붓산 광안리", 22 | "content": "날씨가 정말 좋았따이", 23 | "status": "COMPLETE" 24 | }, 25 | "tag": [ 26 | "tag1", 27 | "tag2" 28 | ] 29 | } 30 | ] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/ReportPlaybackVideo.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "성공", 3 | "statusCode": 200, 4 | "data": { 5 | "memberId": 221, 6 | "boardId": 5, 7 | "reportType": "청소년에게 유해한 내용이에요" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/MockDatas/sample.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boostcampwm2023/iOS09-Layover/a3f2d39f67882d9d3512c1ebb926985df2609662/iOS/Layover/LayoverTests/Mocks/MockDatas/sample.jpeg -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Mocks/Workers/MockSettingWorker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockSettingWorker.swift 3 | // LayoverTests 4 | // 5 | // Created by 김인환 on 12/13/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | @testable import Layover 9 | import Foundation 10 | 11 | final class MockSettingWorker: SettingWorkerProtocol { 12 | 13 | // MARK: - Methods 14 | 15 | func versionNumber() -> String? { 16 | "7.7.7" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /iOS/Layover/LayoverTests/Stubs/StubAuthManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubAuthManager.swift 3 | // LayoverTests 4 | // 5 | // Created by 김인환 on 12/2/23. 6 | // Copyright © 2023 CodeBomber. All rights reserved. 7 | // 8 | @testable import Layover 9 | import Foundation 10 | 11 | class StubAuthManager: AuthManagerProtocol { 12 | 13 | // MARK: - Properties 14 | 15 | var accessToken: String? = "Fake Access Token" 16 | var refreshToken: String? = "Fake Refresh Token" 17 | var isLoggedIn: Bool? = true 18 | var loginType: LoginType? = .kakao 19 | var memberID: Int? = -1 20 | 21 | // MARK: - Methods 22 | 23 | func login(accessToken: String?, refreshToken: String?, loginType: LoginType?) { 24 | self.accessToken = accessToken 25 | self.refreshToken = refreshToken 26 | self.loginType = loginType 27 | isLoggedIn = true 28 | } 29 | 30 | func logout() { 31 | accessToken = nil 32 | refreshToken = nil 33 | memberID = nil 34 | loginType = nil 35 | isLoggedIn = false 36 | } 37 | } 38 | --------------------------------------------------------------------------------