From 1828b51c056e47438a712be16bdcaca4d4f0e1b0 Mon Sep 17 00:00:00 2001 From: Jaesung Lee Date: Sun, 5 Jun 2022 17:57:24 +0900 Subject: [PATCH] =?UTF-8?q?[=EB=B2=84=EC=A0=84]=201.0.0=20-=20=EC=98=A4?= =?UTF-8?q?=ED=94=88=20=EC=95=B1=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- KuringLite.xcodeproj/project.pbxproj | 578 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/KuringLite.xcscheme | 78 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++ KuringLite/Assets.xcassets/Contents.json | 6 + .../Contents.json | 23 + .../app.label.png | Bin 0 -> 1152 bytes .../app.label@2x.png | Bin 0 -> 1810 bytes .../app.label@3x.png | Bin 0 -> 2509 bytes .../Contents.json | 23 + .../app.white.label.vertical.png | Bin 0 -> 4861 bytes .../app.white.label.vertical@2x.png | Bin 0 -> 9328 bytes .../app.white.label.vertical@3x.png | Bin 0 -> 14957 bytes KuringLite/Commons/LottieView.swift | 58 ++ KuringLite/Commons/lottieLoading.json | 1 + KuringLite/ContentView.swift | 46 ++ KuringLite/Extensions/Notice.KuringLite.swift | 35 ++ .../Extensions/NoticeType.KuringLite.swift | 14 + KuringLite/Feedback/FeedbackState.swift | 73 +++ KuringLite/Feedback/FeedbackView.swift | 96 +++ KuringLite/KuringLiteApp.swift | 19 + KuringLite/Launch Screen.storyboard | 74 +++ KuringLite/NoticeList/NoticeList.swift | 59 ++ KuringLite/NoticeList/NoticeListModel.swift | 109 ++++ KuringLite/NoticeList/NoticeRow.swift | 65 ++ KuringLite/NoticeList/NoticeTypeColumn.swift | 38 ++ .../Preview Assets.xcassets/Contents.json | 6 + KuringLite/Search/SearchEngine.swift | 72 +++ KuringLite/Search/SearchTypeColumn.swift | 38 ++ KuringLite/Search/SearchView.swift | 57 ++ KuringLite/Search/SearchedNoticeRow.swift | 52 ++ KuringLite/Search/SearchedResultList.swift | 29 + KuringLite/Search/SearchedStaffRow.swift | 28 + KuringLite/Settings/SettingsView.swift | 71 +++ KuringLite/Settings/SwiftPackageRow.swift | 48 ++ KuringLite/Subscription/Subscription.swift | 50 ++ .../Subscription/SubscriptionSelection.swift | 64 ++ .../Subscription/SubscriptionView.swift | 67 ++ README.md | 46 ++ 42 files changed, 2150 insertions(+), 3 deletions(-) create mode 100644 KuringLite.xcodeproj/project.pbxproj create mode 100644 KuringLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 KuringLite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 KuringLite.xcodeproj/xcshareddata/xcschemes/KuringLite.xcscheme create mode 100644 KuringLite/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 KuringLite/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 KuringLite/Assets.xcassets/Contents.json create mode 100644 KuringLite/Assets.xcassets/app.label.horizontal.imageset/Contents.json create mode 100644 KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label.png create mode 100644 KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@2x.png create mode 100644 KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@3x.png create mode 100644 KuringLite/Assets.xcassets/app.label.vertical.white.imageset/Contents.json create mode 100644 KuringLite/Assets.xcassets/app.label.vertical.white.imageset/app.white.label.vertical.png create mode 100644 KuringLite/Assets.xcassets/app.label.vertical.white.imageset/app.white.label.vertical@2x.png create mode 100644 KuringLite/Assets.xcassets/app.label.vertical.white.imageset/app.white.label.vertical@3x.png create mode 100644 KuringLite/Commons/LottieView.swift create mode 100644 KuringLite/Commons/lottieLoading.json create mode 100644 KuringLite/ContentView.swift create mode 100644 KuringLite/Extensions/Notice.KuringLite.swift create mode 100644 KuringLite/Extensions/NoticeType.KuringLite.swift create mode 100644 KuringLite/Feedback/FeedbackState.swift create mode 100644 KuringLite/Feedback/FeedbackView.swift create mode 100644 KuringLite/KuringLiteApp.swift create mode 100644 KuringLite/Launch Screen.storyboard create mode 100644 KuringLite/NoticeList/NoticeList.swift create mode 100644 KuringLite/NoticeList/NoticeListModel.swift create mode 100644 KuringLite/NoticeList/NoticeRow.swift create mode 100644 KuringLite/NoticeList/NoticeTypeColumn.swift create mode 100644 KuringLite/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 KuringLite/Search/SearchEngine.swift create mode 100644 KuringLite/Search/SearchTypeColumn.swift create mode 100644 KuringLite/Search/SearchView.swift create mode 100644 KuringLite/Search/SearchedNoticeRow.swift create mode 100644 KuringLite/Search/SearchedResultList.swift create mode 100644 KuringLite/Search/SearchedStaffRow.swift create mode 100644 KuringLite/Settings/SettingsView.swift create mode 100644 KuringLite/Settings/SwiftPackageRow.swift create mode 100644 KuringLite/Subscription/Subscription.swift create mode 100644 KuringLite/Subscription/SubscriptionSelection.swift create mode 100644 KuringLite/Subscription/SubscriptionView.swift create mode 100644 README.md diff --git a/.gitignore b/.gitignore index 330d167..8a9974a 100644 --- a/.gitignore +++ b/.gitignore @@ -38,13 +38,13 @@ playground.xcworkspace # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ -# Package.pins -# Package.resolved +Package.pins +Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project -# .swiftpm +.swiftpm .build/ diff --git a/KuringLite.xcodeproj/project.pbxproj b/KuringLite.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1bba3fa --- /dev/null +++ b/KuringLite.xcodeproj/project.pbxproj @@ -0,0 +1,578 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + A95125A8284C973A0072EB8E /* FeedbackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95125A7284C973A0072EB8E /* FeedbackView.swift */; }; + A95125AB284CA6770072EB8E /* KuringSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A95125AA284CA6770072EB8E /* KuringSDK */; }; + A983BAC2284BB08F00B96FF1 /* KuringLiteApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAC1284BB08F00B96FF1 /* KuringLiteApp.swift */; }; + A983BAC4284BB08F00B96FF1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAC3284BB08F00B96FF1 /* ContentView.swift */; }; + A983BAC6284BB08F00B96FF1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A983BAC5284BB08F00B96FF1 /* Assets.xcassets */; }; + A983BAC9284BB08F00B96FF1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A983BAC8284BB08F00B96FF1 /* Preview Assets.xcassets */; }; + A983BAD1284BB0C800B96FF1 /* KuringCommons in Frameworks */ = {isa = PBXBuildFile; productRef = A983BAD0284BB0C800B96FF1 /* KuringCommons */; }; + A983BAD6284BB53800B96FF1 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAD5284BB53800B96FF1 /* SearchView.swift */; }; + A983BAD9284BB54600B96FF1 /* NoticeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAD8284BB54600B96FF1 /* NoticeList.swift */; }; + A983BADC284BB57B00B96FF1 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BADB284BB57B00B96FF1 /* SettingsView.swift */; }; + A983BADF284BB78700B96FF1 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BADE284BB78700B96FF1 /* SubscriptionView.swift */; }; + A983BAE1284BB7B100B96FF1 /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAE0284BB7B100B96FF1 /* Subscription.swift */; }; + A983BAE4284BBDDA00B96FF1 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = A983BAE3284BBDDA00B96FF1 /* Starscream */; }; + A983BAE6284BC16D00B96FF1 /* SubscriptionSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAE5284BC16D00B96FF1 /* SubscriptionSelection.swift */; }; + A983BAE9284BC9CF00B96FF1 /* NoticeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAE8284BC9CF00B96FF1 /* NoticeRow.swift */; }; + A983BAEB284BCABC00B96FF1 /* NoticeListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAEA284BCABC00B96FF1 /* NoticeListModel.swift */; }; + A983BAED284BCB1F00B96FF1 /* NoticeTypeColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAEC284BCB1F00B96FF1 /* NoticeTypeColumn.swift */; }; + A983BAEF284BCB9B00B96FF1 /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAEE284BCB9B00B96FF1 /* LottieView.swift */; }; + A983BAF2284BCBCC00B96FF1 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = A983BAF1284BCBCC00B96FF1 /* Lottie */; }; + A983BAF4284BCC7700B96FF1 /* lottieLoading.json in Resources */ = {isa = PBXBuildFile; fileRef = A983BAF3284BCC7700B96FF1 /* lottieLoading.json */; }; + A983BAF7284BCD3D00B96FF1 /* Notice.KuringLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAF6284BCD3D00B96FF1 /* Notice.KuringLite.swift */; }; + A983BAFA284BCDB700B96FF1 /* NoticeType.KuringLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = A983BAF9284BCDB700B96FF1 /* NoticeType.KuringLite.swift */; }; + A983BAFC284BD10B00B96FF1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A983BAFB284BD10B00B96FF1 /* Launch Screen.storyboard */; }; + A9F8376F284C87980045FDBD /* SearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F8376E284C87980045FDBD /* SearchEngine.swift */; }; + A9F83771284C881C0045FDBD /* SearchTypeColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83770284C881C0045FDBD /* SearchTypeColumn.swift */; }; + A9F83773284C8B730045FDBD /* SearchedResultList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83772284C8B730045FDBD /* SearchedResultList.swift */; }; + A9F83775284C8B870045FDBD /* SearchedNoticeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83774284C8B870045FDBD /* SearchedNoticeRow.swift */; }; + A9F83777284C8B930045FDBD /* SearchedStaffRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83776284C8B930045FDBD /* SearchedStaffRow.swift */; }; + A9F83779284C93A60045FDBD /* SwiftPackageRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83778284C93A60045FDBD /* SwiftPackageRow.swift */; }; + A9F83783284C96880045FDBD /* FeedbackState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9F83781284C96880045FDBD /* FeedbackState.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + A95125A7284C973A0072EB8E /* FeedbackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackView.swift; sourceTree = ""; }; + A983BABE284BB08F00B96FF1 /* KuringLite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KuringLite.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A983BAC1284BB08F00B96FF1 /* KuringLiteApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KuringLiteApp.swift; sourceTree = ""; }; + A983BAC3284BB08F00B96FF1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + A983BAC5284BB08F00B96FF1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + A983BAC8284BB08F00B96FF1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + A983BAD5284BB53800B96FF1 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = ""; }; + A983BAD8284BB54600B96FF1 /* NoticeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeList.swift; sourceTree = ""; }; + A983BADB284BB57B00B96FF1 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + A983BADE284BB78700B96FF1 /* SubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionView.swift; sourceTree = ""; }; + A983BAE0284BB7B100B96FF1 /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = ""; }; + A983BAE5284BC16D00B96FF1 /* SubscriptionSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSelection.swift; sourceTree = ""; }; + A983BAE8284BC9CF00B96FF1 /* NoticeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRow.swift; sourceTree = ""; }; + A983BAEA284BCABC00B96FF1 /* NoticeListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeListModel.swift; sourceTree = ""; }; + A983BAEC284BCB1F00B96FF1 /* NoticeTypeColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeTypeColumn.swift; sourceTree = ""; }; + A983BAEE284BCB9B00B96FF1 /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; + A983BAF3284BCC7700B96FF1 /* lottieLoading.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = lottieLoading.json; sourceTree = ""; }; + A983BAF6284BCD3D00B96FF1 /* Notice.KuringLite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notice.KuringLite.swift; sourceTree = ""; }; + A983BAF9284BCDB700B96FF1 /* NoticeType.KuringLite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeType.KuringLite.swift; sourceTree = ""; }; + A983BAFB284BD10B00B96FF1 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; + A983BAFD284BD2B800B96FF1 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + A9F8376E284C87980045FDBD /* SearchEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchEngine.swift; sourceTree = ""; }; + A9F83770284C881C0045FDBD /* SearchTypeColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTypeColumn.swift; sourceTree = ""; }; + A9F83772284C8B730045FDBD /* SearchedResultList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchedResultList.swift; sourceTree = ""; }; + A9F83774284C8B870045FDBD /* SearchedNoticeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchedNoticeRow.swift; sourceTree = ""; }; + A9F83776284C8B930045FDBD /* SearchedStaffRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchedStaffRow.swift; sourceTree = ""; }; + A9F83778284C93A60045FDBD /* SwiftPackageRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftPackageRow.swift; sourceTree = ""; }; + A9F83781284C96880045FDBD /* FeedbackState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackState.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A983BABB284BB08F00B96FF1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A983BAD1284BB0C800B96FF1 /* KuringCommons in Frameworks */, + A983BAE4284BBDDA00B96FF1 /* Starscream in Frameworks */, + A983BAF2284BCBCC00B96FF1 /* Lottie in Frameworks */, + A95125AB284CA6770072EB8E /* KuringSDK in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A983BAB5284BB08F00B96FF1 = { + isa = PBXGroup; + children = ( + A983BAFD284BD2B800B96FF1 /* README.md */, + A983BAC0284BB08F00B96FF1 /* KuringLite */, + A983BABF284BB08F00B96FF1 /* Products */, + A9F8376B284C86DA0045FDBD /* Frameworks */, + ); + sourceTree = ""; + }; + A983BABF284BB08F00B96FF1 /* Products */ = { + isa = PBXGroup; + children = ( + A983BABE284BB08F00B96FF1 /* KuringLite.app */, + ); + name = Products; + sourceTree = ""; + }; + A983BAC0284BB08F00B96FF1 /* KuringLite */ = { + isa = PBXGroup; + children = ( + A983BAC1284BB08F00B96FF1 /* KuringLiteApp.swift */, + A983BAC3284BB08F00B96FF1 /* ContentView.swift */, + A9F8377F284C96880045FDBD /* Feedback */, + A983BADA284BB54A00B96FF1 /* NoticeList */, + A983BAE7284BC2ED00B96FF1 /* Subscription */, + A983BAD7284BB53C00B96FF1 /* Search */, + A983BADD284BB58200B96FF1 /* Settings */, + A983BAF5284BCC8300B96FF1 /* Commons */, + A983BAF8284BCD4100B96FF1 /* Extensions */, + A983BAC5284BB08F00B96FF1 /* Assets.xcassets */, + A983BAFB284BD10B00B96FF1 /* Launch Screen.storyboard */, + A983BAC7284BB08F00B96FF1 /* Preview Content */, + ); + path = KuringLite; + sourceTree = ""; + }; + A983BAC7284BB08F00B96FF1 /* Preview Content */ = { + isa = PBXGroup; + children = ( + A983BAC8284BB08F00B96FF1 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + A983BAD7284BB53C00B96FF1 /* Search */ = { + isa = PBXGroup; + children = ( + A983BAD5284BB53800B96FF1 /* SearchView.swift */, + A9F83772284C8B730045FDBD /* SearchedResultList.swift */, + A9F83774284C8B870045FDBD /* SearchedNoticeRow.swift */, + A9F83776284C8B930045FDBD /* SearchedStaffRow.swift */, + A9F83770284C881C0045FDBD /* SearchTypeColumn.swift */, + A9F8376E284C87980045FDBD /* SearchEngine.swift */, + ); + path = Search; + sourceTree = ""; + }; + A983BADA284BB54A00B96FF1 /* NoticeList */ = { + isa = PBXGroup; + children = ( + A983BAD8284BB54600B96FF1 /* NoticeList.swift */, + A983BAEA284BCABC00B96FF1 /* NoticeListModel.swift */, + A983BAEC284BCB1F00B96FF1 /* NoticeTypeColumn.swift */, + A983BAE8284BC9CF00B96FF1 /* NoticeRow.swift */, + ); + path = NoticeList; + sourceTree = ""; + }; + A983BADD284BB58200B96FF1 /* Settings */ = { + isa = PBXGroup; + children = ( + A983BADB284BB57B00B96FF1 /* SettingsView.swift */, + A9F83778284C93A60045FDBD /* SwiftPackageRow.swift */, + ); + path = Settings; + sourceTree = ""; + }; + A983BAE7284BC2ED00B96FF1 /* Subscription */ = { + isa = PBXGroup; + children = ( + A983BADE284BB78700B96FF1 /* SubscriptionView.swift */, + A983BAE5284BC16D00B96FF1 /* SubscriptionSelection.swift */, + A983BAE0284BB7B100B96FF1 /* Subscription.swift */, + ); + path = Subscription; + sourceTree = ""; + }; + A983BAF5284BCC8300B96FF1 /* Commons */ = { + isa = PBXGroup; + children = ( + A983BAEE284BCB9B00B96FF1 /* LottieView.swift */, + A983BAF3284BCC7700B96FF1 /* lottieLoading.json */, + ); + path = Commons; + sourceTree = ""; + }; + A983BAF8284BCD4100B96FF1 /* Extensions */ = { + isa = PBXGroup; + children = ( + A983BAF6284BCD3D00B96FF1 /* Notice.KuringLite.swift */, + A983BAF9284BCDB700B96FF1 /* NoticeType.KuringLite.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + A9F8376B284C86DA0045FDBD /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + A9F8377F284C96880045FDBD /* Feedback */ = { + isa = PBXGroup; + children = ( + A9F83781284C96880045FDBD /* FeedbackState.swift */, + A95125A7284C973A0072EB8E /* FeedbackView.swift */, + ); + path = Feedback; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A983BABD284BB08F00B96FF1 /* KuringLite */ = { + isa = PBXNativeTarget; + buildConfigurationList = A983BACC284BB08F00B96FF1 /* Build configuration list for PBXNativeTarget "KuringLite" */; + buildPhases = ( + A983BABA284BB08F00B96FF1 /* Sources */, + A983BABB284BB08F00B96FF1 /* Frameworks */, + A983BABC284BB08F00B96FF1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KuringLite; + packageProductDependencies = ( + A983BAD0284BB0C800B96FF1 /* KuringCommons */, + A983BAE3284BBDDA00B96FF1 /* Starscream */, + A983BAF1284BCBCC00B96FF1 /* Lottie */, + A95125AA284CA6770072EB8E /* KuringSDK */, + ); + productName = KuringLite; + productReference = A983BABE284BB08F00B96FF1 /* KuringLite.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A983BAB6284BB08F00B96FF1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1330; + LastUpgradeCheck = 1330; + TargetAttributes = { + A983BABD284BB08F00B96FF1 = { + CreatedOnToolsVersion = 13.3.1; + }; + }; + }; + buildConfigurationList = A983BAB9284BB08F00B96FF1 /* Build configuration list for PBXProject "KuringLite" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A983BAB5284BB08F00B96FF1; + packageReferences = ( + A983BACF284BB0C800B96FF1 /* XCRemoteSwiftPackageReference "kuring-ios-commons" */, + A983BAE2284BBDDA00B96FF1 /* XCRemoteSwiftPackageReference "Starscream" */, + A983BAF0284BCBCC00B96FF1 /* XCRemoteSwiftPackageReference "lottie-ios" */, + A95125A9284CA6770072EB8E /* XCRemoteSwiftPackageReference "kuring-sdk-ios-spm" */, + ); + productRefGroup = A983BABF284BB08F00B96FF1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A983BABD284BB08F00B96FF1 /* KuringLite */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A983BABC284BB08F00B96FF1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A983BAF4284BCC7700B96FF1 /* lottieLoading.json in Resources */, + A983BAC9284BB08F00B96FF1 /* Preview Assets.xcassets in Resources */, + A983BAFC284BD10B00B96FF1 /* Launch Screen.storyboard in Resources */, + A983BAC6284BB08F00B96FF1 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A983BABA284BB08F00B96FF1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A9F8376F284C87980045FDBD /* SearchEngine.swift in Sources */, + A983BADC284BB57B00B96FF1 /* SettingsView.swift in Sources */, + A983BAE6284BC16D00B96FF1 /* SubscriptionSelection.swift in Sources */, + A983BAFA284BCDB700B96FF1 /* NoticeType.KuringLite.swift in Sources */, + A983BAED284BCB1F00B96FF1 /* NoticeTypeColumn.swift in Sources */, + A983BAC4284BB08F00B96FF1 /* ContentView.swift in Sources */, + A983BAE9284BC9CF00B96FF1 /* NoticeRow.swift in Sources */, + A9F83773284C8B730045FDBD /* SearchedResultList.swift in Sources */, + A9F83771284C881C0045FDBD /* SearchTypeColumn.swift in Sources */, + A95125A8284C973A0072EB8E /* FeedbackView.swift in Sources */, + A983BAD9284BB54600B96FF1 /* NoticeList.swift in Sources */, + A9F83777284C8B930045FDBD /* SearchedStaffRow.swift in Sources */, + A983BAEB284BCABC00B96FF1 /* NoticeListModel.swift in Sources */, + A983BAD6284BB53800B96FF1 /* SearchView.swift in Sources */, + A9F83779284C93A60045FDBD /* SwiftPackageRow.swift in Sources */, + A983BAE1284BB7B100B96FF1 /* Subscription.swift in Sources */, + A983BAC2284BB08F00B96FF1 /* KuringLiteApp.swift in Sources */, + A983BAF7284BCD3D00B96FF1 /* Notice.KuringLite.swift in Sources */, + A9F83783284C96880045FDBD /* FeedbackState.swift in Sources */, + A983BADF284BB78700B96FF1 /* SubscriptionView.swift in Sources */, + A983BAEF284BCB9B00B96FF1 /* LottieView.swift in Sources */, + A9F83775284C8B870045FDBD /* SearchedNoticeRow.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + A983BACA284BB08F00B96FF1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A983BACB284BB08F00B96FF1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + A983BACD284BB08F00B96FF1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"KuringLite/Preview Content\""; + DEVELOPMENT_TEAM = 77CD4KLN3Y; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kuring.lite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A983BACE284BB08F00B96FF1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"KuringLite/Preview Content\""; + DEVELOPMENT_TEAM = 77CD4KLN3Y; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kuring.lite; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A983BAB9284BB08F00B96FF1 /* Build configuration list for PBXProject "KuringLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A983BACA284BB08F00B96FF1 /* Debug */, + A983BACB284BB08F00B96FF1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A983BACC284BB08F00B96FF1 /* Build configuration list for PBXNativeTarget "KuringLite" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A983BACD284BB08F00B96FF1 /* Debug */, + A983BACE284BB08F00B96FF1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + A95125A9284CA6770072EB8E /* XCRemoteSwiftPackageReference "kuring-sdk-ios-spm" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/KU-Stacks/kuring-sdk-ios-spm"; + requirement = { + branch = main; + kind = branch; + }; + }; + A983BACF284BB0C800B96FF1 /* XCRemoteSwiftPackageReference "kuring-ios-commons" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/KU-Stacks/kuring-ios-commons"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; + A983BAE2284BBDDA00B96FF1 /* XCRemoteSwiftPackageReference "Starscream" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/daltoniam/Starscream"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.0; + }; + }; + A983BAF0284BCBCC00B96FF1 /* XCRemoteSwiftPackageReference "lottie-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/airbnb/lottie-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + A95125AA284CA6770072EB8E /* KuringSDK */ = { + isa = XCSwiftPackageProductDependency; + package = A95125A9284CA6770072EB8E /* XCRemoteSwiftPackageReference "kuring-sdk-ios-spm" */; + productName = KuringSDK; + }; + A983BAD0284BB0C800B96FF1 /* KuringCommons */ = { + isa = XCSwiftPackageProductDependency; + package = A983BACF284BB0C800B96FF1 /* XCRemoteSwiftPackageReference "kuring-ios-commons" */; + productName = KuringCommons; + }; + A983BAE3284BBDDA00B96FF1 /* Starscream */ = { + isa = XCSwiftPackageProductDependency; + package = A983BAE2284BBDDA00B96FF1 /* XCRemoteSwiftPackageReference "Starscream" */; + productName = Starscream; + }; + A983BAF1284BCBCC00B96FF1 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = A983BAF0284BCBCC00B96FF1 /* XCRemoteSwiftPackageReference "lottie-ios" */; + productName = Lottie; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = A983BAB6284BB08F00B96FF1 /* Project object */; +} diff --git a/KuringLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/KuringLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/KuringLite.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/KuringLite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/KuringLite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/KuringLite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/KuringLite.xcodeproj/xcshareddata/xcschemes/KuringLite.xcscheme b/KuringLite.xcodeproj/xcshareddata/xcschemes/KuringLite.xcscheme new file mode 100644 index 0000000..7f9252c --- /dev/null +++ b/KuringLite.xcodeproj/xcshareddata/xcschemes/KuringLite.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KuringLite/Assets.xcassets/AccentColor.colorset/Contents.json b/KuringLite/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/KuringLite/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KuringLite/Assets.xcassets/AppIcon.appiconset/Contents.json b/KuringLite/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/KuringLite/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KuringLite/Assets.xcassets/Contents.json b/KuringLite/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/KuringLite/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KuringLite/Assets.xcassets/app.label.horizontal.imageset/Contents.json b/KuringLite/Assets.xcassets/app.label.horizontal.imageset/Contents.json new file mode 100644 index 0000000..f2e4ecb --- /dev/null +++ b/KuringLite/Assets.xcassets/app.label.horizontal.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "app.label.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "app.label@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "app.label@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label.png b/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label.png new file mode 100644 index 0000000000000000000000000000000000000000..845981dcf919fa9ccbaa451604475659110c5d99 GIT binary patch literal 1152 zcmV-`1b_R9P)>vjij5vNQh?qe2C$JrLkaXKS{e$M#DrK0(Vn9u6hei@APX3x zjU1YY2_~$efWm|;NJRAR{NCKoDaZa^@GzcVGVCyKc5lCV?|t)TmDrr7W-@e#N`Nd; zYVnFTLPv}cnu?dXPo;r@cI2c3RE8)^(jh7nT$rNMU-w6?QF z#UP!fM|iV{;~cbXhT^#B=1_z(H0Ch>E?EetBk7v3N)^Y#w)Topm~NeF&-eRJx6{De zaViQKT7kkCRwjjam@GvL$M@j*ppsvJU&zf>6`RN@D!gm47#)a$$&EL3SXgY+y6kKS z;}{}gu5f%Qt|f7a7ml~Q)aF1uX7@Z_q$}ag8+8{&NV8|3ZLZMR`n(w1+D*~$hiNg7 z7v;gZZhErzoR+r@PtOK1;T);z%SEF{-C;&t}#h8su62jW3Ldg+sNfl5V3ybI1}dFX776i z@^2-p*xK?r9a5~LK~ZQFKfLb;QC{m-&$1=Wp&SKK_FQe)aVHU>UTF&6V}vv>0yjTc zXw_`%|KD>uZ9_Vs(1_le+n(HDNt(AHnYVu7$Gbj|go_N(%UVxBZm+k@YK6lI9B}#y z2M>^Dq8+u-q6jm13u~@!5T14uGCtwKgHPR_na&Y4#9xJapw2yJD33bbhfuZ@zyqZD zWxJO~AzV@Fg~#1OyAa9k&-u{yAY^J0;w53A+R|e)TD_HVFG%Wfqyj6v&Rd&|l#D}8 z&uk_kR41?Q9AgynN)HA#9Nd6^)ONT*SDA_dHU zWvHnrUo|{4!a45gXekPh)oIZ-U4*J8T!n3{M=VmQ!748agX9nhR&nFLIiV;qhay0N2E SR^b2u0000v=lP{ literal 0 HcmV?d00001 diff --git a/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@2x.png b/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3319788c79bc89df1d1e5b4fda0459faf0077bc2 GIT binary patch literal 1810 zcmV+t2krQYP)BpR+9yq*Qf=igc*rprm|AD9!??iW7$hHB1dsOUJrmU?EC7p{fIy0WmfeEDR{l zSQwftBo%Gq;0OT&NnoI&m14w@AdqU546PLVc>eGCQu}Nt_icQ4Kk0h)*>{fozvuIP zY@!g~BjKO)ikv8wX^Z?vA%9lxrGOKm5Z@Du8~{R*13)Np00>160HMeMAQU+O#3;m9 zq?Y9^QYfK4=0?bLXvscDA?gV&Sx4%=e3fti^G#o%P=QPkjnH&%R4!P0uftGifxCG# z242@?OXN>pyF~rRy6My37HIu(fvl1`@e*A)^(GC*6SVWBNcq1X(CpSK?d?AyuaP85 zDhj2^A-G+<$-5aB-Ye1Ux-~@TxGj-WCteo&@$Lc3B41>2C;QJGJnrs@i(`F!^Gy9b zN9_&^nILya7=2;OWAepQZ!|m(tA6KXlG+`%)Z^4bB!%KFjmZ!amfMVEyoVOIH^@s^ zlpKkC#mdPv$T4=n@XO7-m&t2CJFNvGAB%NRvMV9tZYyH$QE)PoWn?t@SKgue^A?_P?1`UCn68B z+r9qk#DQz>@9bt_JA@_AZslqcFAdxXi#zqM8aK%hXXBs$epkGvoe{$tWGuT@%GRtH zxGFNl{P6TS5qqnFz*l-l=(C4+>FYm#5Mm=kA7nQU>t5R46vy>k=}n9CK!*1>e!Wdj z!?kOMcOzs0MU9(=;qofYyIFD>S44(uh6u2_X5cy1l2=7?byR{?EihQx*(5jM7@B6W zE|V2Z2<#<}iwyA~2CVfcARJuy61(N~L*lPqU^HJ4(?g8mn8=8`lc&e2k6pAGPzz&& zRqyh)#^S+^YoHsZ7uf5v$`%sEO`cKYa~w_991$6vFT0HkSiZK^pgi+|UI@$y&OE#9 z0$G6n)DhCPDJ?WZuI1knrIlk?7Wr6}7)D&PRr3Elpc}v37M8wVERfse$K-k5&65Qx zwQQRHXIK(hi@RO{q1yq48n{{ade??AB{Je}=EZbfNfc56%aBstu>_f8mdiLpWfg~( z(Y3eb+z~HJ7#8{ZE9ZT&VBjXvDCJ6OKiU&w29r8NBBQC%DuK0{yEFED&G9UE%FsBm z19zO_F+2X@K*&^}G!G&XC;XRI@PWG4K9ljaM>CCM*e|Dfq~rypTUO!IC+ zf1EA#7H8&N9430P@&9|-RoBliv-=+UR!+M%8KRY3NY3ok*SJPs=L_OG5PPzJTnOH( z;d?LPIGoY9Nx79d&8kFO*O>JK z`MxDWJ^DkjSo7vbEK0Ke7$!VD8Lrn{IO4U{Z(1O!!}(eW@VgaPNa!9H*=n1{z-G^R z`V9(*J+(e(IbP#@G)CV4>5Cf6_AT}Gbe!XK)8@pPCeO9xYcg=%5KE1#PsuFpK8kA; zsX>#a%o(#*8dZa1(K6ksy9g&_E&ZB|*Dca^BNCw*E}?1`P(%%^&Lc A$N&HU literal 0 HcmV?d00001 diff --git a/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@3x.png b/KuringLite/Assets.xcassets/app.label.horizontal.imageset/app.label@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6f406aae57a8a014158ca3f825224ea061bfd010 GIT binary patch literal 2509 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 literal 0 HcmV?d00001 diff --git a/KuringLite/Assets.xcassets/app.label.vertical.white.imageset/app.white.label.vertical@2x.png b/KuringLite/Assets.xcassets/app.label.vertical.white.imageset/app.white.label.vertical@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a89edb6f631eff3d4fbc1594882de82dd277d54f GIT binary patch literal 9328 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 literal 0 HcmV?d00001 diff --git a/KuringLite/Commons/LottieView.swift b/KuringLite/Commons/LottieView.swift new file mode 100644 index 0000000..aa1d9b0 --- /dev/null +++ b/KuringLite/Commons/LottieView.swift @@ -0,0 +1,58 @@ +// +// LottieView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import Lottie + +struct LottieView: UIViewRepresentable { + + // makeCoordinator를 구현하여 제약사항을 구현합니다. + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + // json파일명을 받을 프로퍼티 + var filename: String + + // lottie View + var animationView = AnimationView() + + + class Coordinator: NSObject { + var parent: LottieView + + init(_ animationView: LottieView) { + // frame을 LottieView로 할당합니다. + self.parent = animationView + super.init() + } + } + + func makeUIView(context: UIViewRepresentableContext) -> UIView { + let view = UIView() + + // lottie 구현뷰 + animationView.animation = Animation.named(filename) + animationView.contentMode = .scaleAspectFit + animationView.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(animationView) + + NSLayoutConstraint.activate([ + animationView.widthAnchor.constraint(equalTo: view.widthAnchor), + animationView.heightAnchor.constraint(equalTo: view.heightAnchor) + ]) + + // 애니메이션이 계속 반복되게합니다. + animationView.loopMode = .loop + animationView.play() + return view + } + + func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext) { } +} + diff --git a/KuringLite/Commons/lottieLoading.json b/KuringLite/Commons/lottieLoading.json new file mode 100644 index 0000000..3cec72a --- /dev/null +++ b/KuringLite/Commons/lottieLoading.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE 1.0.0","a":"","k":"","d":"","tc":"none"},"fr":60,"ip":9,"op":41,"w":300,"h":300,"nm":"Comp 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"ball4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":223,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":25,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":40,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":55,"s":[169.5]},{"t":70,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":40,"op":71,"st":25,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"ball 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":150,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":20,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":35,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":50,"s":[169.5]},{"t":65,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":35,"op":66,"st":20,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"ball 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":77,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":15,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":30,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":45,"s":[169.5]},{"t":60,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":61,"st":15,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"ball3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":223,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":-6,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":9,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":24,"s":[169.5]},{"t":39,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":9,"op":40,"st":-6,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"ball 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":150,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":-11,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":4,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":19,"s":[169.5]},{"t":34,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":4,"op":35,"st":-11,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"ball 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":77,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.534],"y":[0]},"t":-16,"s":[169.5]},{"i":{"x":[0.424],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":-1,"s":[129.5]},{"i":{"x":[0.465],"y":[1]},"o":{"x":[0.054],"y":[0]},"t":14,"s":[169.5]},{"t":29,"s":[129.5]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-14.359],[14.359,0],[0,14.359],[-14.359,0]],"o":[[0,14.359],[-14.359,0],[0,-14.359],[14.359,0]],"v":[[26,0],[0,26],[-26,0],[0,-26]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.23921568627450981,0.7411764705882353,0.5019607843137255,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-1,"op":30,"st":-16,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Pre-comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150,150,0],"ix":2},"a":{"a":0,"k":[150,150,0],"ix":1},"s":{"a":0,"k":[41,41,100],"ix":6}},"ao":0,"w":300,"h":300,"ip":0,"op":211,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/KuringLite/ContentView.swift b/KuringLite/ContentView.swift new file mode 100644 index 0000000..e218f16 --- /dev/null +++ b/KuringLite/ContentView.swift @@ -0,0 +1,46 @@ +// +// ContentView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringCommons + +struct ContentView: View { + @State private var showsSubscriptionView: Bool = false + + var body: some View { + NavigationView { + NoticeList() + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItemGroup(placement: .navigationBarLeading) { + Image("app.label.horizontal") + } + + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button(action: { showsSubscriptionView.toggle() }) { + Image(systemName: "checklist") + } + + NavigationLink { + SearchView() + } label: { + Image(systemName: "magnifyingglass") + } + + NavigationLink { + SettingsView() + } label: { + Image(systemName: "ellipsis") + } + } + } + .sheet(isPresented: $showsSubscriptionView) { + SubscriptionView() + } + } + } +} diff --git a/KuringLite/Extensions/Notice.KuringLite.swift b/KuringLite/Extensions/Notice.KuringLite.swift new file mode 100644 index 0000000..6bcb729 --- /dev/null +++ b/KuringLite/Extensions/Notice.KuringLite.swift @@ -0,0 +1,35 @@ +// +// Notice.KuringLite.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// Developed by Hamlit Jason on 2021/12/01. +// + +import KuringSDK +import UIKit + +extension Notice { + enum NoticeURL { + case original(_ articleID: String) + case library(_ articleID: String) + + var urlString: String { + switch self { + case .original(let articleID): + return "https://www.konkuk.ac.kr/do/MessageBoard/ArticleRead.do?id=\(articleID)" + case .library(let articleID): + return "https://library.konkuk.ac.kr/#/bbs/notice/\(articleID)" + } + } + } + + var urlString: String { + switch category { + case .도서관 : + return NoticeURL.library(articleID).urlString + default: + return NoticeURL.original(articleID).urlString + } + } +} diff --git a/KuringLite/Extensions/NoticeType.KuringLite.swift b/KuringLite/Extensions/NoticeType.KuringLite.swift new file mode 100644 index 0000000..e9251d0 --- /dev/null +++ b/KuringLite/Extensions/NoticeType.KuringLite.swift @@ -0,0 +1,14 @@ +// +// NoticeType.KuringLite.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import KuringSDK + +extension NoticeType { + var isSubscribed: Bool { + Kuring.subscribedCategories.contains(self) + } +} diff --git a/KuringLite/Feedback/FeedbackState.swift b/KuringLite/Feedback/FeedbackState.swift new file mode 100644 index 0000000..2b273e8 --- /dev/null +++ b/KuringLite/Feedback/FeedbackState.swift @@ -0,0 +1,73 @@ +// +// FeedbackState.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +class FeedbackState: ObservableObject { + @Published var feedback: String { + didSet { updateStates() } + } + @Published private(set) var textLimitGuide: String + @Published private(set) var isSendable: Bool = false + @Published private(set) var isOverTextLimit: Bool = false + @Published private(set) var textEditorColor: Color = ColorSet.Label.tertiary.color + @Published private(set) var textLimitColor: Color = ColorSet.Label.secondary.color + + let placeholder = "피드백을 남겨주세요" + let textLimit: (min: Int, max: Int) = (min: 5, max: 256) + + init() { + feedback = placeholder + textLimitGuide = "\(textLimit.min)글자 이상 입력해주세요" + } + + func send(onComplete: @escaping () -> Void) { + guard !feedback.isEmpty, feedback != placeholder else { return } + guard isSendable else { return } + Kuring.sendFeedback(feedback) { [weak self] result in + guard let self = self else { return } + // 전송 완료 시 결과와 상관 없이 `feedback`를 활성화 시킵니다. + self.feedback = self.placeholder + + switch result { + case .success: break + case .failure(let error): Logger.debug(error.localizedDescription) + } + onComplete() + } + isSendable = false + } + + private func updateStates() { + isSendable = feedback != placeholder + && feedback.count >= textLimit.min + && feedback.count <= textLimit.max + textLimitGuide = feedback.count < textLimit.min || feedback == placeholder + ? "\(textLimit.min)글자 이상 입력해주세요" + : "글자수: \(feedback.count) / \(textLimit.max)" + isOverTextLimit = feedback.count > textLimit.max + textLimitColor = isOverTextLimit + ? ColorSet.pink.color + : ColorSet.Label.secondary.color + } + + func endEditing() { + if feedback.isEmpty { + feedback = placeholder + textEditorColor = ColorSet.Label.tertiary.color + } + } + + func startEditing() { + if feedback == placeholder { + feedback = "" + textEditorColor = ColorSet.Label.primary.color + } + } +} diff --git a/KuringLite/Feedback/FeedbackView.swift b/KuringLite/Feedback/FeedbackView.swift new file mode 100644 index 0000000..1dc5f73 --- /dev/null +++ b/KuringLite/Feedback/FeedbackView.swift @@ -0,0 +1,96 @@ +// +// FeedbackView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringCommons + +struct FeedbackView: View { + @Environment(\.presentationMode) var presentationMode + @StateObject private var stateModel = FeedbackState() + + var body: some View { + NavigationView { + ZStack { + ColorSet.Background.primary.color + .edgesIgnoringSafeArea(.all) + + VStack { + Text("피드백을 남겨서 앱이 성장 하는데에\n큰 기여를 해주세요 🙂") + .foregroundColor(ColorSet.Label.primary.color) + .lineLimit(2) + .multilineTextAlignment(.center) + .padding(.top, 24) + .padding(.bottom, 32) + + TextEditor(text: $stateModel.feedback) + .foregroundColor(stateModel.textEditorColor) + .font(.footnote) + .frame(maxHeight: 164) + .padding(8) + .background( + RoundedRectangle(cornerRadius: 12) + .stroke(ColorSet.green.color, lineWidth: 1) + .foregroundColor(.clear) + .frame(maxHeight: 180) + + ) + .onTapGesture { + stateModel.startEditing() + } + + HStack { + Spacer() + + Text(stateModel.textLimitGuide) + .font(.caption) + .foregroundColor(stateModel.textLimitColor) + } + + .padding(.bottom, 24) + + Button(action: send) { + Text("피드백 보내기") + .foregroundColor(ColorSet.Background.primary.color) + .padding(.horizontal) + .background( + RoundedRectangle(cornerRadius: 26) + .foregroundColor(ColorSet.green.color) + .frame(height: 52) + .frame(minWidth: 232) + ) + .padding() + } + .opacity(stateModel.isSendable ? 1.0 : 0.5) + .disabled(!stateModel.isSendable) + + Spacer() + } + .padding(.horizontal, 16) + } + .navigationTitle("💬 피드백") + .navigationBarTitleDisplayMode(.inline) + .onTapGesture { + hideKeyboard() + stateModel.endEditing() + } + } + } + + init() { + UITextView.appearance().backgroundColor = .clear + } + + func send() { + stateModel.send { + presentationMode.wrappedValue.dismiss() + } + } + + func hideKeyboard() { + UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) + } +} diff --git a/KuringLite/KuringLiteApp.swift b/KuringLite/KuringLiteApp.swift new file mode 100644 index 0000000..0ae66d8 --- /dev/null +++ b/KuringLite/KuringLiteApp.swift @@ -0,0 +1,19 @@ +// +// KuringLiteApp.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringCommons + +@main +struct KuringLiteApp: App { + var body: some Scene { + WindowGroup { + ContentView() + .accentColor(ColorSet.Label.primary.color) + } + } +} diff --git a/KuringLite/Launch Screen.storyboard b/KuringLite/Launch Screen.storyboard new file mode 100644 index 0000000..63792e1 --- /dev/null +++ b/KuringLite/Launch Screen.storyboard @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KuringLite/NoticeList/NoticeList.swift b/KuringLite/NoticeList/NoticeList.swift new file mode 100644 index 0000000..9a73456 --- /dev/null +++ b/KuringLite/NoticeList/NoticeList.swift @@ -0,0 +1,59 @@ +// +// NoticeList.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct NoticeList: View { + @StateObject private var model = NoticeListModel() + + var body: some View { + VStack { + ScrollView(.horizontal, showsIndicators: false) { + LazyHStack(spacing: 10) { + ForEach(NoticeType.allCases, id: \.self) { + NoticeTypeColumn( + model: model, + noticeType: $0 + ) + } + } + .padding(.leading, 16) + } + .frame(height: 48) + + ZStack { + List { + ForEach(model.currentNotices) { notice in + ZStack(alignment: .leading) { + Link(destination: URL(string: notice.urlString)!) { + EmptyView() + } + .opacity(0) + + NoticeRow(notice: notice) + } + .onAppear { + // 마지막에 도달했을 때 공지 더 가져오기 + if model.currentNotices.last == notice { + model.load() + } + } + } + } + .listStyle(.plain) + .onAppear { model.load() } + + if model.isLoading { + LottieView(filename: "lottieLoading") + .frame(width: 100, height: 100, alignment: .center) + } + } + } + } +} diff --git a/KuringLite/NoticeList/NoticeListModel.swift b/KuringLite/NoticeList/NoticeListModel.swift new file mode 100644 index 0000000..8f5d62c --- /dev/null +++ b/KuringLite/NoticeList/NoticeListModel.swift @@ -0,0 +1,109 @@ +// +// NoticeListDataModel.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +class NoticeListModel: ObservableObject { + @Published var currentType: NoticeType = .학사 { + didSet { + if noticeList[currentType] == nil { load() } + currentNotices = noticeList[currentType] ?? [] + } + } + + @Published var currentNotices: [Notice] = [] + /// 각 타입별로 가져온 공지사항을 저장 + var noticeList: [NoticeType: [Notice]] = [:] { + didSet { + currentNotices = noticeList[currentType] ?? [] + } + } + + @Published var isLoading = false + + var query: NoticeListQuery? + + // MARK: State + /// 각 타입별로 어느 오프셋부터 공지사항을 가져오면 되는지 기록 + var offsetList: [NoticeType: Int] = [:] + /// 각 타입별로 불러올 수 있는 공지사항이 더 존재하는지 여부 기록 + var hasNextList: [NoticeType: Bool] = [:] + /// 한번 요청 시 가져올 수 있는 공지 사항 개수 최댓값 + let loadLimit = 20 + + func refresh() { + isLoading = true + // 오프셋0부터 데이터 가져오기 + let params = NoticeListQuery.Params( + type: currentType, + offset: 0, + max: UInt(loadLimit) + ) + query = Kuring.createNoticeListQuery(with: params) + query?.load { [weak self] result in + guard let self = self else { return } + switch result { + case .success(let notices): + let noticeType = notices.first?.category ?? self.currentType + var newNotices: [Notice] = [] + for notice in notices { + if notice.id == self.noticeList[noticeType]?.first?.id { return } + newNotices.append(notice) + } + guard !newNotices.isEmpty else { return } + + // 가져온 데이터 수 만큼 오프셋 값 추가 + self.offsetList[noticeType, default: 0] += newNotices.count + + // 가져온 데이터 array 가장 앞에 삽입 + // Update notices + self.noticeList[noticeType, default: []].insert(contentsOf: newNotices, at: 0) + case .failure(let error): + Logger.error(error.localizedDescription) + } + self.isLoading = false + } + } + + func load() { + if hasNextList[currentType] == false { return } + isLoading = true + let currentOffset = offsetList[currentType] ?? 0 + let params = NoticeListQuery.Params( + type: currentType, + offset: UInt(currentOffset), + max: UInt(loadLimit) + ) + query = Kuring.createNoticeListQuery(with: params) + query?.load { [weak self] result in + guard let self = self else { return } + switch result { + case .success(let notices): + // Update hasNext + let noticeType = notices.first?.category ?? self.currentType + let hasNext = notices.count >= self.loadLimit + self.hasNextList.updateValue(hasNext, forKey: noticeType) + + // Update offset + let prevOffset = self.offsetList[noticeType] ?? 0 + let currentOffset = prevOffset + notices.count + self.offsetList.updateValue(currentOffset, forKey: noticeType) + + // Update notices + var currentNotices = self.noticeList[noticeType] ?? [] + notices.forEach { currentNotices.append($0) } + self.noticeList.updateValue(currentNotices, forKey: noticeType) + case .failure(let error): + Logger.error(error.localizedDescription) + } + self.isLoading = false + } + } +} + diff --git a/KuringLite/NoticeList/NoticeRow.swift b/KuringLite/NoticeList/NoticeRow.swift new file mode 100644 index 0000000..75e3575 --- /dev/null +++ b/KuringLite/NoticeList/NoticeRow.swift @@ -0,0 +1,65 @@ +// +// NoticeRow.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct NoticeRow: View { + let notice: Notice + + var dateText: String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM.dd" + return formatter.string(from: Date(timeIntervalSince1970: notice.postedAt)) + } + + var body: some View { + HStack { + // MARK: New notice marker + Image(systemName: "circle.fill") + .resizable() + .frame(width: 8, height: 8) + .foregroundColor( + notice.isNew + ? notice.isSubscribed ? ColorSet.pink.color : ColorSet.gray.color + : .clear + ) + .padding(.leading, 8) + + VStack(alignment: .leading, spacing: 8) { + // MARK: Title + Text(notice.subject) + .font(.subheadline) + .lineLimit(2) + + HStack { + // MARK: Date + Text(dateText) + .foregroundColor(ColorSet.Label.secondary.color) + .font(.caption) + + // MARK: Tag + ForEach(notice.tags, id: \.self) { + Text($0) + .frame(height: 16) + .foregroundColor(ColorSet.Background.primary.color) + .font(.caption) + .padding(.horizontal, 4) + .background( + RoundedRectangle(cornerRadius: 8) + .foregroundColor(ColorSet.secondaryGray.color) + ) + + } + } + } + .padding(.trailing, 24) + } + .frame(minHeight: 56) + } +} diff --git a/KuringLite/NoticeList/NoticeTypeColumn.swift b/KuringLite/NoticeList/NoticeTypeColumn.swift new file mode 100644 index 0000000..300f8d0 --- /dev/null +++ b/KuringLite/NoticeList/NoticeTypeColumn.swift @@ -0,0 +1,38 @@ +// +// NoticeTypeColumn.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct NoticeTypeColumn: View { + @ObservedObject var model: NoticeListModel + let noticeType: NoticeType + + var body: some View { + Text(noticeType.koreanValue) + .font(.subheadline) + .foregroundColor( + noticeType == model.currentType + ? ColorSet.Label.green.color + : ColorSet.Label.primary.color + ) + .padding(.horizontal, 16) + .frame(height: 36) + .background( + RoundedRectangle(cornerRadius: 18) + .foregroundColor( + noticeType == model.currentType + ? ColorSet.secondaryGreen.color + : Color.clear + ) + ) + .onTapGesture { + model.currentType = noticeType + } + } +} diff --git a/KuringLite/Preview Content/Preview Assets.xcassets/Contents.json b/KuringLite/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/KuringLite/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KuringLite/Search/SearchEngine.swift b/KuringLite/Search/SearchEngine.swift new file mode 100644 index 0000000..ec83aba --- /dev/null +++ b/KuringLite/Search/SearchEngine.swift @@ -0,0 +1,72 @@ +// +// SearchEngine.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +class SearchEngine: ObservableObject { + @Published var searchText: String = "" + + @Published var currentType: Searcher.SearchType = .notice { + didSet { + resetResult() + search() + } + } + + @Published var staffResult: [Staff] = [] + @Published var noticeResult: [Notice] = [] + + var searcher: Searcher? + + func start() { + searcher = Kuring.createSearcher(delegate: self) + searcher?.connect() + } + + func resetResult() { + staffResult = [] + noticeResult = [] + } + + func search() { + guard !searchText.isEmpty else { + return resetResult() + } + searcher?.search(searchText, forType: currentType) + } +} + +// MARK: SearcherDelegate +extension SearchEngine: SearcherDelegate { + /// 서쳐의 연결 상태가 변경될 때마다 호출되는 이벤트 입니다. + func searcher(_ searcher: Searcher, didChangeState state: Searcher.ConnectionState) { + // NOTE: disconnected 일 때 연결상태가 불안정하다는 시스템 메세지를 보여줘야 하나? + } + + /// 서쳐에서 에러가 발생할 때 호출되는 이벤트 입니다. + func searcher(_ searcher: Searcher, didReceiveError error: Error?) { + Logger.debug(error?.localizedDescription ?? "") + } + + /// 서쳐에서 교직원 정보(`[Staff]`)를 가져왔을 때 호출 되는 이벤트 입니다. + func searcher(_ searcher: Searcher, didReceiveStaffList staffs: [Staff]) { + self.staffResult = staffs + } + + /// 서쳐에서 공지 정보(`[Notice]`)를 가져왔을 때 호출 되는 이벤트 입니다. + func searcher(_ searcher: Searcher, didReceiveNoticeList notices: [Notice]) { + self.noticeResult = notices + } + + /// 서처에서 웹소켓 연결 유지를 위해 30초마다 보내는 **heart beat** 가 서버로 전송 되었을 때 호출되는 이벤트 입니다. + func searcherDidSendHeartbeat(_ searcher: Searcher) { + // TODO: 주기적으로 핑 보내는지 확인 필요 + Logger.debug("didSendHeartbeat") + } +} diff --git a/KuringLite/Search/SearchTypeColumn.swift b/KuringLite/Search/SearchTypeColumn.swift new file mode 100644 index 0000000..db27685 --- /dev/null +++ b/KuringLite/Search/SearchTypeColumn.swift @@ -0,0 +1,38 @@ +// +// SearchTypeColumn.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct SearchTypeColumn: View { + @ObservedObject var engine: SearchEngine + let searchType: Searcher.SearchType + + var body: some View { + Text(searchType.koreanValue) + .font(.subheadline) + .foregroundColor( + searchType == engine.currentType + ? ColorSet.Label.green.color + : ColorSet.Label.primary.color + ) + .padding(.horizontal, 16) + .frame(height: 36) + .background( + RoundedRectangle(cornerRadius: 18) + .foregroundColor( + searchType == engine.currentType + ? ColorSet.secondaryGreen.color + : Color.clear + ) + ) + .onTapGesture { + engine.currentType = searchType + } + } +} diff --git a/KuringLite/Search/SearchView.swift b/KuringLite/Search/SearchView.swift new file mode 100644 index 0000000..0340781 --- /dev/null +++ b/KuringLite/Search/SearchView.swift @@ -0,0 +1,57 @@ +// +// SearchView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct SearchView: View { + @StateObject private var engine = SearchEngine() + + var body: some View { + VStack { + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(ColorSet.green.color) + + TextField("검색어를 입력해주세요", text: $engine.searchText) + } + .padding(.horizontal, 20) + .frame(height: 40) + .background( + RoundedRectangle(cornerRadius: 20) + .stroke(ColorSet.green.color, lineWidth: 1) + ) + .padding(16) + + ScrollView(showsIndicators: false) { + LazyVStack { + HStack(spacing: 10) { + ForEach(Searcher.SearchType.allCases, id: \.self) { + SearchTypeColumn( + engine: engine, + searchType: $0 + ) + } + } + + SearchedResultList(engine: engine) + } + } + } + .onAppear { engine.start() } + .onChange(of: engine.searchText) { newValue in + engine.search() + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .principal) { + Text("🔍 검색하기") + } + } + } +} diff --git a/KuringLite/Search/SearchedNoticeRow.swift b/KuringLite/Search/SearchedNoticeRow.swift new file mode 100644 index 0000000..cb3d589 --- /dev/null +++ b/KuringLite/Search/SearchedNoticeRow.swift @@ -0,0 +1,52 @@ +// +// SearchedNoticeRow.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct SearchedNoticeRow: View { + let notice: Notice + + var dateText: String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM.dd" + return formatter.string(from: Date(timeIntervalSince1970: notice.postedAt)) + } + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + // MARK: Title + Text(notice.subject) + .font(.subheadline) + .lineLimit(2) + + HStack { + // MARK: Date + Text(dateText) + .foregroundColor(ColorSet.Label.secondary.color) + .font(.caption) + + // MARK: Tag + ForEach(notice.tags, id: \.self) { + Text($0) + .frame(height: 16) + .foregroundColor(ColorSet.Background.primary.color) + .font(.caption) + .padding(.horizontal, 4) + .background( + RoundedRectangle(cornerRadius: 8) + .foregroundColor(ColorSet.secondaryGray.color) + ) + + } + } + } + .padding(.vertical, 8) + .padding(.horizontal, 20) + } +} diff --git a/KuringLite/Search/SearchedResultList.swift b/KuringLite/Search/SearchedResultList.swift new file mode 100644 index 0000000..bee04a2 --- /dev/null +++ b/KuringLite/Search/SearchedResultList.swift @@ -0,0 +1,29 @@ +// +// SearchedResultList.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI + +struct SearchedResultList: View { + @ObservedObject var engine: SearchEngine + + var body: some View { + LazyVStack(alignment: .leading) { + switch engine.currentType { + case .notice: + ForEach(engine.noticeResult) { notice in + SearchedNoticeRow(notice: notice) + } + case .staff: + ForEach(engine.staffResult, id: \.email) { staff in + SearchedStaffRow(staff: staff) + } + default: + EmptyView() + } + } + } +} diff --git a/KuringLite/Search/SearchedStaffRow.swift b/KuringLite/Search/SearchedStaffRow.swift new file mode 100644 index 0000000..cdd77d9 --- /dev/null +++ b/KuringLite/Search/SearchedStaffRow.swift @@ -0,0 +1,28 @@ +// +// SearchedStaffRow.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +struct SearchedStaffRow: View { + let staff: Staff + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text(staff.name) + .font(.subheadline) + .foregroundColor(ColorSet.Label.primary.color) + + Text("\(staff.deptName) · \(staff.collegeName)") + .font(.caption) + .foregroundColor(ColorSet.Label.secondary.color) + } + .padding(.vertical, 8) + .padding(.horizontal, 20) + } +} diff --git a/KuringLite/Settings/SettingsView.swift b/KuringLite/Settings/SettingsView.swift new file mode 100644 index 0000000..8bc09ed --- /dev/null +++ b/KuringLite/Settings/SettingsView.swift @@ -0,0 +1,71 @@ +// +// SettingsView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringCommons + +struct SettingsView: View { + @State private var showsSubscription: Bool = false + @State private var showsFeedbackView: Bool = false + + var body: some View { + List { + Section { + Button(action: { showsSubscription.toggle() }) { + HStack { + Text("🗞 공지 구독하기") + .font(.subheadline) + + Spacer() + + Image(systemName: "chevron.right") + .foregroundColor(ColorSet.Label.tertiary.color) + } + } + .foregroundColor(ColorSet.Label.primary.color) + } header: { + Text("공지구독") + } + + Section { + Button(action: { showsFeedbackView.toggle() }) { + HStack { + Text("💬 피드백 보내기") + .font(.subheadline) + + Spacer() + + Image(systemName: "chevron.right") + .foregroundColor(ColorSet.Label.tertiary.color) + } + } + .foregroundColor(ColorSet.Label.primary.color) + } header: { + Text("피드백") + } + + Section { + ForEach(SwiftPackage.allUsed) { + SwiftPackageRow(package: $0) + } + } header: { + Text("사용된 스위프트 패키지") + } + } + .sheet(isPresented: $showsSubscription) { + SubscriptionView() + } + .sheet(isPresented: $showsFeedbackView) { + FeedbackView() + } + .toolbar { + ToolbarItem(placement: .principal) { + Text("👋 더보기") + } + } + } +} diff --git a/KuringLite/Settings/SwiftPackageRow.swift b/KuringLite/Settings/SwiftPackageRow.swift new file mode 100644 index 0000000..d4e717d --- /dev/null +++ b/KuringLite/Settings/SwiftPackageRow.swift @@ -0,0 +1,48 @@ +// +// SwiftPackageRow.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringCommons + +struct SwiftPackage: Identifiable { + var id: String { name } + let name: String + let urlString: String + let version: String + + static let allUsed: [SwiftPackage] = [ + .init(name: "KuringSDK", urlString: "https://github.com/KU-Stacks/kuring-sdk-ios-spm", version: "1.2.1"), + .init(name: "KuringCommons", urlString: "https://github.com/KU-Stacks/kuring-ios-commons", version: "1.0.2"), + .init(name: "Lottie", urlString: "https://github.com/airbnb/lottie-ios", version: "3.3.0"), + .init(name: "Starscream", urlString: "https://github.com/daltoniam/Starscream", version: "4.0.4"), + ] +} + + +struct SwiftPackageRow: View { + let package: SwiftPackage + var body: some View { + ZStack { + Link(destination: URL(string: package.urlString)!) { + EmptyView() + } + .opacity(0) + + HStack { + Text("🏛 \(package.name)") + .font(.subheadline) + .foregroundColor(ColorSet.Label.primary.color) + + Spacer() + + Text(package.version) + .font(.caption) + .foregroundColor(ColorSet.Label.secondary.color) + } + } + } +} diff --git a/KuringLite/Subscription/Subscription.swift b/KuringLite/Subscription/Subscription.swift new file mode 100644 index 0000000..a793351 --- /dev/null +++ b/KuringLite/Subscription/Subscription.swift @@ -0,0 +1,50 @@ +// +// Subscription.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI +import KuringSDK +import KuringCommons + +public class Subscription: ObservableObject { + @Published var selectedNoticeTypes: [NoticeType] = NoticeType.allCases.filter { $0.isSubscribed } + @Published var noticeTypes: [NoticeType] = NoticeType.allCases + @Published var isUpdatable: Bool = false + @Published var isSaved: Bool = false + + + public func select(noticeType: NoticeType) { + + if isSelecting(noticeType) { + selectedNoticeTypes.removeAll { $0 == noticeType } + } else { + selectedNoticeTypes.append(noticeType) + } + isUpdatable = true + } + + public func isSelecting(_ noticeType: NoticeType) -> Bool { + selectedNoticeTypes.contains(noticeType) + } + + public func save() { + let subscriptionList = selectedNoticeTypes.compactMap { $0.stringValue } + // Remote + Kuring.updateSubscription(categories: subscriptionList) { _ in } + // Local + Kuring.categoryStrings = subscriptionList + + isSaved = true + isUpdatable = false + } + + public func reset() { + self.selectedNoticeTypes = NoticeType.allCases.filter { $0.isSubscribed } + + isUpdatable = false + } +} + diff --git a/KuringLite/Subscription/SubscriptionSelection.swift b/KuringLite/Subscription/SubscriptionSelection.swift new file mode 100644 index 0000000..495f51a --- /dev/null +++ b/KuringLite/Subscription/SubscriptionSelection.swift @@ -0,0 +1,64 @@ +// +// SubscriptionSelection.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI + +struct SubscriptionSelection: View { + @ObservedObject var subscription: Subscription + + let columns = [ + GridItem(.adaptive(minimum: 60)), + GridItem(.adaptive(minimum: 60)), + GridItem(.adaptive(minimum: 60)) + ] + + var body: some View { + VStack { + // 카테고리 그룹 버튼 + HStack { + Text("대학 공지 카테고리") + .font(.subheadline.weight(.semibold)) + + Spacer() + } + + // 구독 가능 카테고리 목록 + LazyVGrid(columns: columns) { + ForEach(subscription.noticeTypes, id: \.self) { noticeType in + Button { + subscription.select(noticeType: noticeType) + } label: { + RoundedRectangle(cornerRadius: 8) + .stroke(Color.white, lineWidth: 1) + .frame(height: 32) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(subscription.isSelecting(noticeType) ? .white : .clear) + ) + .overlay( + Text(noticeType.koreanValue) + .foregroundColor( + subscription.isSelecting(noticeType) + ? Color(red: 61 / 255, green: 189 / 255, blue: 128 / 255) + : .white + ) + ) + } + } + } + } + .padding(.vertical, 15) + .foregroundColor(.white) + .padding(.horizontal, 16) + .background( + Color.white.opacity(0.2) + .clipShape(RoundedRectangle(cornerRadius: 16)) + ) + .padding(.horizontal, 38) + .clipped() + } +} diff --git a/KuringLite/Subscription/SubscriptionView.swift b/KuringLite/Subscription/SubscriptionView.swift new file mode 100644 index 0000000..d695599 --- /dev/null +++ b/KuringLite/Subscription/SubscriptionView.swift @@ -0,0 +1,67 @@ +// +// SubscriptionView.swift +// KuringLite +// +// Created by Jaesung Lee on 2022/06/05. +// + +import SwiftUI + +struct SubscriptionView: View { + @Environment(\.presentationMode) var presentationMode + @StateObject private var subscription = Subscription() + + var body: some View { + NavigationView { + ZStack(alignment: .top) { + Color(red: 61 / 255, green: 189 / 255, blue: 128 / 255) + .ignoresSafeArea(.all) + + LazyVStack { + Image(systemName: "bell") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 48, height: 48) + .clipped() + .padding(.bottom, 24) + + Text("어떤 알림들을 받아보시겠습니까?\n알림 받고 싶은 카테고리를 선택해주세요.") + .font(.body.weight(.semibold)) + .padding(.bottom, 20) + + SubscriptionSelection(subscription: subscription) + } + + } + .foregroundColor(.white) + .toolbar { + ToolbarItem(placement: .principal) { + Text("푸시 알림 설정") + .font(.body.weight(.semibold)) + .foregroundColor(.white) + } + + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button(action: subscription.reset) { + Image(systemName: "arrow.uturn.left") + } + .foregroundColor(.white) + .opacity(subscription.isUpdatable ? 1 : 0.5) + .disabled(!subscription.isUpdatable) + + Button(action: save) { + Image(systemName: "checkmark") + } + .foregroundColor(.white) + .opacity(subscription.isUpdatable ? 1 : 0.5) + .disabled(!subscription.isUpdatable) + } + } + } + } + + func save() { + subscription.save() + presentationMode.wrappedValue.dismiss() + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ae105f --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# 쿠링Lite + +쿠링 Lite 는 모두가 자유롭게 쿠링 기능을 체험하고 기능 개발에 참여해볼 수 있는 오픈 프로젝트 입니다. + +## 개요 + +쿠링 Lite 는 실제 서비스 되고 있는 **쿠링 - 건국대학교 공지앱** 의 주요 기능 일부만을 모아둔 오픈된 앱 프로젝트 입니다. 모두가 소스코드를 확인할 수 있으며, Xcode 및 연결된 실제 디바이스에서 앱을 구동시켜볼 수 있습니다. + +앱 프로젝트의 개발환경은 다음과 같습니다. + +- SwiftUI 기반 +- iOS 14 이상 +- Xcode 13.1 이상 +- Swift 5.6 이상 + +앱 프로젝트에 사용되는 스위프트 패키지는 다음과 같습니다. + +- KuringSDK 1.2.1 이상 +- KuringCommons 1.0.2 이상 +- Lottie 3.3.0 +- Starscream 4.0.4 + +앱 프로젝트에서 제공하는 기능은 다음과 같습니다. + +- 공지 카테고리 목록 +- 공지 리스트 가져오기 +- 공지 구독하기 +- 공지 / 교직원 검색하기 +- 피드백 전송하기 + +### KuringSDK + +**KuringSDK**(이하 쿠링SDK)는 쿠링 앱 기능을 모아둔 쿠링 iOS 만의 소프트웨어 개발 키트 입니다. 로컬 저장소 관리, API 통신 관리, 모델 정의, 앱 기능 구현에 필요한 다양한 퍼블릭 인터페이스 등 여러가지 개발 도구를 제공합니다. + +[👉🏼 깃헙 링크 바로가기](https://github.com/KU-Stacks/kuring-sdk-ios-spm) + +### KuringCommons + +**KuringCommons**는 쿠링 앱에 사용되는 공통 UI 요소와 로깅에 대한 가이드를 제공하는 쿠링 iOS 만의 소프트웨어 개발 키트 입니다. + +[👉🏼 깃헙 링크 바로가기](https://github.com/KU-Stacks/kuring-ios-commons) + + +## 이 프로젝트의 개발자 + +이 오픈 프로젝트는 쿠링 iOS 팀에 의해 운영관리 됩니다.