├── .vscode ├── settings.json └── launch.json ├── screenshot ├── Feeds.png ├── Home.png ├── Repo.png ├── Stars.png ├── User.png ├── Pamphlet.png ├── RepoDir.png ├── RssAtom.png ├── RssFeeds.png ├── UserInfo.png ├── appstyle.png ├── EditIssue.png ├── IssueDetail.png ├── RepoBranch.png ├── RepoIssues.png ├── IssueComments.png ├── SearchHistory.png ├── SearchResults.png ├── UserInteract.png ├── LocalDeveloper.png ├── PamphletResource.png ├── RepoBranchCreate.png ├── GithubTrendingRepo.png └── GithubTrendingDeveloper.png ├── DJGithub ├── Assets.xcassets │ ├── Contents.json │ ├── Pixel │ │ ├── Contents.json │ │ ├── p1.imageset │ │ │ ├── p1-1.png │ │ │ ├── p1.png │ │ │ └── Contents.json │ │ ├── p10.imageset │ │ │ ├── p10.png │ │ │ ├── p10-1.png │ │ │ └── Contents.json │ │ ├── p11.imageset │ │ │ ├── p11.png │ │ │ ├── p11-1.png │ │ │ └── Contents.json │ │ ├── p12.imageset │ │ │ ├── p12.png │ │ │ ├── p12-1.png │ │ │ └── Contents.json │ │ ├── p13.imageset │ │ │ ├── p13.png │ │ │ ├── p13-1.png │ │ │ └── Contents.json │ │ ├── p14.imageset │ │ │ ├── p14.png │ │ │ ├── p14-1.png │ │ │ └── Contents.json │ │ ├── p15.imageset │ │ │ ├── p15.png │ │ │ ├── p15-1.png │ │ │ └── Contents.json │ │ ├── p16.imageset │ │ │ ├── p16.png │ │ │ ├── p16-1.png │ │ │ └── Contents.json │ │ ├── p17.imageset │ │ │ ├── p17.png │ │ │ ├── p17-1.png │ │ │ └── Contents.json │ │ ├── p18.imageset │ │ │ ├── p18.png │ │ │ ├── p18-1.png │ │ │ └── Contents.json │ │ ├── p19.imageset │ │ │ ├── p19.png │ │ │ ├── p19-1.png │ │ │ └── Contents.json │ │ ├── p2.imageset │ │ │ ├── p2-1.png │ │ │ ├── p2.png │ │ │ └── Contents.json │ │ ├── p20.imageset │ │ │ ├── p20.png │ │ │ ├── p20-1.png │ │ │ └── Contents.json │ │ ├── p21.imageset │ │ │ ├── p21.png │ │ │ ├── p21-1.png │ │ │ └── Contents.json │ │ ├── p22.imageset │ │ │ ├── p22.png │ │ │ ├── p22-1.png │ │ │ └── Contents.json │ │ ├── p23.imageset │ │ │ ├── p23.png │ │ │ ├── p23-1.png │ │ │ └── Contents.json │ │ ├── p24.imageset │ │ │ ├── p24.png │ │ │ ├── p24-1.png │ │ │ └── Contents.json │ │ ├── p25.imageset │ │ │ ├── p25.png │ │ │ ├── p25-1.png │ │ │ └── Contents.json │ │ ├── p26.imageset │ │ │ ├── p26.png │ │ │ ├── p26-1.png │ │ │ └── Contents.json │ │ ├── p27.imageset │ │ │ ├── p27.png │ │ │ ├── p27-1.png │ │ │ └── Contents.json │ │ ├── p28.imageset │ │ │ ├── p28.png │ │ │ ├── p28-1.png │ │ │ └── Contents.json │ │ ├── p29.imageset │ │ │ ├── p29.png │ │ │ ├── p29-1.png │ │ │ └── Contents.json │ │ ├── p3.imageset │ │ │ ├── p3-1.png │ │ │ ├── p3.png │ │ │ └── Contents.json │ │ ├── p30.imageset │ │ │ ├── p30.png │ │ │ ├── p30-1.png │ │ │ └── Contents.json │ │ ├── p4.imageset │ │ │ ├── p4-1.png │ │ │ ├── p4.png │ │ │ └── Contents.json │ │ ├── p5.imageset │ │ │ ├── p5-1.png │ │ │ ├── p5.png │ │ │ └── Contents.json │ │ ├── p6.imageset │ │ │ ├── p6-1.png │ │ │ ├── p6.png │ │ │ └── Contents.json │ │ ├── p7.imageset │ │ │ ├── p7-1.png │ │ │ ├── p7.png │ │ │ └── Contents.json │ │ ├── p8.imageset │ │ │ ├── p8-1.png │ │ │ ├── p8.png │ │ │ └── Contents.json │ │ └── p9.imageset │ │ │ ├── p9-1.png │ │ │ ├── p9.png │ │ │ └── Contents.json │ ├── rss.imageset │ │ ├── rss.png │ │ └── Contents.json │ ├── book.imageset │ │ ├── book.png │ │ └── Contents.json │ ├── plus.imageset │ │ ├── plus.png │ │ ├── plus 1.png │ │ ├── plus 2.png │ │ └── Contents.json │ ├── star.imageset │ │ ├── star.png │ │ └── Contents.json │ ├── user.imageset │ │ ├── user.png │ │ └── Contents.json │ ├── arrow.imageset │ │ ├── arrow.png │ │ └── Contents.json │ ├── check.imageset │ │ ├── check.png │ │ └── Contents.json │ ├── close.imageset │ │ ├── close.png │ │ └── Contents.json │ ├── coding.imageset │ │ ├── coding.png │ │ └── Contents.json │ ├── email.imageset │ │ ├── email.png │ │ └── Contents.json │ ├── group.imageset │ │ ├── group.png │ │ └── Contents.json │ ├── issue.imageset │ │ ├── issue.png │ │ └── Contents.json │ ├── link.imageset │ │ ├── attach-1.png │ │ ├── attach.png │ │ └── Contents.json │ ├── people.imageset │ │ ├── people.png │ │ └── Contents.json │ ├── person.imageset │ │ ├── person.png │ │ └── Contents.json │ ├── reload.imageset │ │ ├── reload.png │ │ └── Contents.json │ ├── search.imageset │ │ ├── search.png │ │ └── Contents.json │ ├── stared.imageset │ │ ├── stared.png │ │ └── Contents.json │ ├── weibo.imageset │ │ ├── weibo.png │ │ └── Contents.json │ ├── find.imageset │ │ ├── search (1).png │ │ └── Contents.json │ ├── message.imageset │ │ ├── message.png │ │ └── Contents.json │ ├── setting.imageset │ │ ├── setting.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── icon-29.png │ │ ├── icon-40.png │ │ ├── icon-76.png │ │ ├── icon-1024.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-76@2x.png │ │ ├── icon-20-ipad.png │ │ ├── icon-29-ipad.png │ │ ├── icon-83.5@2x.png │ │ ├── icon-20@2x-ipad.png │ │ └── icon-29@2x-ipad.png │ ├── arrow-24.imageset │ │ ├── arrow-24.png │ │ └── Contents.json │ ├── document.imageset │ │ ├── document.png │ │ └── Contents.json │ ├── github.imageset │ │ ├── github (1).png │ │ └── Contents.json │ ├── location.imageset │ │ ├── location.png │ │ └── Contents.json │ ├── rss-feed.imageset │ │ ├── rss-feed.png │ │ └── Contents.json │ ├── directory.imageset │ │ ├── directory.png │ │ └── Contents.json │ ├── git-merge.imageset │ │ ├── git-merge.png │ │ └── Contents.json │ ├── git-branch.imageset │ │ ├── git-branch.png │ │ └── Contents.json │ ├── icons-erase.imageset │ │ ├── icons-erase.png │ │ └── Contents.json │ ├── repo_reload.imageset │ │ ├── repo_reload.png │ │ └── Contents.json │ ├── search_sort.imageset │ │ ├── search_sort.png │ │ └── Contents.json │ ├── pull-request.imageset │ │ ├── pull-request.png │ │ └── Contents.json │ ├── triangle.imageset │ │ ├── xiaoshanjiao@2x.png │ │ ├── xiaoshanjiao@3x.png │ │ └── Contents.json │ └── AccentColor.colorset │ │ └── Contents.json ├── Pamphlet │ ├── Extension │ │ └── Array+Ext.swift │ ├── ViewController │ │ └── PamphletContentViewController.swift │ ├── Model │ │ ├── PamphletResourceModel.swift │ │ └── PamphletSimpleModel.swift │ ├── Utils │ │ └── URLRouterModel.swift │ ├── Resources │ │ ├── PamphletSectionContentResource.json │ │ ├── Data │ │ │ ├── 新鲜事 │ │ │ │ ├── D-周报.json │ │ │ │ └── D-播客.json │ │ │ └── Swift │ │ │ │ ├── D-Swift 官方.json │ │ │ │ ├── D-SwiftUI.json │ │ │ │ └── D-Swift 技术.json │ │ └── lib-Concurrency.json │ └── View │ │ ├── PamphletResourceCell.swift │ │ └── PamphletSimpleCell.swift ├── DJFoundation │ ├── Protocol │ │ ├── DJCodable.swift │ │ ├── URLConvertible.swift │ │ └── DefaultValue.swift │ ├── Common │ │ └── Error.swift │ └── Utils │ │ ├── Utils.swift │ │ └── DJParser.swift ├── Github │ ├── Model │ │ ├── Readme.swift │ │ ├── Config.swift │ │ ├── RepoPullFile.swift │ │ ├── RepoBranch.swift │ │ ├── StatusModel.swift │ │ ├── SearchList.swift │ │ ├── LocalRepo.swift │ │ ├── Organization.swift │ │ ├── IssueDetail.swift │ │ ├── NextPageModel.swift │ │ ├── RepoContent.swift │ │ ├── SqlCountModel.swift │ │ ├── RepoPull.swift │ │ ├── Language.swift │ │ ├── RepoBranchCommit.swift │ │ ├── Issue.swift │ │ ├── UserViewer.swift │ │ └── Repo.swift │ ├── Utils │ │ ├── UnChecked.swift │ │ ├── Parse │ │ │ ├── Parser.swift │ │ │ ├── DJJSONParser.swift │ │ │ ├── RegularParser.swift │ │ │ └── DateParser.swift │ │ ├── NotificationKeys.swift │ │ ├── Color.swift │ │ ├── GithubTrending │ │ │ ├── GithubTrendingDeveloper.swift │ │ │ ├── GithubTrendingRepo.swift │ │ │ └── GithubTrendingItemsManager.swift │ │ ├── DJObserverable.swift │ │ ├── ImageCoder.swift │ │ ├── LanguageManager.swift │ │ └── ConfigManager.swift │ ├── AppStyle │ │ ├── AppStyle.swift │ │ └── AppStyleUtility.swift │ ├── Extension │ │ ├── Base.swift │ │ ├── Bundle+Ext.swift │ │ ├── Int+Ext.swift │ │ ├── Bool+Ext.swift │ │ ├── String+File.swift │ │ ├── UIView+Ext.swift │ │ ├── UIColor+Ext.swift │ │ ├── UIScrollView+Refresh.swift │ │ ├── URL+Ext.swift │ │ ├── Result.swift │ │ ├── UIViewController+Share.swift │ │ ├── UIApplication+Ext.swift │ │ ├── Color+Ext.swift │ │ ├── Default+Ext.swift │ │ ├── UIImage+Ext.swift │ │ ├── UIView+Loading.swift │ │ ├── JSONSerialization+Ext.swift │ │ └── String+Ext.swift │ ├── ViewController │ │ ├── Developer │ │ │ ├── LocalDataViewController.swift │ │ │ ├── LocalDataManager.swift │ │ │ └── View │ │ │ │ ├── LocalDeveloperSectionHeaderView.swift │ │ │ │ └── LocalRepoCell.swift │ │ ├── GithubTrending │ │ │ ├── GithubTrendingRepoViewController.swift │ │ │ ├── Model │ │ │ │ └── GithubTrendingType.swift │ │ │ ├── LoaderView.swift │ │ │ ├── GithubTrendingReposRepresentation.swift │ │ │ ├── GithubTrendingManager.swift │ │ │ └── GithubTrendingViewController.swift │ │ ├── Home │ │ │ ├── DevelopersTrendingView.swift │ │ │ ├── HomeHorizontalItemsView.swift │ │ │ ├── GithubTrendingItemView.swift │ │ │ ├── RssFeedItem.swift │ │ │ ├── ReposTrendingView.swift │ │ │ └── ViewModel │ │ │ │ └── HomeViewModel.swift │ │ ├── Authorization │ │ │ └── AuthorizationViewManager.swift │ │ ├── User │ │ │ ├── ViewModel │ │ │ │ ├── Setting.swift │ │ │ │ ├── SettingType.swift │ │ │ │ └── UserViewModel.swift │ │ │ ├── Views │ │ │ │ ├── UserContrbutionItemCell.swift │ │ │ │ ├── UserContributionCell.swift │ │ │ │ ├── UserInfoCell.swift │ │ │ │ └── UserCell.swift │ │ │ ├── SettingViewController.swift │ │ │ └── SettingView.swift │ │ ├── Repo │ │ │ └── Views │ │ │ │ ├── RepoBranchCommitCell.swift │ │ │ │ ├── RepoHeaderView.swift │ │ │ │ ├── RepoListView.swift │ │ │ │ ├── RepoFooterView.swift │ │ │ │ └── RepoBranchCommitFileCell.swift │ │ ├── Feed │ │ │ ├── Views │ │ │ │ └── RssFeedsItemView.swift │ │ │ └── ViewModel │ │ │ │ └── FeedViewModel.swift │ │ └── Search │ │ │ └── Views │ │ │ └── SearchWordHeaderView.swift │ ├── Store │ │ ├── LocalRepoManager.swift │ │ ├── FeedManager.swift │ │ ├── LocalUserManager.swift │ │ └── SearchWordManager.swift │ ├── Source │ │ └── Language.xcdatamodeld │ │ │ └── Language.xcdatamodel │ │ │ └── contents │ └── Widget │ │ ├── LanguageGradientView.swift │ │ ├── NavigationController.swift │ │ └── AvatarImageView.swift ├── SwiftUI │ ├── Extension │ │ └── Image+Ext.swift │ └── Modifier │ │ └── ViewDidLoadModifier.swift ├── Info.plist ├── AppDelegate.swift ├── LoadingViewerViewController.swift └── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── .swiftlint.yml ├── DJGithub.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── polarisdev.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ ├── polarisdev.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── jiqinqiang.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── .gitignore ├── DJGithubUITests ├── DJGithubUITestsLaunchTests.swift └── DJGithubUITests.swift └── DJGithubTests └── DJGithubTests.swift /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /screenshot/Feeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/Feeds.png -------------------------------------------------------------------------------- /screenshot/Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/Home.png -------------------------------------------------------------------------------- /screenshot/Repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/Repo.png -------------------------------------------------------------------------------- /screenshot/Stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/Stars.png -------------------------------------------------------------------------------- /screenshot/User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/User.png -------------------------------------------------------------------------------- /screenshot/Pamphlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/Pamphlet.png -------------------------------------------------------------------------------- /screenshot/RepoDir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RepoDir.png -------------------------------------------------------------------------------- /screenshot/RssAtom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RssAtom.png -------------------------------------------------------------------------------- /screenshot/RssFeeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RssFeeds.png -------------------------------------------------------------------------------- /screenshot/UserInfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/UserInfo.png -------------------------------------------------------------------------------- /screenshot/appstyle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/appstyle.png -------------------------------------------------------------------------------- /screenshot/EditIssue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/EditIssue.png -------------------------------------------------------------------------------- /screenshot/IssueDetail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/IssueDetail.png -------------------------------------------------------------------------------- /screenshot/RepoBranch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RepoBranch.png -------------------------------------------------------------------------------- /screenshot/RepoIssues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RepoIssues.png -------------------------------------------------------------------------------- /screenshot/IssueComments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/IssueComments.png -------------------------------------------------------------------------------- /screenshot/SearchHistory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/SearchHistory.png -------------------------------------------------------------------------------- /screenshot/SearchResults.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/SearchResults.png -------------------------------------------------------------------------------- /screenshot/UserInteract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/UserInteract.png -------------------------------------------------------------------------------- /screenshot/LocalDeveloper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/LocalDeveloper.png -------------------------------------------------------------------------------- /screenshot/PamphletResource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/PamphletResource.png -------------------------------------------------------------------------------- /screenshot/RepoBranchCreate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/RepoBranchCreate.png -------------------------------------------------------------------------------- /screenshot/GithubTrendingRepo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/GithubTrendingRepo.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /screenshot/GithubTrendingDeveloper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/screenshot/GithubTrendingDeveloper.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/rss.imageset/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/rss.imageset/rss.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/book.imageset/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/book.imageset/book.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/plus.imageset/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/plus.imageset/plus.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/user.imageset/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/user.imageset/user.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p1.imageset/p1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p1.imageset/p1-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p1.imageset/p1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p1.imageset/p1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p10.imageset/p10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p10.imageset/p10.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p11.imageset/p11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p11.imageset/p11.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p12.imageset/p12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p12.imageset/p12.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p13.imageset/p13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p13.imageset/p13.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p14.imageset/p14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p14.imageset/p14.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p15.imageset/p15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p15.imageset/p15.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p16.imageset/p16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p16.imageset/p16.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p17.imageset/p17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p17.imageset/p17.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p18.imageset/p18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p18.imageset/p18.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p19.imageset/p19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p19.imageset/p19.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p2.imageset/p2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p2.imageset/p2-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p2.imageset/p2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p2.imageset/p2.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p20.imageset/p20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p20.imageset/p20.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p21.imageset/p21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p21.imageset/p21.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p22.imageset/p22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p22.imageset/p22.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p23.imageset/p23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p23.imageset/p23.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p24.imageset/p24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p24.imageset/p24.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p25.imageset/p25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p25.imageset/p25.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p26.imageset/p26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p26.imageset/p26.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p27.imageset/p27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p27.imageset/p27.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p28.imageset/p28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p28.imageset/p28.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p29.imageset/p29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p29.imageset/p29.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p3.imageset/p3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p3.imageset/p3-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p3.imageset/p3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p3.imageset/p3.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p30.imageset/p30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p30.imageset/p30.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p4.imageset/p4-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p4.imageset/p4-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p4.imageset/p4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p4.imageset/p4.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p5.imageset/p5-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p5.imageset/p5-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p5.imageset/p5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p5.imageset/p5.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p6.imageset/p6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p6.imageset/p6-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p6.imageset/p6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p6.imageset/p6.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p7.imageset/p7-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p7.imageset/p7-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p7.imageset/p7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p7.imageset/p7.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p8.imageset/p8-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p8.imageset/p8-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p8.imageset/p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p8.imageset/p8.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p9.imageset/p9-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p9.imageset/p9-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p9.imageset/p9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p9.imageset/p9.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/arrow.imageset/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/arrow.imageset/arrow.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/check.imageset/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/check.imageset/check.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/close.imageset/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/close.imageset/close.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/coding.imageset/coding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/coding.imageset/coding.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/email.imageset/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/email.imageset/email.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/group.imageset/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/group.imageset/group.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/issue.imageset/issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/issue.imageset/issue.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/link.imageset/attach-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/link.imageset/attach-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/link.imageset/attach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/link.imageset/attach.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/people.imageset/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/people.imageset/people.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/person.imageset/person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/person.imageset/person.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/plus.imageset/plus 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/plus.imageset/plus 1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/plus.imageset/plus 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/plus.imageset/plus 2.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/reload.imageset/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/reload.imageset/reload.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/search.imageset/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/search.imageset/search.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/stared.imageset/stared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/stared.imageset/stared.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/weibo.imageset/weibo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/weibo.imageset/weibo.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p10.imageset/p10-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p10.imageset/p10-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p11.imageset/p11-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p11.imageset/p11-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p12.imageset/p12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p12.imageset/p12-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p13.imageset/p13-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p13.imageset/p13-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p14.imageset/p14-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p14.imageset/p14-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p15.imageset/p15-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p15.imageset/p15-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p16.imageset/p16-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p16.imageset/p16-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p17.imageset/p17-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p17.imageset/p17-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p18.imageset/p18-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p18.imageset/p18-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p19.imageset/p19-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p19.imageset/p19-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p20.imageset/p20-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p20.imageset/p20-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p21.imageset/p21-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p21.imageset/p21-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p22.imageset/p22-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p22.imageset/p22-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p23.imageset/p23-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p23.imageset/p23-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p24.imageset/p24-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p24.imageset/p24-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p25.imageset/p25-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p25.imageset/p25-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p26.imageset/p26-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p26.imageset/p26-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p27.imageset/p27-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p27.imageset/p27-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p28.imageset/p28-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p28.imageset/p28-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p29.imageset/p29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p29.imageset/p29-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p30.imageset/p30-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/Pixel/p30.imageset/p30-1.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/find.imageset/search (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/find.imageset/search (1).png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/message.imageset/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/message.imageset/message.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/setting.imageset/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/setting.imageset/setting.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/arrow-24.imageset/arrow-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/arrow-24.imageset/arrow-24.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/document.imageset/document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/document.imageset/document.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/github.imageset/github (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/github.imageset/github (1).png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/location.imageset/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/location.imageset/location.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/rss-feed.imageset/rss-feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/rss-feed.imageset/rss-feed.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/directory.imageset/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/directory.imageset/directory.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/git-merge.imageset/git-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/git-merge.imageset/git-merge.png -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | 2 | disabled_rules: # rule identifiers to exclude from running 3 | - identifier_name 4 | - shorthand_operator 5 | - force_cast 6 | - line_length 7 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/git-branch.imageset/git-branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/git-branch.imageset/git-branch.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/icons-erase.imageset/icons-erase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/icons-erase.imageset/icons-erase.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/repo_reload.imageset/repo_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/repo_reload.imageset/repo_reload.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/search_sort.imageset/search_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/search_sort.imageset/search_sort.png -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Extension/Array+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import UIKit 9 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/pull-request.imageset/pull-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/pull-request.imageset/pull-request.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/triangle.imageset/xiaoshanjiao@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/triangle.imageset/xiaoshanjiao@2x.png -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/triangle.imageset/xiaoshanjiao@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub/Assets.xcassets/triangle.imageset/xiaoshanjiao@3x.png -------------------------------------------------------------------------------- /DJGithub.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DJGithub/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 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Protocol/DJCodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJCodable.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/3. 6 | // 7 | 8 | import Foundation 9 | 10 | typealias DJCodable = Decodable & Encodable 11 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Readme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // readme.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/26. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Readme: DJCodable { 11 | var content: String? 12 | } 13 | -------------------------------------------------------------------------------- /DJGithub.xcodeproj/xcuserdata/polarisdev.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /DJGithub.xcodeproj/project.xcworkspace/xcuserdata/polarisdev.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyljqq/DJGithub/HEAD/DJGithub.xcodeproj/project.xcworkspace/xcuserdata/polarisdev.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DJGithub/Github/Utils/UnChecked.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnChecked.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2025/4/18. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UnChecked: @unchecked Sendable { 11 | var value: T 12 | } 13 | -------------------------------------------------------------------------------- /DJGithub/SwiftUI/Extension/Image+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Image+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/2/17. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Image { 11 | static let placeholderImage = Image(uiImage: UIImage(named: "person")!) 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /DJGithub/Github/config.plist 2 | .DS_Store 3 | build/ 4 | DJGithub/Github/Resource/config.json 5 | DJGithub.xcodeproj/project.xcworkspace/xcuserdata/jiqinqiang.xcuserdatad/UserInterfaceState.xcuserstate 6 | DJGithub.xcodeproj/project.xcworkspace/xcuserdata/polarisdev.xcuserdatad/UserInterfaceState.xcuserstate 7 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/Parse/Parser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/10. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Parsable { 11 | 12 | associatedtype DataType: DJCodable 13 | 14 | func parse(with data: Data?) async throws -> DataType? 15 | } 16 | -------------------------------------------------------------------------------- /DJGithub/Github/AppStyle/AppStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppStyle.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/30. 6 | // 7 | 8 | import UIKit 9 | 10 | enum AppStyle { 11 | case gray 12 | 13 | var color: UIColor { 14 | switch self { 15 | case .gray: return .lightGray 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DJGithub.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Base.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Untitled.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2025/4/18. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor func executeOnMainThread(_ block: @escaping () -> Void) { 11 | Task { 12 | await MainActor.run { 13 | block() 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Bundle+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/2/1. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | var appVersion: String { 12 | return (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Developer/LocalDataViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalDataViewController.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/7. 6 | // 7 | 8 | import UIKit 9 | 10 | class LocalDataViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Int+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/17. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Int { 11 | var toGitNum: String { 12 | if self > 1000 { 13 | return "\(String(format: "%.1f", Double(self) / 1000))k" 14 | } 15 | return "\(self)" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Bool+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bool+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/29. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bool { 11 | 12 | var followText: String { 13 | return self ? "UnFollow" : "Follow" 14 | } 15 | 16 | var starText: String { 17 | return self ? "UnStar" : "Star" 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/ViewController/PamphletContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PamphletSimpleContentViewController.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class PamphletContentViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/NotificationKeys.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationKeys.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/28. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NotificationKeys { 11 | static let createPullRequestKey = NSNotification.Name("DJCreatePullRequest") 12 | static let closePullRequestKey = NSNotification.Name("DJClosePullRequestKey") 13 | } 14 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/String+File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+File.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/4. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | var fileSuffix: String? { 12 | let arr = self.components(separatedBy: ".") 13 | guard arr.count > 1 else { 14 | return nil 15 | } 16 | return arr.last 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Config.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Config.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/29. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Config: DJCodable { 11 | var authorization: String = "" 12 | let userName: String = "" 13 | 14 | init() { 15 | 16 | } 17 | 18 | enum CodingKeys: String, CodingKey { 19 | case authorization 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/RepoPullFile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoPullFile.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/28. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RepoPullFile: DJCodable { 11 | var filename: String 12 | var status: String 13 | var additions: Int 14 | var deletions: Int 15 | var contentsUrl: String 16 | var blobUrl: String 17 | var patch: String? 18 | } 19 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/RepoBranch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoBranch.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/27. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RepoBranch: DJCodable { 11 | struct RepoBranchCommit: DJCodable { 12 | var sha: String 13 | var url: String 14 | } 15 | 16 | var name: String 17 | var protected: Bool 18 | 19 | var commit: RepoBranchCommit 20 | } 21 | -------------------------------------------------------------------------------- /DJGithub/Github/Store/LocalRepoManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalRepoManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct LocalRepoManager { 11 | 12 | static func loadRepos(with filename: String? = nil) async -> [LocalRepoGroup] { 13 | guard let filename else { return [] } 14 | return loadBundleJSONFile(filename) 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/rss.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rss.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "arrow.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/book.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "book.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/check.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "check.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/email.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "email.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/group.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "group.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/issue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "issue.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/weibo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "weibo.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/arrow-24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "arrow-24.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/coding.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "coding.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/document.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "document.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/find.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "search (1).png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/github.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "github (1).png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/location.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "location.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/message.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "message.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/people.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "people.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/reload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "reload.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/rss-feed.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rss-feed.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "search.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/directory.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "directory.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/git-branch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "git-branch.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/git-merge.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "git-merge.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIView+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | static var className: String { 12 | return String(describing: self.classForCoder()) 13 | } 14 | } 15 | 16 | extension UIViewController { 17 | static var className: String { 18 | return String(describing: self.classForCoder()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/icons-erase.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icons-erase.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/pull-request.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pull-request.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/repo_reload.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "repo_reload.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/search_sort.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "search_sort.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p1-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p1.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p2-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p2.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p3-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p3.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p4-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p4.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p5-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p5.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p6-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p6.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p7-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p7.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p8-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p8.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p9-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p9.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p10-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p10.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p11-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p11.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p12-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p12.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p13-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p13.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p14-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p14.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p15-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p15.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p16-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p16.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p17.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p17-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p17.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p18.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p18-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p18.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p19.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p19-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p19.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p20.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p20-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p20.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p21.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p21-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p21.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p22.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p22-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p22.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p23.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p23-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p23.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p24-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p24.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p25.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p25-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p25.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p26.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p26-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p26.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p27.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p27-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p27.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p28.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p28-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p28.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p29.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p29-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p29.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/Pixel/p30.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "p30-1.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "p30.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "attach.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "attach-1.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/StatusModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusModel.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/28. 6 | // 7 | 8 | import Foundation 9 | 10 | struct StatusModel: DJCodable { 11 | var statusCode: Int 12 | 13 | var isStatus204: Bool { 14 | return statusCode == 204 15 | } 16 | 17 | var isStatus201: Bool { 18 | return statusCode == 201 19 | } 20 | 21 | var isStatus404: Bool { 22 | return statusCode == 404 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Model/PamphletResourceModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PamphletResourceModel.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PamphletResourceModel: DJCodable { 11 | let title: String 12 | @Default var desc: String 13 | let url: String 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case title = "t" 17 | case url = "id" 18 | case desc = "d" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Common/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import Foundation 9 | 10 | enum DJError: Error { 11 | case invalidURL(String) 12 | case parseError(String) 13 | case requestError 14 | case statusCode(Int) 15 | case dataError 16 | case other(Error) 17 | case unknown 18 | 19 | static func map(_ error: Error) -> DJError { 20 | return (error as? DJError) ?? .other(error) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/person.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "person.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "star.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/stared.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "stared.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/user.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "user.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/setting.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "setting.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/SearchList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchList.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/1. 6 | // 7 | 8 | import Foundation 9 | 10 | enum SearchType { 11 | case users 12 | case repos 13 | 14 | var title: String { 15 | switch self { 16 | case .users: return "users" 17 | case .repos: return "repos" 18 | } 19 | } 20 | } 21 | 22 | struct SearchList: DJCodable { 23 | var totalCount: Int 24 | var incompleteResults: Bool 25 | var items: [T] 26 | } 27 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/GithubTrending/GithubTrendingRepoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingRepoViewController.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import UIKit 9 | 10 | class GithubTrendingRepoViewController: UIViewController { 11 | 12 | private let manager = GithubTrendingManager() 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | setUp() 18 | } 19 | 20 | private func setUp() { 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/DevelopersTrendingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DevelopersTrendingView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DevelopersTrendingView: View { 11 | var body: some View { 12 | Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) 13 | } 14 | } 15 | 16 | struct DevelopersTrendingView_Previews: PreviewProvider { 17 | static var previews: some View { 18 | DevelopersTrendingView() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIColor+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/17. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | static var randomColor: UIColor? { 12 | return randomHex.toColor 13 | } 14 | 15 | static var randomHex: String { 16 | let red = arc4random() % 256 17 | let green = arc4random() % 256 18 | let blue = arc4random() % 256 19 | return String(red, radix: 16) + String(green, radix: 16) + String(blue, radix: 16) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Utils/URLRouterModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLRouterModel.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | struct URLRouterModel { 11 | let scheme: String 12 | let host: String 13 | let path: String 14 | let type: HostVCType? 15 | let queryItems: [String: String] 16 | 17 | var transition: VCTransition = .push 18 | } 19 | 20 | extension URLRouterModel { 21 | @MainActor var vc: UIViewController? { 22 | return type?.getVC(with: queryItems) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | static let backgroundColor = UIColorFromRGB(0xf5f5f5) 14 | static let textColor = UIColorFromRGB(0x222222) 15 | static let textReadColor = UIColorFromRGB(0x4444) 16 | static let textGrayColor = UIColorFromRGB(0x666666) 17 | static let lightBlue = UIColorFromRGB(0x6495ED) 18 | static let lightGreen = UIColorFromRGB(0x90ee90) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/", 12 | "args": [], 13 | "cwd": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /DJGithub/Github/Utils/GithubTrending/GithubTrendingDeveloper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingDeveloper.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/20. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GithubTrendingDeveloper: Codable { 11 | 12 | let id = UUID() 13 | 14 | let avatarImageUrl: String 15 | let name: String 16 | let login: String 17 | let repoName: String 18 | let repoDesc: String 19 | 20 | enum CodingKeys: String, CodingKey { 21 | case avatarImageUrl, name, login, repoName, repoDesc 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/triangle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "xiaoshanjiao@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "xiaoshanjiao@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/LocalRepo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalRepo.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct LocalRepoGroup: DJCodable { 11 | var name: String 12 | var id: Int 13 | var repos: [LocalRepo] 14 | } 15 | 16 | struct LocalRepo: DJCodable { 17 | var id: String 18 | var des: String? 19 | var groupId: Int? 20 | 21 | var description: String { 22 | return self.des ?? "No description provided." 23 | } 24 | 25 | enum CodingKeys: String, CodingKey { 26 | case id, des 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/DJObserverable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJObserverable.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class DJObserverable { 11 | 12 | var value: T? { 13 | didSet { 14 | listener?(value) 15 | } 16 | } 17 | 18 | private var listener: ((T?) -> Void)? 19 | 20 | init(value: T? = nil) { 21 | self.value = value 22 | } 23 | 24 | func bind(_ listener: @escaping (T?) -> Void) { 25 | self.listener = listener 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DJGithub/Assets.xcassets/plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "plus 1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "plus 2.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "plus.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 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Authorization/AuthorizationViewManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationViewManager.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/28. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class AuthorizationViewManager: NSObject { 12 | 13 | @MainActor 14 | func addAuthorizationView(_ completionHandler: ((Bool) -> Void)? = nil) -> UIViewController { 15 | let authorizationView = AuthorizationView(completionHandler: completionHandler) 16 | return UIHostingController(rootView: authorizationView) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/GithubTrending/Model/GithubTrendingType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingType.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/20. 6 | // 7 | 8 | import Foundation 9 | 10 | enum GithubTrendingType { 11 | case repo(String) 12 | case developer(String) 13 | } 14 | 15 | extension GithubTrendingType { 16 | 17 | var urlString: String { 18 | switch self { 19 | case .repo(let path): return "https://github.com/trending" + path 20 | case .developer(let path): return "https://github.com/trending/developers" + path 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIScrollView+Refresh.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/18. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIScrollView { 11 | var contentInsetTop: CGFloat { 12 | if #available(iOS 11.0, *) { 13 | return contentInset.top + adjustedContentInset.top 14 | } else { 15 | return contentInset.top 16 | } 17 | } 18 | 19 | var contentInsetBottom: CGFloat { 20 | if #available(iOS 11.0, *) { 21 | return contentInset.bottom + adjustedContentInset.bottom 22 | } else { 23 | return contentInset.bottom 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/GithubTrending/LoaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoaderView: View { 11 | 12 | var tintColor: Color = .blue 13 | var scaleSize: CGFloat = 1.0 14 | 15 | var body: some View { 16 | ProgressView() 17 | .scaleEffect(scaleSize, anchor: .center) 18 | .progressViewStyle(CircularProgressViewStyle(tint: tintColor)) 19 | } 20 | } 21 | 22 | struct LoaderView_Previews: PreviewProvider { 23 | static var previews: some View { 24 | LoaderView() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/URL+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/20. 6 | // 7 | 8 | import Foundation 9 | 10 | extension URL { 11 | func appending(by queryItems: [String: String]) -> URL { 12 | guard !queryItems.isEmpty, var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: true) else { 13 | return self 14 | } 15 | urlComponents.queryItems = urlComponents.queryItems ?? [] + queryItems.map { URLQueryItem(name: $0, value: $1.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)) } 16 | return urlComponents.url != nil ? urlComponents.url! : self 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/Parse/DJJSONParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJJSONParser.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/10. 6 | // 7 | 8 | import Foundation 9 | 10 | class DJJSONParser: JSONDecoder, Parsable { 11 | 12 | typealias DataType = T 13 | 14 | override init() { 15 | super.init() 16 | self.keyDecodingStrategy = .convertFromSnakeCase 17 | } 18 | 19 | func parse(with data: Data?) async throws -> T? { 20 | guard let data = data else { return nil } 21 | do { 22 | return try self.decode(T.self, from: data) 23 | } catch { 24 | throw DJError.parseError("\(error)") 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Organization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Organization.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/11. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Organization: DJCodable { 11 | let avatarUrl: String 12 | let createdAt: String 13 | let login: String 14 | 15 | @Default var description: String 16 | @Default var name: String 17 | @Default var email: String 18 | @Default var location: String 19 | @Default var websiteUrl: String 20 | 21 | var membersWithRole: TotalCountModel 22 | var pinnableItems: TotalCountModel 23 | var pinnedItems: PinnedRepos 24 | } 25 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/PamphletSectionContentResource.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sectionName": "新鲜事", 4 | "items": [ 5 | { 6 | "title": "周报" 7 | }, 8 | { 9 | "title": "播客" 10 | } 11 | ], 12 | }, 13 | { 14 | "sectionName": "Swift", 15 | "items": [ 16 | { 17 | "title": "Swift 官方" 18 | }, 19 | { 20 | "title": "Swift 技术" 21 | }, 22 | { 23 | "title": "SwiftUI" 24 | } 25 | ], 26 | }, 27 | { 28 | "sectionName": "美术", 29 | "items": [ 30 | { 31 | "title": "图片工具" 32 | }, 33 | { 34 | "title": "美术素材" 35 | } 36 | ] 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/GithubTrending/GithubTrendingReposRepresentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingReposRepresentation.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct GithubTrendingReposRepresentation: UIViewControllerRepresentable { 12 | 13 | let userName: String 14 | let repoName: String 15 | 16 | func makeUIViewController(context: Context) -> some UIViewController { 17 | return RepoViewController(with: userName, repoName: repoName) 18 | } 19 | 20 | func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/ViewModel/Setting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Setting.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/1/31. 6 | // 7 | 8 | import Foundation 9 | 10 | @Observable class Setting { 11 | 12 | let dataSource: [SettingSection] = [ 13 | SettingSection(name: "Abillity", items: [.iCloud]), 14 | SettingSection(name: "Social", items: [.github, .email, .weibo]), 15 | SettingSection(name: "Other", items: [.version(Bundle.main.appVersion), .signUp]) 16 | ] 17 | 18 | } 19 | 20 | extension Setting { 21 | struct SettingSection { 22 | let id = UUID() 23 | 24 | let name: String 25 | let items: [SettingType] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/Views/UserContrbutionItemCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserContrbutionCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import UIKit 9 | 10 | class UserContrbutionItemCell: UICollectionViewCell { 11 | override init(frame: CGRect) { 12 | super.init(frame: frame) 13 | 14 | setUp() 15 | } 16 | 17 | func render(with item: UserContributionItem) { 18 | contentView.backgroundColor = item.color.toColor 19 | } 20 | 21 | func setUp() { 22 | contentView.backgroundColor = .white 23 | contentView.layer.cornerRadius = 2 24 | } 25 | 26 | required init?(coder: NSCoder) { 27 | super.init(coder: coder) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/Data/新鲜事/D-周报.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "t":"老司机技术周报", 4 | "id":"https://github.com/SwiftOldDriver/iOS-Weekly" 5 | }, 6 | { 7 | "t":"13週報", 8 | "id":"https://ethanhuang13.substack.com/" 9 | }, 10 | { 11 | "t":"摸鱼周报", 12 | "id":"https://zhangferry.com/" 13 | }, 14 | { 15 | "t":"iOS Dev Weekly", 16 | "id":"https://iosdevweekly.com/" 17 | }, 18 | { 19 | "t":"Swift Evolution Monthly", 20 | "d":"Swift Weekly 的精神继承者", 21 | "id":"http://se-monthly.flinedev.com/" 22 | }, 23 | { 24 | "t":"SwiftUI Weekly", 25 | "id":"http://weekly.swiftwithmajid.com/" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Protocol/URLConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLConvertible.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol URLRequestConvertible { 11 | func asURLRequest() -> URLRequest? 12 | } 13 | 14 | extension URLRequestConvertible { 15 | public var urlRequest: URLRequest? { return asURLRequest() } 16 | } 17 | 18 | public protocol URLConvertible { 19 | func asURL() throws -> URL 20 | } 21 | 22 | extension String: URLConvertible { 23 | public func asURL() throws -> URL { 24 | guard let url = URL(string: self) else { 25 | throw DJError.invalidURL("Error: Invalid URLString: " + self) 26 | } 27 | return url 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /DJGithub/Github/Store/FeedManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FeedManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/10. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FeedManager { 11 | static func getFeeds() async -> Feeds? { 12 | return try? await APIClient.shared.model(with: GithubRouter.feeds) 13 | } 14 | 15 | static func fetchFeedInfo(with urlString: String) async -> FeedInfo? { 16 | return try? await APIClient.shared.data(with: urlString, decoder: DJXMLParser()) 17 | } 18 | 19 | static func fetchRssInfo(with urlString: String) async -> T? { 20 | return try? await APIClient.shared.data(with: urlString, decoder: DJXMLParser()) as T? 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/HomeHorizontalItemsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeHorizontalItemsView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeHorizontalItemsView: View { 11 | 12 | let title: String 13 | let horizontalView: ChildView 14 | 15 | var body: some View { 16 | VStack(alignment: .leading) { 17 | Text(title) 18 | .font(.headline) 19 | .fontWeight(.bold) 20 | .foregroundColor(.black) 21 | .padding(.top, 10) 22 | .padding(.leading, 16) 23 | horizontalView 24 | } 25 | .background(Color.backgroundColor) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Result { 11 | 12 | func parse() -> T? { 13 | switch self { 14 | case .success(let d): 15 | if let d = d as? [String: Any] { 16 | if let statusCode = d["statusCode"] as? Int, 17 | ![204, 404, 201].contains(statusCode) { 18 | print("result parse error, status code:\(statusCode)") 19 | return nil 20 | } 21 | return try? DJDecoder(dict: d).decode() 22 | } 23 | case .failure(let error): 24 | print("result parse error: \(error)") 25 | break 26 | } 27 | return nil 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIViewController+Share.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Share.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/8. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | func share(with urlString: String) { 12 | guard let shareURL = URL(string: urlString) else { return } 13 | let sharedItems: [Any] = [shareURL] 14 | let activityVC = UIActivityViewController(activityItems: sharedItems, applicationActivities: nil) 15 | self.present(activityVC, animated: true) 16 | } 17 | 18 | var naviVc: UIViewController? { 19 | var vc: UIViewController? = self 20 | while vc?.parent != nil && !(vc?.parent is UINavigationController) { 21 | vc = vc?.parent 22 | } 23 | return vc 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIApplication+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/19. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIApplication { 11 | 12 | var keyWindow: UIWindow? { 13 | // Get connected scenes 14 | return UIApplication.shared.connectedScenes 15 | // Keep only active scenes, onscreen and visible to the user 16 | .filter { $0.activationState == .foregroundActive } 17 | // Keep only the first `UIWindowScene` 18 | .first(where: { $0 is UIWindowScene }) 19 | // Get its associated windows 20 | .flatMap({ $0 as? UIWindowScene })?.windows 21 | // Finally, keep only the key window 22 | .first(where: \.isKeyWindow) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/IssueDetail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IssueDetail.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/5. 6 | // 7 | 8 | import Foundation 9 | 10 | struct IssueDetail: DJCodable { 11 | 12 | var id: Int 13 | var number: Int 14 | var title: String 15 | var body: String? 16 | var createdAt: String 17 | var updatedAt: String 18 | var authorAssociation: String 19 | var htmlUrl: String 20 | var reactions: IssueDetailReaction? 21 | 22 | var state: Issue.IssueState 23 | 24 | var user: UserFollowing 25 | 26 | } 27 | 28 | struct IssueDetailReaction: DJCodable { 29 | var plus: Int 30 | var minus: Int 31 | var totalCount: Int 32 | 33 | enum CodingKeys: String, CodingKey { 34 | case plus = "+1", minus = "-1", totalCount 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DJGithub/SwiftUI/Modifier/ViewDidLoadModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewDidLoadModifier.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/2/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ViewDidLoadModifer: ViewModifier { 11 | @State private var viewDidLoad = false 12 | 13 | var action: (() -> Void)? 14 | 15 | func body(content: Content) -> some View { 16 | content 17 | .onAppear { 18 | if !viewDidLoad { 19 | viewDidLoad = true 20 | action?() 21 | } 22 | } 23 | } 24 | 25 | } 26 | 27 | extension View { 28 | func onViewDidLoad(perform action: (() -> Void)? = nil) -> some View { 29 | self.modifier(ViewDidLoadModifer(action: action)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/NextPageModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NextPageModel.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/27. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NextPageModel { 11 | 12 | var nextIndex: Int = 0 13 | var dataSource: [Any] = [] 14 | var isEnding: Bool = false 15 | 16 | init() { 17 | self.nextIndex = 0 18 | self.dataSource = [] 19 | self.isEnding = false 20 | } 21 | 22 | init(nextIndex: Int, dataSource: [Any], isEnding: Bool) { 23 | self.nextIndex = nextIndex 24 | self.dataSource = dataSource 25 | self.isEnding = isEnding 26 | } 27 | 28 | mutating func update(nextIndex: Int, dataSource: [Any], isEnding: Bool) { 29 | self.nextIndex = nextIndex 30 | self.dataSource = dataSource 31 | self.isEnding = isEnding 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /DJGithub/Github/Store/LocalUserManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalUserManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/25. 6 | // 7 | 8 | import Foundation 9 | 10 | struct LocalUserManager { 11 | 12 | static func getUser() -> UserViewer? { 13 | return DJUserDefaults.userInfo() 14 | } 15 | 16 | static func saveUser(_ user: UserViewer?) { 17 | DJUserDefaults.save(with: user) 18 | } 19 | 20 | static func loadViewer() async -> UserViewer? { 21 | if let viewer = await UserManager.fetchUserInfo() { 22 | saveUser(viewer) 23 | return UnChecked(value: viewer).value 24 | } 25 | return nil 26 | } 27 | 28 | static func getViewerName() -> String { 29 | return DJUserDefaults.viewerName() 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /DJGithub/Github/Source/Language.xcdatamodeld/Language.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Color+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Color { 11 | init(with hex: String) { 12 | if let color = hex.toColor { 13 | self.init(uiColor: color) 14 | } else { 15 | self.init(white: 1) 16 | } 17 | } 18 | 19 | init(with red: CGFloat, green: CGFloat, blue: CGFloat) { 20 | let color = UIColor(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: 1) 21 | self.init(uiColor: color) 22 | } 23 | } 24 | 25 | extension Color { 26 | static let textColor = Color(uiColor: .textColor) 27 | static let textGrayColor = Color(uiColor: .textGrayColor) 28 | static let backgroundColor = Color(uiColor: .backgroundColor) 29 | static let lightBlue = Color(uiColor: .lightBlue) 30 | } 31 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/RepoContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoContent.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/4. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RepoContent: DJCodable { 11 | enum RepoContentType: String, DJCodable { 12 | case file, dir 13 | } 14 | 15 | var name: String 16 | var path: String 17 | var size: Int 18 | var url: String 19 | var content: String? 20 | var downloadUrl: String? 21 | var type: RepoContentType 22 | 23 | var deepLength: Int = 0 24 | var isExpanded: Bool = false 25 | var contents: [RepoContent] = [] 26 | 27 | enum CodingKeys: String, CodingKey { 28 | case name, path, size, url, downloadUrl, type, content 29 | } 30 | 31 | var isDir: Bool { 32 | if case RepoContentType.dir = self.type { 33 | return true 34 | } 35 | return false 36 | } 37 | } 38 | 39 | struct RepoContents: DJCodable { 40 | var items: [RepoContent] 41 | } 42 | -------------------------------------------------------------------------------- /DJGithub/Github/Store/SearchWordManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchWordManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/2. 6 | // 7 | 8 | import Foundation 9 | 10 | private let SearchWordKey = "SearchWordKey" 11 | 12 | struct SearchWordManager { 13 | static let shared = SearchWordManager() 14 | 15 | func save(with word: String) { 16 | DJUserDefaults.addHistoryWord(with: word) 17 | } 18 | 19 | func load() -> [String] { 20 | return DJUserDefaults.allHistorySearchWords() 21 | } 22 | 23 | func move(index: Int, to top: Int) { 24 | var words = self.load() 25 | guard words.count > index else { return } 26 | let word = words[index] 27 | words.remove(at: index) 28 | words.insert(word, at: 0) 29 | UserDefaults.standard.setValue(words, forKey: SearchWordKey) 30 | } 31 | 32 | func removeAll() { 33 | UserDefaults.standard.set(nil, forKey: SearchWordKey) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Repo/Views/RepoBranchCommitCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoBranchCommitCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/28. 6 | // 7 | 8 | import UIKit 9 | 10 | class RepoBranchCommitCell: UITableViewCell { 11 | 12 | lazy var commitView: RepoBranchCommitUserView = { 13 | let view = RepoBranchCommitUserView() 14 | return view 15 | }() 16 | 17 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 18 | super.init(style: style, reuseIdentifier: reuseIdentifier) 19 | 20 | contentView.addSubview(commitView) 21 | commitView.snp.makeConstraints { make in 22 | make.edges.equalToSuperview() 23 | } 24 | } 25 | 26 | func render(with model: RepoBranchCommitInfo) { 27 | commitView.render(with: model) 28 | } 29 | 30 | required init?(coder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/Views/UserContributionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserContributionCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import UIKit 9 | 10 | class UserContributionCell: UITableViewCell { 11 | 12 | var userContributionView: UserContributionView = UserContributionView() 13 | 14 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 15 | super.init(style: style, reuseIdentifier: reuseIdentifier) 16 | 17 | setUp() 18 | } 19 | 20 | func render(with userContribution: UserContribution) { 21 | userContributionView.render(with: userContribution) 22 | } 23 | 24 | func setUp() { 25 | contentView.addSubview(userContributionView) 26 | userContributionView.snp.makeConstraints { make in 27 | make.edges.equalTo(self.contentView) 28 | } 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | super.init(coder: coder) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Feed/Views/RssFeedsItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RssFeedsItemView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/29. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RssFeedsItemView: View { 11 | 12 | let title: String 13 | let desc: String 14 | 15 | var body: some View { 16 | VStack(alignment: .leading, spacing: 5) { 17 | Text(title) 18 | .font(.system(size: 14)) 19 | .foregroundColor(.textColor) 20 | Text(desc) 21 | .font(.system(size: 12)) 22 | .foregroundColor(.textGrayColor) 23 | } 24 | .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) 25 | } 26 | } 27 | 28 | struct RssFeedsItemView_Previews: PreviewProvider { 29 | static var previews: some View { 30 | RssFeedsItemView( 31 | title: "rss feed title", desc: "rss feed content" 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Developer/LocalDataManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalDataManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/7. 6 | // 7 | 8 | import Foundation 9 | 10 | enum LocalDataType { 11 | case repo 12 | case developer 13 | } 14 | 15 | struct LocalDataViewModel { 16 | 17 | let type: LocalDataType 18 | 19 | init(type: LocalDataType) { 20 | self.type = type 21 | } 22 | 23 | func loadData(with filename: String? = nil) async -> [DJCodable] { 24 | switch type { 25 | case .developer: 26 | var rs: [DJCodable] = await DeveloperGroupManager.shared.loadFromDatabase() 27 | if rs.isEmpty { 28 | rs = await DeveloperGroupManager.shared.loadLocalDeveloperGroups() 29 | } 30 | return rs 31 | case .repo: 32 | return await LocalRepoManager.loadRepos(with: filename) 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DJGithubUITests/DJGithubUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJGithubUITestsLaunchTests.swift 3 | // DJGithubUITests 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import XCTest 9 | 10 | class DJGithubUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/ViewModel/SettingType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingType.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/1/31. 6 | // 7 | 8 | import Foundation 9 | 10 | enum SettingType { 11 | case iCloud 12 | case email, weibo, github 13 | case version(String), signUp 14 | } 15 | 16 | extension SettingType { 17 | var title: String { 18 | switch self { 19 | case .email: return "355086587@qq.com" 20 | case .weibo: return "@JJJJJJJiqqqq" 21 | case .github: return "东方宜玲" 22 | case .iCloud: return "iCloud" 23 | case .version(let version): return "App Version: \(version)" 24 | case .signUp: return "SignUp" 25 | } 26 | } 27 | 28 | var iconName: String { 29 | switch self { 30 | case .email: return "email" 31 | case .weibo: return "weibo" 32 | case .github: return "github" 33 | default: return "" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Developer/View/LocalDeveloperSectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalDeveloperSectionHeaderView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/3. 6 | // 7 | 8 | import UIKit 9 | 10 | class LocalDeveloperSectionHeaderView: UIView { 11 | 12 | lazy var titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .black 15 | label.font = UIFont.systemFont(ofSize: 14, weight: .bold) 16 | return label 17 | }() 18 | 19 | init() { 20 | super.init(frame: .zero) 21 | self.backgroundColor = .backgroundColor 22 | addSubview(self.titleLabel) 23 | self.titleLabel.snp.makeConstraints { make in 24 | make.leading.equalTo(16) 25 | make.centerY.equalTo(self) 26 | } 27 | } 28 | 29 | func render(with title: String) { 30 | self.titleLabel.text = title 31 | } 32 | 33 | required init?(coder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/GithubTrending/GithubTrendingManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingManager.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | class GithubTrendingManager: NSObject { 12 | 13 | @MainActor 14 | func addTrendingView(with types: [GithubTrendingType], 15 | selectedIndex: Int = 0, 16 | repoSelectedClosure: @escaping (GithubTrendingRepo) -> Void, 17 | developerSelectedClosure: @escaping (GithubTrendingDeveloper) -> Void) -> UIViewController { 18 | return UIHostingController( 19 | rootView: GithubTrendingView( 20 | types: types, 21 | selectedIndex: selectedIndex, 22 | repoSelectedClosure: repoSelectedClosure, 23 | developerSelectedClosure: developerSelectedClosure 24 | ) 25 | ) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/GithubTrendingItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingItemView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct GithubTrendingItemView: View { 11 | 12 | let views: [ChildView] 13 | 14 | var body: some View { 15 | ScrollView(.horizontal, showsIndicators: false) { 16 | LazyHStack(spacing: 10) { 17 | ForEach(0.. Self? { 14 | serialQueue.sync { 15 | let sql = "select count(1) from \(tableName) \(condition);" 16 | if let rs = try? SQLiteDatabase.queryCount(with: sql) { 17 | return try? DJDecoder(dict: rs).decode() 18 | } 19 | return nil 20 | } 21 | } 22 | } 23 | 24 | struct SqlMaxModel: DJCodable { 25 | var maxValue: String 26 | 27 | static func query(with tableName: String, fieldName: String, condition: String = "") -> Self? { 28 | let sql = "select max(\(fieldName)) from \(tableName) \(condition)" 29 | if let rs = try? SQLiteDatabase.queryCount(with: sql) { 30 | if let count = rs["count"] as? String { 31 | return SqlMaxModel(maxValue: count) 32 | } 33 | } 34 | return nil 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/Parse/RegularParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RegularParser.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/11. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RegularParser { 11 | static func parse(with pattern: String, validateString: String) -> [String] { 12 | do { 13 | let expression = try NSRegularExpression(pattern: pattern) 14 | let matches = expression.matches(in: validateString, range: NSRange(location: 0, length: validateString.count)) 15 | return matches.compactMap { (validateString as NSString).substring(with: $0.range(at: 1)) } 16 | } catch { 17 | print("regular parse error: \(error)") 18 | } 19 | return [] 20 | } 21 | 22 | static func matches(with pattern: String, validateString: String) -> [NSTextCheckingResult] { 23 | do { 24 | let expression = try NSRegularExpression(pattern: pattern) 25 | return expression.matches(in: validateString, range: NSRange(location: 0, length: validateString.count)) 26 | } catch { 27 | return [] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/RepoPull.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoPull.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/28. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RepoPull: DJCodable { 11 | var id: Int? 12 | var number: Int 13 | var state: Issue.IssueState 14 | var title: String 15 | var body: String? 16 | var createdAt: String 17 | var updatedAt: String 18 | 19 | var merged: Bool 20 | var mergeable: Bool? 21 | var mergedBy: UserFollowing? 22 | var commits: Int 23 | var additions: Int 24 | var deletions: Int 25 | var changedFiles: Int 26 | 27 | var user: UserFollowing 28 | var head: RepoPullBranch 29 | var base: RepoPullBranch 30 | 31 | var desc: String { 32 | return self.body ?? "" 33 | } 34 | } 35 | 36 | struct RepoPullMerge: DJCodable { 37 | var merged: Bool 38 | var messsage: String? 39 | var sha: String 40 | } 41 | 42 | extension RepoPull { 43 | struct RepoPullBranch: DJCodable { 44 | var label: String 45 | var ref: String 46 | var sha: String 47 | var user: UserFollowing 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/GithubTrending/GithubTrendingRepo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrending.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/19. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GithubTrendingRepo: Codable { 11 | let id = UUID() 12 | 13 | let userName: String 14 | let repoName: String 15 | let languange: String 16 | let desc: String 17 | let star: String 18 | let fork: String 19 | let footerDesc: String 20 | let languageColor: String 21 | 22 | enum CodingKeys: String, CodingKey { 23 | case userName, repoName, languange, desc, star 24 | case fork, footerDesc, languageColor 25 | } 26 | } 27 | 28 | extension GithubTrendingRepo { 29 | #if DEBUG 30 | static let sample: GithubTrendingRepo = GithubTrendingRepo( 31 | userName: "dyljqq", 32 | repoName: "DJGithub", 33 | languange: "Swift", 34 | desc: "DJGithub", 35 | star: "14", 36 | fork: "2", 37 | footerDesc: "2 stars today", 38 | languageColor: "0x000000" 39 | ) 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/Data/Swift/D-Swift 官方.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "t":"Swift.org - Welcome to Swift.org", 4 | "d":"Swift 官网", 5 | "id":"https://www.swift.org/" 6 | }, 7 | { 8 | "t":"The Swift Programming Language", 9 | "d":"Swift Github 官方地址", 10 | "id":"https://github.com/apple/swift" 11 | }, 12 | { 13 | "t":"Swift.org - Blog", 14 | "d":"Swift 博客", 15 | "id":"https://www.swift.org/blog/" 16 | }, 17 | { 18 | "t":"The Swift Programming Language", 19 | "d":"Swift 官方手册", 20 | "id":"https://docs.swift.org/swift-book/" 21 | }, 22 | { 23 | "t":"中文版 Apple 官方 Swift 教程《The Swift Programming Language》", 24 | "id":"https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese" 25 | }, 26 | { 27 | "t":"WWDC Session", 28 | "id":"https://developer.apple.com/videos/" 29 | }, 30 | { 31 | "t":"Exploring SwiftUI Sample Apps", 32 | "id":"https://developer.apple.com/tutorials/sample-apps" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Repo/Views/RepoListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoListView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/2/17. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RepoListView: View { 11 | 12 | @State var repos: [Repo] = [] 13 | 14 | var body: some View { 15 | ScrollView { 16 | LazyVStack() { 17 | ForEach(repos, id: \.id) { repo in 18 | SimpleRepoView(repo: repo) 19 | } 20 | } 21 | } 22 | } 23 | 24 | func loadData(from remote: Bool) { 25 | if let repos = DJUserDefaults.getStaredRepos() { 26 | self.repos = repos 27 | } 28 | 29 | Task { 30 | self.repos = await RepoManager.fetchStaredRepos(with: "dyljqq", page: 1) 31 | } 32 | } 33 | } 34 | 35 | struct RepoListView_Previews: PreviewProvider { 36 | static var previews: some View { 37 | RepoListView( 38 | repos: [ 39 | .sample, .sample, .sample, 40 | .sample, .sample, .sample, 41 | .sample, .sample, .sample, 42 | ] 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/ViewModel/UserViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserViewModel.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/22. 6 | // 7 | 8 | import UIKit 9 | 10 | @MainActor 11 | struct UserViewModel { 12 | 13 | var userObserver: DJObserverable = DJObserverable() 14 | 15 | var userViewer: UserViewer? { 16 | return userObserver.value 17 | } 18 | 19 | func fetchUser(with name: String) async { 20 | if name.isEmpty || ConfigManager.checkOwner(by: name), 21 | let userViewer = ConfigManager.viewer { 22 | userObserver.value = userViewer 23 | } 24 | 25 | Task.detached { 26 | if let viewer = await UserManager.fetchUserInfo(by: name) { 27 | await MainActor.run { 28 | userObserver.value = viewer 29 | if ConfigManager.checkOwner(by: viewer.login) { 30 | LocalUserManager.saveUser(viewer) 31 | } 32 | } 33 | } 34 | } 35 | } 36 | 37 | mutating func update(_ userViewer: UserViewer?) { 38 | userObserver.value = userViewer 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/Data/新鲜事/D-播客.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "t":"weak self", 4 | "id":"https://weakself.dev/" 5 | }, 6 | { 7 | "t":"ggtalk", 8 | "id":"https://talk.swift.gg/" 9 | }, 10 | { 11 | "t":"Swift by Sundell", 12 | "id":"https://swiftbysundell.com/podcast/" 13 | }, 14 | { 15 | "t":"Swift Unwrapped", 16 | "id":"https://swiftunwrapped.github.io/" 17 | }, 18 | { 19 | "t":"中文播客榜", 20 | "d":"枫言枫语出品", 21 | "id":"https://xyzrank.com/" 22 | }, 23 | { 24 | "t":"乱翻书", 25 | "d":"这是一档关注商业、科技和互联网的从业者对话节目。关心How和Why,以及少有人注意到的What。内容主要方向是科技考古、产品复盘和热点解读,希望能够为你带来信息增量。 「乱翻书」主理人是潘乱,代表作品有《腾讯没有梦想》、字节跳动/快手早期关键节点的系列特写。", 26 | "id":"https://www.xiaoyuzhoufm.com/podcast/61358d971c5d56efe5bcb5d2" 27 | }, 28 | { 29 | "t":"漫编室", 30 | "d":"编辑与漫画有关的书籍,思考与漫画有关的困境。 漫编室主编,铁雄。", 31 | "id":"https://www.xiaoyuzhoufm.com/podcast/5fab75d183c34e85dda07f5d" 32 | }, 33 | { 34 | "t":"Anyway.FM 设计杂谈", 35 | "d":"由 UI 设计师 JJ Ying 和 Leon Gao 主播的 podcast,主题包括但不限于视觉设计。", 36 | "id":"https://www.xiaoyuzhoufm.com/podcast/5e280faf418a84a0461fba0e" 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/Default+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Default+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/2. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bool { 11 | enum False: DefaultValue { 12 | static let defaultValue = false 13 | } 14 | enum True: DefaultValue { 15 | static let defaultValue = true 16 | } 17 | } 18 | 19 | extension Default { 20 | typealias True = Default 21 | typealias False = Default 22 | } 23 | 24 | extension String: DefaultValue { 25 | static let defaultValue = "" 26 | 27 | enum UnProvidedDesc: DefaultValue { 28 | static let defaultValue = "No description provided." 29 | } 30 | enum UnProviedTitle: DefaultValue { 31 | static let defaultValue = "No title provided." 32 | } 33 | enum Company: DefaultValue { 34 | static let defaultValue = "团队" 35 | } 36 | enum Email: DefaultValue { 37 | static let defaultValue = "邮箱" 38 | } 39 | enum Location: DefaultValue { 40 | static let defaultValue = "地点" 41 | } 42 | enum Blog: DefaultValue { 43 | static let defaultValue = "个人主页" 44 | } 45 | enum Now: DefaultValue { 46 | static let defaultValue = DateHelper.standard.dateToString(Date.now) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DJGithub/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIApplicationSceneManifest 11 | 12 | UIApplicationSupportsMultipleScenes 13 | 14 | UISceneConfigurations 15 | 16 | UIWindowSceneSessionRoleApplication 17 | 18 | 19 | UISceneConfigurationName 20 | Default Configuration 21 | UISceneDelegateClassName 22 | $(PRODUCT_MODULE_NAME).SceneDelegate 23 | UISceneStoryboardFile 24 | Main 25 | 26 | 27 | 28 | 29 | LSApplicationQueriesSchemes 30 | 31 | 32 | sinaweibohd 33 | sinaweibo 34 | sinaweibosso 35 | weibosdk 36 | weibosdk2.5 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Protocol/DefaultValue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultValue.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/2. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DefaultValue { 11 | associatedtype Value: Codable 12 | static var defaultValue: Value { get } 13 | } 14 | 15 | @propertyWrapper 16 | struct Default { 17 | var wrappedValue: T.Value 18 | } 19 | 20 | extension Default: Decodable { 21 | init(from decoder: Decoder) throws { 22 | let container = try decoder.singleValueContainer() 23 | if let value = try? container.decode(T.Value.self) as? String, value.isEmpty { 24 | wrappedValue = T.defaultValue 25 | } else { 26 | wrappedValue = (try? container.decode(T.Value.self)) ?? T.defaultValue 27 | } 28 | } 29 | } 30 | 31 | extension Default: Encodable { 32 | func encode(to encoder: Encoder) throws { 33 | var container = encoder.singleValueContainer() 34 | try container.encode(wrappedValue) 35 | } 36 | } 37 | 38 | extension KeyedDecodingContainer { 39 | func decode( 40 | _ type: Default.Type, 41 | forKey key: Key 42 | ) throws -> Default where T: DefaultValue { 43 | try decodeIfPresent(type, forKey: key) ?? Default(wrappedValue: T.defaultValue) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DJGithub/Github/Widget/LanguageGradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LanguageGradientView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/26. 6 | // 7 | 8 | import UIKit 9 | 10 | class LanguageGradientView: UIView { 11 | 12 | private var percent: CGFloat = 0 13 | 14 | var primaryLanguageLayer: CAShapeLayer = { 15 | let layer = CAShapeLayer() 16 | return layer 17 | }() 18 | 19 | init() { 20 | super.init(frame: CGRect.zero) 21 | setUp() 22 | } 23 | 24 | private func setUp() { 25 | backgroundColor = UIColorFromRGB(0xeeeeee) 26 | self.layer.addSublayer(primaryLanguageLayer) 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | func render(with primaryLanguage: PrimaryLanguage, language: Repository.RepositotyLanguage) { 34 | guard language.totalSize > 0 else { return } 35 | percent = language.primaryPercent(with: primaryLanguage.name) 36 | primaryLanguageLayer.backgroundColor = primaryLanguage.color.toColor?.cgColor 37 | } 38 | 39 | override func layoutSubviews() { 40 | super.layoutSubviews() 41 | 42 | primaryLanguageLayer.frame = CGRect(x: 0, y: 0, width: percent * self.bounds.width, height: self.bounds.height) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/UIImage+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/29. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImage { 11 | static var defaultPersonImage: UIImage? { 12 | return UIImage(named: "person") 13 | } 14 | } 15 | 16 | extension UIImageView { 17 | 18 | func setImage(with url: URL?, placeHolder: UIImage? = nil) { 19 | self.kf.setImage(with: url, placeholder: placeHolder) 20 | } 21 | 22 | func setImage(with urlString: String, placeHolder: UIImage? = nil) { 23 | self.kf.setImage(with: URL(string: urlString), placeholder: placeHolder) 24 | } 25 | 26 | func setImage(with urlString: String?, placeHolder: UIImage? = nil) { 27 | self.kf.setImage(with: URL(string: urlString ?? ""), placeholder: placeHolder) 28 | } 29 | 30 | func setImage(with urlString: String?, placeHolder: UIImage? = nil, completionHandler: @escaping ((UIImage?) -> Void)) { 31 | self.kf.setImage(with: URL(string: urlString ?? ""), placeholder: placeHolder, completionHandler: { result in 32 | switch result { 33 | case .success(let r): 34 | completionHandler(r.image) 35 | case .failure(let error): 36 | print("fetch image error: \(error)") 37 | } 38 | }) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/RssFeedItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RssFeedItem.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/1/29. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RssFeedItem: View { 11 | 12 | let item: RssFeedLatestCellModel 13 | 14 | var body: some View { 15 | HStack(alignment: .top) { 16 | VStack(alignment: .leading) { 17 | Text(item.title) 18 | .font(.headline) 19 | .foregroundColor(.textColor) 20 | .lineLimit(2) 21 | .padding(.top, 10) 22 | Spacer() 23 | HStack { 24 | Text(item.from) 25 | .font(.subheadline) 26 | .foregroundColor(.textGrayColor) 27 | .lineLimit(1) 28 | Spacer() 29 | Text(item.date) 30 | .font(.subheadline) 31 | .foregroundColor(.textGrayColor) 32 | } 33 | .padding(5) 34 | } 35 | } 36 | } 37 | } 38 | 39 | struct RssFeedItem_Previews: PreviewProvider { 40 | static var previews: some View { 41 | RssFeedItem(item: RssFeedLatestCellModel.example) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/ReposTrendingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReposTrendingView.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ReposTrendingView: View { 11 | 12 | let items: [GithubTrendingRepo] 13 | 14 | var body: some View { 15 | ScrollView(.horizontal, showsIndicators: false) { 16 | LazyHStack { 17 | ForEach(0.. = DJObserverable() 13 | 14 | var feeds: Feeds? { 15 | return DJUserDefaults.getFeeds() 16 | } 17 | 18 | func fetchLocalFeedInfo() -> FeedInfo? { 19 | return DJUserDefaults.getFeedInfo() 20 | } 21 | 22 | static func asyncFetchFeedInfo() async -> FeedInfo? { 23 | if let feeds = await asyncFetchFeeds(), 24 | let feedInfo = await FeedManager.fetchFeedInfo(with: "\(feeds.currentUserUrl)&page=1") { 25 | DJUserDefaults.setFeedInfo(with: feedInfo) 26 | return feedInfo 27 | } 28 | return nil 29 | } 30 | 31 | private static func asyncFetchFeeds() async -> Feeds? { 32 | if let feeds = await FeedManager.getFeeds() { 33 | Self.updateLocalFeeds(feeds) 34 | return feeds 35 | } else { 36 | return nil 37 | } 38 | } 39 | 40 | private static func updateLocalFeeds(_ feeds: Feeds?) { 41 | DJUserDefaults.setFeeds(with: feeds) 42 | } 43 | 44 | private func fetchLocalFeeds() -> Feeds? { 45 | return DJUserDefaults.getFeeds() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Language.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Language.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/17. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Language: DJCodable { 11 | var id: Int? 12 | var language: String 13 | var hex: String 14 | } 15 | 16 | extension Language: SQLTable { 17 | static var tableName: String { 18 | return "language" 19 | } 20 | 21 | static var fields: [String] { 22 | return [ 23 | "language", 24 | "hex" 25 | ] 26 | } 27 | 28 | static var fieldsTypeMapping: [String: FieldType] { 29 | return [ 30 | "language": .text, 31 | "hex": .text 32 | ] 33 | } 34 | 35 | var fieldsValueMapping: [String: Any] { 36 | return [ 37 | "language": self.language, 38 | "hex": self.hex 39 | ] 40 | } 41 | 42 | static var uniqueKeys: [String] { 43 | return ["id"] 44 | } 45 | 46 | static func get(with language: String) -> Self? { 47 | let values: [Self] = Self.select(with: " where language=\"\(language)\"") 48 | return values.first 49 | } 50 | 51 | func update(with languageName: String, color: String) { 52 | let sql = "update \(Self.tableName) set hex=\"\(color)\" where language=\"\(languageName)\"" 53 | try? Self.update(with: sql) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Utils/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // utils.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import UIKit 9 | 10 | func UIColorFromRGB(_ rgbValue: UInt, alpha: CGFloat = 1.0) -> UIColor { 11 | return UIColor( 12 | red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, 13 | green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, 14 | blue: CGFloat(rgbValue & 0x0000FF) / 255.0, 15 | alpha: alpha 16 | ) 17 | } 18 | 19 | func isEmpty(by str: String?) -> Bool { 20 | return str == nil || str!.isEmpty 21 | } 22 | 23 | func loadBundleJSONFile(_ filename: String) -> T { 24 | guard let file = Bundle.main.url(forResource: filename, withExtension: "json") else { 25 | fatalError("Couldn't find \(filename) in main bundle.") 26 | } 27 | do { 28 | let decoder = JSONDecoder() 29 | let data = try Data(contentsOf: file) 30 | return try decoder.decode(T.self, from: data) 31 | } catch { 32 | fatalError("load data from \(filename), error: \(error)") 33 | } 34 | } 35 | 36 | func loadBundleData(_ filename: String) -> Data { 37 | let data: Data 38 | guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else { 39 | fatalError("Couldn't find \(filename) in main bundle.") 40 | } 41 | do { 42 | data = try Data(contentsOf: file) 43 | return data 44 | } catch { 45 | fatalError("Couldn't load \(filename) from main bundle:\n\(error)") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/JSONSerialization+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONSerialization+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import Foundation 9 | 10 | extension JSONSerialization { 11 | 12 | @discardableResult 13 | static func loadJSON(withFilename filename: String) throws -> T? { 14 | let fm = FileManager.default 15 | let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) 16 | if let url = urls.first { 17 | var fileURL = url.appendingPathComponent(filename) 18 | fileURL = fileURL.appendingPathExtension("json") 19 | let data = try Data(contentsOf: fileURL) 20 | return try DJDecoder(data: data).decode() 21 | } 22 | return nil 23 | } 24 | 25 | @discardableResult 26 | static func save(jsonObject: Any, toFilename filename: String) throws -> Bool { 27 | let fm = FileManager.default 28 | let urls = fm.urls(for: .documentDirectory, in: .userDomainMask) 29 | if let url = urls.first { 30 | var fileURL = url.appendingPathComponent(filename) 31 | fileURL = fileURL.appendingPathExtension("json") 32 | let data = try JSONSerialization.data(withJSONObject: jsonObject, options: [.prettyPrinted]) 33 | try data.write(to: fileURL, options: [.atomicWrite]) 34 | return true 35 | } 36 | 37 | return false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /DJGithub/Github/Extension/String+Ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Ext.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import UIKit 9 | import CryptoKit 10 | 11 | extension String { 12 | var toColor: UIColor? { 13 | let hexStr = self.first == "#" ? String(Array(self).dropFirst()) : self 14 | guard let v = UInt(hexStr, radix: 16) else { 15 | return nil 16 | } 17 | return UIColorFromRGB(v) 18 | } 19 | 20 | var md5: String { 21 | guard let data = data(using: .utf8) else { return self } 22 | let hashed = SHA256.hash(data: data) 23 | return hashed.compactMap { String(format: "%02x", $0) }.joined() 24 | } 25 | 26 | var splitRepoFullName: (String, String)? { 27 | let arr = self.components(separatedBy: "/") 28 | return arr.count == 2 ? (arr[0], arr[1]) : nil 29 | } 30 | } 31 | 32 | extension String { 33 | 34 | func asyncBoundingRect(with size: CGSize, 35 | options: NSStringDrawingOptions = [], 36 | attributes: [NSAttributedString.Key: Any]? = nil, 37 | context: NSStringDrawingContext?) -> CGRect? { 38 | let rect = (self as NSString).boundingRect( 39 | with: size, 40 | options: options, 41 | attributes: attributes, 42 | context: context 43 | ) 44 | return rect 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Home/ViewModel/HomeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModel.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2024/5/13. 6 | // 7 | 8 | import Foundation 9 | 10 | @Observable 11 | class HomeViewModel { 12 | 13 | var repos: [GithubTrendingRepo] = [] 14 | var developers: [GithubTrendingDeveloper] = [] 15 | var pamphletModels: [PamphletSectionModel] = [] 16 | 17 | static func loadRepos() async -> [GithubTrendingRepo] { 18 | if let repos = GithubTrendingItemsManager.shared.load(with: .repo) as [GithubTrendingRepo]? { 19 | return repos 20 | } 21 | 22 | let type = GithubTrendingType.repo("/swift?since=daily") 23 | let repos: [GithubTrendingRepo] = await GithubTrendingParser(urlString: type.urlString).parse(with: .repo) 24 | return repos 25 | } 26 | 27 | static func loadDevelopers() async -> [GithubTrendingDeveloper] { 28 | if let developers = GithubTrendingItemsManager.shared.load(with: .developer) as [GithubTrendingDeveloper]? { 29 | return developers 30 | } 31 | 32 | let type = GithubTrendingType.developer("/swift?since=daily") 33 | let developers: [GithubTrendingDeveloper] = await GithubTrendingParser(urlString: type.urlString).parse(with: .developer) 34 | return developers 35 | } 36 | 37 | static func loadPamphletModels() async -> [PamphletSectionModel] { 38 | return loadBundleJSONFile("PamphletData") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/RepoBranchCommit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoBranchCommit.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/27. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RepoBranchCommitInfo: DJCodable { 11 | var commit: RepoBranchCommit 12 | var url: String 13 | var htmlUrl: String 14 | var commentsUrl: String 15 | var stats: RepoBranchCommitStats? 16 | 17 | var files: [RepoPullFile]? 18 | var displayedFiles: [RepoPullFile] { 19 | return files ?? [] 20 | } 21 | 22 | var author: RepoBranchCommitInfoUser? 23 | var committer: RepoBranchCommitInfoUser? 24 | } 25 | 26 | struct RepoBranchCommit: DJCodable { 27 | struct RepoBranchCommitAuthor: DJCodable { 28 | var name: String 29 | var email: String? 30 | var date: String 31 | } 32 | 33 | var message: String 34 | var commentCount: Int 35 | 36 | var author: RepoBranchCommitAuthor 37 | var committer: RepoBranchCommitAuthor 38 | } 39 | 40 | extension RepoBranchCommitInfo { 41 | struct RepoBranchCommitInfoUser: DJCodable { 42 | var id: Int 43 | var login: String 44 | var avatarUrl: String 45 | } 46 | 47 | struct RepoBranchCommitStats: DJCodable { 48 | var total: Int 49 | var additions: Int 50 | var deletions: Int 51 | } 52 | } 53 | 54 | struct PullRequestModel: DJCodable { 55 | let title: String 56 | let userName: String 57 | let repoName: String 58 | let base: String 59 | // to use compare userName + branch name,etc: dyljqq:dev 60 | let compare: String 61 | let commiterName: String 62 | } 63 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Developer/View/LocalRepoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalRepoCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/7. 6 | // 7 | 8 | import UIKit 9 | 10 | class LocalRepoCell: UITableViewCell { 11 | 12 | lazy var titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .textColor 15 | label.font = UIFont.systemFont(ofSize: 14) 16 | return label 17 | }() 18 | 19 | lazy var descLabel: UILabel = { 20 | let label = UILabel() 21 | label.textColor = .textGrayColor 22 | label.font = UIFont.systemFont(ofSize: 12) 23 | label.numberOfLines = 0 24 | return label 25 | }() 26 | 27 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 28 | super.init(style: style, reuseIdentifier: reuseIdentifier) 29 | 30 | setUp() 31 | } 32 | 33 | func render(with repo: LocalRepo) { 34 | self.titleLabel.text = repo.id 35 | self.descLabel.text = repo.description 36 | } 37 | 38 | private func setUp() { 39 | contentView.addSubview(titleLabel) 40 | contentView.addSubview(descLabel) 41 | 42 | titleLabel.snp.makeConstraints { make in 43 | make.top.equalTo(10) 44 | make.leading.equalTo(12) 45 | } 46 | descLabel.snp.makeConstraints { make in 47 | make.bottom.equalTo(-10) 48 | make.leading.equalTo(titleLabel) 49 | make.trailing.equalTo(-12) 50 | } 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/Parse/DateParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateParse.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/10. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DateParser { 11 | let dateFormatter = DateFormatter() 12 | 13 | init(with dateFormat: String) { 14 | dateFormatter.dateFormat = dateFormat 15 | } 16 | 17 | func string(from date: Date) -> String? { 18 | return dateFormatter.string(from: date) 19 | } 20 | 21 | func date(from string: String) -> Date? { 22 | return dateFormatter.date(from: string) 23 | } 24 | 25 | } 26 | 27 | extension Date { 28 | var dateFormatString: String { 29 | let diff = (Date.now.timeIntervalSince1970 - self.timeIntervalSince1970) 30 | if diff < 60 { 31 | return "\(Int(diff)) seconds ago" 32 | } else if diff >= 60 && diff < 3600 { 33 | return "\(Int(diff / 60)) minuts ago" 34 | } else if diff >= 3600 && diff < 24 * 3600 { 35 | return "\(Int(diff / 3600)) hours ago" 36 | } else if diff >= 24 * 3600 && diff <= 24 * 30 * 3600 { 37 | return "\(Int(diff / (24 * 3600))) days ago" 38 | } else if diff >= (24 * 30 * 3600) && diff < (12 * 30 * 24 * 30 * 3600) { 39 | return "\(Int(diff / (30 * 24 * 3600))) months ago" 40 | } 41 | return normalDateParser.string(from: self) ?? "" 42 | } 43 | } 44 | 45 | /** 46 | yyyy-MM-dd'T'HH:mm:ssZ // 24小时制 47 | yyyy-MM-dd'T'hh:mm:ssZ // 12小时制 48 | */ 49 | let feedDateParser = DateParser(with: "yyyy-MM-dd'T'HH:mm:ssZ") 50 | let normalDateParser = DateParser(with: "yyyy-MM-dd'T'HH:mm:ssZ") 51 | -------------------------------------------------------------------------------- /DJGithubUITests/DJGithubUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJGithubUITests.swift 3 | // DJGithubUITests 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import XCTest 9 | 10 | class DJGithubUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Issue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Issue.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/4. 6 | // 7 | 8 | import UIKit 9 | 10 | struct IssueItems: DJCodable { 11 | var items: [Issue] 12 | } 13 | 14 | struct IssueLayout { 15 | let issue: Issue 16 | let title: String 17 | var height: CGFloat = 0 18 | var imageName: String = "" 19 | 20 | init(issue: Issue) { 21 | self.issue = issue 22 | self.title = "#\(self.issue.number) - \(self.issue.title)" 23 | } 24 | 25 | @MainActor 26 | mutating func calHeight() { 27 | let rect = (self.title as NSString).boundingRect( 28 | with: CGSize(width: FrameGuide.screenWidth - 45, height: 0), 29 | options: .usesLineFragmentOrigin, 30 | attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14)], 31 | context: nil 32 | ) 33 | self.height = rect.height + 41 34 | } 35 | } 36 | 37 | struct Issue: DJCodable { 38 | enum IssueState: String, DJCodable { 39 | case open, closed 40 | } 41 | 42 | struct PullRequest: DJCodable { 43 | var url: String 44 | var htmlUrl: String 45 | var diffUrl: String 46 | } 47 | 48 | var id: Int 49 | var number: Int 50 | var title: String 51 | var state: IssueState 52 | var comments: Int? 53 | var htmlUrl: String 54 | 55 | var createdAt: String 56 | var updatedAt: String 57 | 58 | var user: UserFollowing? 59 | var pullRequest: PullRequest? 60 | } 61 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/SettingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingViewController.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/2/1. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | class SettingViewController: UIViewController { 12 | 13 | lazy var vc: UIViewController = { 14 | let settingView = SettingView { [weak self] type in 15 | self?.itemDidSelected(with: type) 16 | } 17 | let vc = UIHostingController(rootView: settingView) 18 | return vc 19 | }() 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | view.backgroundColor = .backgroundColor 25 | navigationItem.title = "Setting" 26 | 27 | view.addSubview(vc.view) 28 | vc.view.snp.makeConstraints { make in 29 | make.top.equalTo(FrameGuide.navigationBarAndStatusBarHeight) 30 | make.bottom.leading.trailing.equalToSuperview() 31 | } 32 | } 33 | 34 | func itemDidSelected(with type: SettingType) { 35 | switch type { 36 | case .email: URLRouter.open(with: "mailto: \(type.title)", jumpType: .outside) 37 | case .weibo: URLRouter.open(with: "sinaweibo://userinfo?uid=2579435503", jumpType: .outside) 38 | case .github: URLRouter.open(with: "djgithub://user?name=dyljqq") 39 | case .signUp: 40 | DJUserDefaults.clearViewerInfo() 41 | UIApplication.shared.keyWindow?.rootViewController = LoadingViewerViewController() 42 | default: break 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/ImageCoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCoder.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/24. 6 | // 7 | 8 | import UIKit 9 | 10 | class ImageDecoder { 11 | 12 | static func decode(data: Data, to pointSize: CGSize, scale: CGFloat, completionHandler: @Sendable @escaping (UIImage?) -> Void) { 13 | DispatchQueue.global().async { 14 | let image = downsampledImage(data: data, to: pointSize, scale: scale) 15 | DispatchQueue.main.async { 16 | completionHandler(image) 17 | } 18 | } 19 | } 20 | 21 | static func downsampledImage(data: Data, to pointSize: CGSize, scale: CGFloat) -> UIImage? { 22 | let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary 23 | guard let imageSource = CGImageSourceCreateWithData(data as CFData, imageSourceOptions) else { 24 | return nil 25 | } 26 | 27 | let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale 28 | let downsampleOptions = [ 29 | kCGImageSourceCreateThumbnailFromImageAlways: true, 30 | kCGImageSourceShouldCacheImmediately: true, 31 | kCGImageSourceCreateThumbnailWithTransform: true, 32 | kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary 33 | guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { 34 | return nil 35 | } 36 | return UIImage(cgImage: downsampledImage) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /DJGithub.xcodeproj/xcuserdata/polarisdev.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DJGithub.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | SnapKitPlayground (Playground) 1.xcscheme 13 | 14 | isShown 15 | 16 | orderHint 17 | 2 18 | 19 | SnapKitPlayground (Playground) 2.xcscheme 20 | 21 | isShown 22 | 23 | orderHint 24 | 3 25 | 26 | SnapKitPlayground (Playground).xcscheme 27 | 28 | isShown 29 | 30 | orderHint 31 | 1 32 | 33 | SwiftyMarkdown (Playground) 1.xcscheme 34 | 35 | isShown 36 | 37 | orderHint 38 | 5 39 | 40 | SwiftyMarkdown (Playground) 2.xcscheme 41 | 42 | isShown 43 | 44 | orderHint 45 | 6 46 | 47 | SwiftyMarkdown (Playground).xcscheme 48 | 49 | isShown 50 | 51 | orderHint 52 | 4 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Repo/Views/RepoFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoFooterView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/25. 6 | // 7 | 8 | import UIKit 9 | import WebKit 10 | import MarkdownView 11 | 12 | class RepoFooterView: UIView { 13 | 14 | var fetchHeightClosure: ((CGFloat) -> Void)? 15 | var touchLink: ((URLRequest?) -> Void)? 16 | 17 | lazy var mdView: MarkdownView = { 18 | let mdView = MarkdownView() 19 | mdView.isScrollEnabled = false 20 | return mdView 21 | }() 22 | 23 | init() { 24 | super.init(frame: .zero) 25 | 26 | addSubview(self.mdView) 27 | mdView.snp.makeConstraints { make in 28 | make.edges.equalTo(self) 29 | } 30 | 31 | self.mdView.onRendered = { [weak self] height in 32 | self?.fetchHeightClosure?(height) 33 | } 34 | 35 | self.mdView.onTouchLink = { [weak self] req in 36 | self?.touchLink?(req) 37 | return false 38 | } 39 | } 40 | 41 | func render(with content: String?) { 42 | guard let content = content else { return } 43 | DispatchQueue.global().async { 44 | if let data = NSData(base64Encoded: content, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as? Data, 45 | let decodedString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as? String { 46 | DispatchQueue.main.async { 47 | self.mdView.load(markdown: decodedString, enableImage: true) 48 | } 49 | } 50 | } 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | super.init(coder: coder) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/Views/UserInfoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/8. 6 | // 7 | 8 | import UIKit 9 | 10 | class UserInfoCell: UITableViewCell { 11 | 12 | lazy var nameLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .textGrayColor 15 | label.font = UIFont.systemFont(ofSize: 14) 16 | return label 17 | }() 18 | 19 | lazy var contentLabel: UILabel = { 20 | let label = UILabel() 21 | label.textColor = .textColor 22 | label.font = UIFont.systemFont(ofSize: 14) 23 | label.textAlignment = .left 24 | return label 25 | }() 26 | 27 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 28 | super.init(style: style, reuseIdentifier: reuseIdentifier) 29 | 30 | setUp() 31 | } 32 | 33 | func render(with name: String, content: String?) { 34 | self.nameLabel.text = "\(name):" 35 | self.contentLabel.text = content 36 | } 37 | 38 | private func setUp() { 39 | contentView.addSubview(nameLabel) 40 | contentView.addSubview(contentLabel) 41 | 42 | nameLabel.snp.makeConstraints { make in 43 | make.leading.equalTo(12) 44 | make.centerY.equalToSuperview() 45 | } 46 | contentLabel.snp.makeConstraints { make in 47 | make.trailing.equalTo(-12) 48 | make.leading.equalTo(nameLabel.snp.trailing).offset(10) 49 | make.centerY.equalTo(nameLabel) 50 | } 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/Views/UserCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import UIKit 9 | 10 | class UserCell: UITableViewCell { 11 | 12 | lazy var iconImageView: UIImageView = { 13 | let imageView = UIImageView() 14 | return imageView 15 | }() 16 | 17 | lazy var contentLabel: UILabel = { 18 | let label = UILabel() 19 | label.textColor = .textColor 20 | label.font = UIFont.systemFont(ofSize: 14) 21 | return label 22 | }() 23 | 24 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 25 | super.init(style: style, reuseIdentifier: reuseIdentifier) 26 | 27 | setUp() 28 | } 29 | 30 | func setUp() { 31 | contentView.addSubview(iconImageView) 32 | contentView.addSubview(contentLabel) 33 | 34 | iconImageView.snp.makeConstraints { make in 35 | make.leading.equalTo(12) 36 | make.centerY.equalTo(contentView) 37 | make.width.height.equalTo(20) 38 | } 39 | contentLabel.snp.makeConstraints { make in 40 | make.centerY.equalTo(iconImageView) 41 | make.leading.equalTo(iconImageView.snp.trailing).offset(10) 42 | make.trailing.equalTo(-12) 43 | } 44 | } 45 | 46 | func render(with iconName: String, content: String, contentColor: UIColor) { 47 | iconImageView.image = UIImage(named: iconName) 48 | contentLabel.text = content 49 | contentLabel.textColor = contentColor 50 | } 51 | 52 | required init?(coder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DJGithub/Github/Widget/NavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Navigation.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/24. 6 | // 7 | 8 | import UIKit 9 | 10 | class NavigationController: UINavigationController { 11 | 12 | override init(rootViewController: UIViewController) { 13 | super.init(rootViewController: rootViewController) 14 | } 15 | 16 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 17 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 18 | } 19 | 20 | required init?(coder aDecoder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | let backButtonAppearance = UIBarButtonItemAppearance(style: .plain) 28 | backButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear] 29 | 30 | let barApp = UINavigationBarAppearance() 31 | barApp.backgroundEffect = UIBlurEffect(style: .regular) 32 | barApp.backButtonAppearance = backButtonAppearance 33 | navigationBar.scrollEdgeAppearance = barApp 34 | 35 | let barAppearace = UIBarButtonItem.appearance() 36 | barAppearace.setBackButtonTitlePositionAdjustment(UIOffset(horizontal: 0, vertical: -60), for: UIBarMetrics.default) 37 | 38 | } 39 | 40 | override func pushViewController(_ viewController: UIViewController, animated: Bool) { 41 | if self.children.count == 1 { 42 | viewController.hidesBottomBarWhenPushed = true 43 | } 44 | super.pushViewController(viewController, animated: animated) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /DJGithub/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class MyApp { 12 | static func main() { 13 | UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self)) 14 | } 15 | } 16 | 17 | class AppDelegate: UIResponder, UIApplicationDelegate { 18 | 19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 20 | return true 21 | } 22 | 23 | // MARK: UISceneSession Lifecycle 24 | 25 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 26 | // Called when a new scene session is being created. 27 | // Use this method to select a configuration to create the new scene with. 28 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 29 | } 30 | 31 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 32 | // Called when the user discards a scene session. 33 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 34 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Repo/Views/RepoBranchCommitFileCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoBranchCommitFileCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/28. 6 | // 7 | 8 | import UIKit 9 | 10 | class RepoBranchCommitFileCell: UITableViewCell { 11 | 12 | lazy var titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .textColor 15 | label.font = UIFont.systemFont(ofSize: 14) 16 | return label 17 | }() 18 | 19 | lazy var descLabel: UILabel = { 20 | let label = UILabel() 21 | label.font = UIFont.systemFont(ofSize: 14) 22 | label.textColor = .textGrayColor 23 | return label 24 | }() 25 | 26 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 27 | super.init(style: style, reuseIdentifier: reuseIdentifier) 28 | 29 | setUp() 30 | } 31 | 32 | func render(with file: RepoPullFile) { 33 | titleLabel.text = file.filename 34 | descLabel.text = "\(file.additions) additions and \(file.deletions) deletions." 35 | } 36 | 37 | private func setUp() { 38 | contentView.addSubview(titleLabel) 39 | contentView.addSubview(descLabel) 40 | 41 | titleLabel.snp.makeConstraints { make in 42 | make.leading.equalTo(12) 43 | make.trailing.equalTo(-12) 44 | make.top.equalTo(10) 45 | } 46 | descLabel.snp.makeConstraints { make in 47 | make.leading.trailing.equalTo(titleLabel) 48 | make.top.equalTo(titleLabel.snp.bottom).offset(10) 49 | } 50 | } 51 | 52 | required init?(coder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/View/PamphletResourceCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PamphletResourceCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class PamphletResourceCell: UITableViewCell { 11 | 12 | lazy var titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .textColor 15 | label.font = UIFont.systemFont(ofSize: 14) 16 | label.numberOfLines = 0 17 | return label 18 | }() 19 | 20 | lazy var contentLabel: UILabel = { 21 | let label = UILabel() 22 | label.font = UIFont.systemFont(ofSize: 12) 23 | label.textColor = .textGrayColor 24 | label.numberOfLines = 0 25 | return label 26 | }() 27 | 28 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 29 | super.init(style: style, reuseIdentifier: reuseIdentifier) 30 | 31 | setUp() 32 | } 33 | 34 | func render(with model: PamphletResourceModel) { 35 | titleLabel.text = model.title 36 | contentLabel.text = model.desc 37 | } 38 | 39 | private func setUp() { 40 | contentView.addSubview(titleLabel) 41 | contentView.addSubview(contentLabel) 42 | 43 | titleLabel.snp.makeConstraints { make in 44 | make.leading.equalTo(12) 45 | make.trailing.equalTo(-12) 46 | make.top.equalTo(10) 47 | } 48 | contentLabel.snp.makeConstraints { make in 49 | make.leading.equalTo(12) 50 | make.trailing.equalTo(-12) 51 | make.bottom.equalTo(-10) 52 | } 53 | } 54 | 55 | required init?(coder: NSCoder) { 56 | fatalError("init(coder:) has not been implemented") 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/LanguageManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LanguageManager.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/17. 6 | // 7 | 8 | import UIKit 9 | import CoreData 10 | 11 | class LanguageManager { 12 | 13 | nonisolated(unsafe) static var mapping: [String: String] = [:] 14 | 15 | init() { 16 | try? Language.createTable() 17 | } 18 | 19 | static func save(_ languages: [Language]) async { 20 | Task { 21 | for language in languages { 22 | if Language.get(with: language.language) == nil { 23 | try? language.insert() 24 | } 25 | } 26 | await loadLanguageMapping() 27 | } 28 | } 29 | 30 | static func loadLanguageMapping() async { 31 | var rs = [String: String]() 32 | let languages: [Language] = Language.selectAll() 33 | for language in languages { 34 | rs[language.language] = language.hex 35 | } 36 | LanguageManager.mapping = rs 37 | } 38 | 39 | static func save(with languageName: String, color: String) async { 40 | var col = color 41 | if color.hasPrefix("#") { 42 | col = String(color.dropFirst()) 43 | } 44 | if let lang = Language.get(with: languageName) { 45 | if col != lang.hex { 46 | lang.update(with: languageName, color: col) 47 | } 48 | } else { 49 | try? Language(language: languageName, hex: col).insert() 50 | } 51 | mapping[languageName] = color 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /DJGithub/LoadingViewerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import UIKit 9 | 10 | class LoadingViewerViewController: UIViewController { 11 | 12 | private let manager = AuthorizationViewManager() 13 | 14 | lazy var authorizationViewController: UIViewController = { 15 | return manager.addAuthorizationView { [weak self] isSucess in 16 | guard let strongSelf = self else { return } 17 | strongSelf.loadData(isSucess) 18 | } 19 | }() 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | view.backgroundColor = .backgroundColor 24 | 25 | view.addSubview(authorizationViewController.view) 26 | authorizationViewController.view.snp.makeConstraints { make in 27 | make.edges.equalToSuperview() 28 | } 29 | } 30 | 31 | private func loadData(_ isSuccess: Bool) { 32 | guard isSuccess else { 33 | HUD.show(with: "Error to load viewer info.") 34 | return 35 | } 36 | UIApplication.shared.keyWindow?.rootViewController = TabBarController(nibName: nil, bundle: nil) 37 | 38 | Task { 39 | await withThrowingTaskGroup(of: Void.self) { group in 40 | group.addTask { 41 | await DeveloperGroupManager.shared.updateAll() 42 | } 43 | group.addTask { 44 | await UserFollowingManager.shared.fetchUserFollowingStatus() 45 | } 46 | } 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/Data/Swift/D-SwiftUI.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "t":"SwiftUI by Example - free quick start tutorials for Swift developers", 4 | "id":"https://www.hackingwithswift.com/quick-start/swiftui" 5 | }, 6 | { 7 | "t":"Advanced SwiftUI Animations – Part 1: Paths", 8 | "id":"https://swiftui-lab.com/swiftui-animations-part1/" 9 | }, 10 | { 11 | "t":"【中文翻译】高级 SwiftUI 动画 — Part 1:Paths", 12 | "id":"https://mp.weixin.qq.com/s/5KinQfNtcovf_451UGwLQQ" 13 | }, 14 | { 15 | "t":"Advanced SwiftUI Animations – Part 2: GeometryEffect", 16 | "id":"https://swiftui-lab.com/swiftui-animations-part2/" 17 | }, 18 | { 19 | "t":"【中文翻译】高级 SwiftUI 动画 — Part 2:GeometryEffect", 20 | "id":"https://mp.weixin.qq.com/s/rE_HILLt-uxzScS7wXh5jw" 21 | }, 22 | { 23 | "t":"Advanced SwiftUI Animations - Part 3: AnimatableModifier", 24 | "id":"https://swiftui-lab.com/swiftui-animations-part3/" 25 | }, 26 | { 27 | "t":"【中文翻译】高级 SwiftUI 动画 — Part 3:AnimatableModifier", 28 | "id":"https://mp.weixin.qq.com/s/M6MW3idXS6jZJtl4Mz5DGQ" 29 | }, 30 | { 31 | "t":"Advanced SwiftUI Animations — Part 4: TimelineView", 32 | "id":"https://swiftui-lab.com/swiftui-animations-part4/" 33 | }, 34 | { 35 | "t":"【中文翻译】SwiftUI 动画进阶 — Part4:TimelineView", 36 | "id":"https://mp.weixin.qq.com/s/9OBtFNb5ddnx4_fS93Mnrg" 37 | }, 38 | { 39 | "t":"Advanced SwiftUI Animations – Part 5: Canvas", 40 | "id":"https://swiftui-lab.com/swiftui-animations-part5/" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /DJGithub/Github/AppStyle/AppStyleUtility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppStyleMaskView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/30. 6 | // 7 | 8 | import UIKit 9 | 10 | @MainActor 11 | struct AppStyleUtility { 12 | 13 | static let shared = AppStyleUtility() 14 | 15 | let tag = 8888 16 | 17 | var coverViews: [UIView] = [] 18 | 19 | func open(with style: AppStyle) { 20 | guard let window = UIApplication.shared.keyWindow else { return } 21 | blendAppStyle(with: style, for: window) 22 | } 23 | 24 | func close() { 25 | guard let window = UIApplication.shared.keyWindow else { return } 26 | for subview in window.subviews { 27 | if subview.tag == tag { 28 | subview.removeFromSuperview() 29 | } 30 | } 31 | } 32 | 33 | private func blendAppStyle(with style: AppStyle, for maskView: UIView) { 34 | guard #available(iOS 13.0, *), !hasBlended(with: maskView) else { return } 35 | 36 | let view = UIView(frame: maskView.bounds) 37 | view.isUserInteractionEnabled = false 38 | view.backgroundColor = style.color 39 | view.layer.compositingFilter = "saturationBlendMode" 40 | view.layer.zPosition = CGFloat.greatestFiniteMagnitude 41 | maskView.addSubview(view) 42 | } 43 | 44 | private func hasBlended(with maskView: UIView) -> Bool { 45 | for subView in maskView.subviews { 46 | if subView.tag == tag { 47 | return true 48 | } 49 | } 50 | return false 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/UserViewer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserViewer.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/25. 6 | // 7 | 8 | import Foundation 9 | 10 | struct UserViewer: DJCodable { 11 | var name: String? 12 | var login: String 13 | var avatarUrl: String 14 | 15 | @Default var websiteUrl: String 16 | @Default var bio: String 17 | @Default var company: String 18 | @Default var location: String 19 | @Default var email: String 20 | 21 | var createdAt: String 22 | 23 | @Default var viewerCanFollow: Bool 24 | @Default var viewerIsFollowing: Bool 25 | @Default var isViewer: Bool 26 | 27 | var following: TotalCountModel 28 | var followers: TotalCountModel 29 | var starredRepositories: TotalCountModel 30 | var repositories: TotalCountModel 31 | 32 | var pinnedItems: PinnedRepos 33 | var userContribution: UserContribution? 34 | } 35 | 36 | struct PinnedRepos: DJCodable { 37 | var nodes: [PinnedRepoNode] 38 | } 39 | 40 | struct PinnedRepoNode: DJCodable { 41 | var name: String 42 | var nameWithOwner: String 43 | var description: String? 44 | var viewerHasStarred: Bool 45 | var owner: PinnedRepoNodeOwner 46 | var stargazers: TotalCountModel 47 | var primaryLanguage: PrimaryLanguage? 48 | } 49 | 50 | extension PinnedRepoNode { 51 | struct PinnedRepoNodeOwner: DJCodable { 52 | var avatarUrl: String 53 | } 54 | } 55 | 56 | struct PrimaryLanguage: DJCodable { 57 | var name: String 58 | var color: String 59 | } 60 | 61 | struct TotalCountModel: DJCodable { 62 | var totalCount: Int 63 | } 64 | -------------------------------------------------------------------------------- /DJGithub/Github/Utils/GithubTrending/GithubTrendingItemsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GithubTrendingItemsManager.swift 3 | // DJGithub 4 | // 5 | // Created by polaris dev on 2023/1/30. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GithubTrendingItemsManager { 11 | static let shared = GithubTrendingItemsManager() 12 | 13 | func load(with type: TrendingType) -> T? { 14 | do { 15 | return try JSONSerialization.loadJSON(withFilename: type.filename) 16 | } catch { 17 | print("Error to load data: \(error)") 18 | } 19 | return nil 20 | } 21 | 22 | func save(with type: TrendingType, items: [T]) { 23 | guard let dict = DJEncoder(model: items).encode() else { return } 24 | do { 25 | try JSONSerialization.save(jsonObject: dict, toFilename: type.filename) 26 | } catch { 27 | print("Error to save file: \(error)") 28 | } 29 | } 30 | 31 | } 32 | 33 | extension GithubTrendingItemsManager { 34 | enum TrendingType { 35 | case repo 36 | case developer 37 | 38 | var dirName: String { 39 | return "GithubTrending" 40 | } 41 | 42 | var filename: String { 43 | switch self { 44 | case .repo: return "repo" 45 | case .developer: return "developer" 46 | } 47 | } 48 | 49 | var filePath: URL? { 50 | guard let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil } 51 | return documentURL.appendingPathComponent(filename, isDirectory: false) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/lib-Concurrency.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sectionName":"介绍", 4 | "id":2111231520, 5 | "issues":[ 6 | { 7 | "id":2111231521, 8 | "title":"Swift Concurrency 是什么?", 9 | "number":63 10 | }, 11 | { 12 | "id":2111231524, 13 | "title":"async/await", 14 | "number":64 15 | }, 16 | { 17 | "id":2111231526, 18 | "title":"Async Sequences", 19 | "number":65 20 | }, 21 | { 22 | "id":2111231528, 23 | "title":"结构化并发", 24 | "number":66 25 | }, 26 | { 27 | "id":2111231530, 28 | "title":"Actors", 29 | "number":67 30 | }, 31 | { 32 | "id":2206271528, 33 | "title":"Distributed Actors", 34 | "number":178 35 | }, 36 | { 37 | "id":2111231532, 38 | "title":"相关提案", 39 | "number":68 40 | }, 41 | { 42 | "id":2111231533, 43 | "title":"学习路径", 44 | "number":69 45 | }, 46 | { 47 | "id":2111231534, 48 | "title":"Swift Concurrency 和 Combine", 49 | "number":70 50 | }, 51 | { 52 | "id":2206271523, 53 | "title":"Concurrency 5.7 版本更新", 54 | "number":177 55 | } 56 | ] 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /DJGithub/DJFoundation/Utils/DJParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DJParser.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/15. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DJDecoder { 11 | let decoder = JSONDecoder() 12 | 13 | private var data: Data? 14 | 15 | init(dict: [String: Any]) { 16 | let data = try? JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) 17 | self.init(data: data) 18 | } 19 | 20 | init(data: Data?) { 21 | self.data = data 22 | decoder.keyDecodingStrategy = .convertFromSnakeCase 23 | } 24 | 25 | init(value: Any) { 26 | let data = try? JSONSerialization.data(withJSONObject: value) 27 | self.init(data: data) 28 | } 29 | 30 | func decode() throws -> T? { 31 | guard let data = data else { return nil } 32 | do { 33 | return try decoder.decode(T.self, from: data) 34 | } catch { 35 | throw DJError.parseError("\(error)") 36 | } 37 | } 38 | } 39 | 40 | struct DJEncoder { 41 | let encoder = JSONEncoder() 42 | 43 | let model: T? 44 | 45 | init(model: T?) { 46 | self.model = model 47 | encoder.keyEncodingStrategy = .convertToSnakeCase 48 | } 49 | 50 | func encode() -> Any? { 51 | guard let model = model else { return nil } 52 | do { 53 | let data = try encoder.encode(model) 54 | return try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) 55 | } catch { 56 | print("JDEncode Error: \(error)") 57 | } 58 | return nil 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /DJGithub/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/Search/Views/SearchWordHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchWordHeaderView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/10/3. 6 | // 7 | 8 | import UIKit 9 | 10 | class SearchWordHeaderView: UIView { 11 | 12 | var eraseClosure: (() -> Void)? 13 | 14 | lazy var titleLabel: UILabel = { 15 | let label = UILabel() 16 | label.text = "History search words" 17 | label.textColor = .black 18 | label.font = UIFont.systemFont(ofSize: 14, weight: .bold) 19 | return label 20 | }() 21 | 22 | lazy var eraseImageView: UIImageView = { 23 | let imageView = UIImageView() 24 | imageView.image = ImageDecoder.downsampledImage( 25 | data: UIImage(named: "icons-erase")!.pngData()!, to: CGSize(width: 25, height: 25), scale: 1) 26 | imageView.isUserInteractionEnabled = true 27 | let tap = UITapGestureRecognizer(target: self, action: #selector(eraseAction)) 28 | imageView.addGestureRecognizer(tap) 29 | return imageView 30 | }() 31 | 32 | init() { 33 | super.init(frame: .zero) 34 | 35 | setUp() 36 | } 37 | 38 | @objc func eraseAction() { 39 | eraseClosure?() 40 | } 41 | 42 | private func setUp() { 43 | addSubview(titleLabel) 44 | addSubview(eraseImageView) 45 | titleLabel.snp.makeConstraints { make in 46 | make.centerY.equalTo(self) 47 | make.leading.equalTo(12) 48 | } 49 | eraseImageView.snp.makeConstraints { make in 50 | make.centerY.equalTo(self) 51 | make.trailing.equalTo(-13) 52 | make.width.height.equalTo(25) 53 | } 54 | } 55 | 56 | required init?(coder: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /DJGithub/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DJGithub/Github/Model/Repo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Repo.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/9/16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Repo: DJCodable { 11 | var name: String 12 | var fullName: String 13 | var forksCount: Int 14 | var stargazersCount: Int 15 | var watchersCount: Int 16 | var openIssuesCount: Int 17 | var size: Int 18 | var description: String? 19 | var updatedAt: String 20 | var language: String? 21 | var defaultBranch: String? 22 | 23 | var owner: RepoOwner? 24 | var license: License? 25 | 26 | var desc: String { 27 | return self.description ?? "No description provided." 28 | } 29 | } 30 | 31 | extension Repo { 32 | var id: UUID { 33 | return UUID() 34 | } 35 | } 36 | 37 | struct RepoOwner: DJCodable { 38 | var avatarUrl: String 39 | var id: Int 40 | var login: String 41 | var type: String 42 | } 43 | 44 | struct License: DJCodable { 45 | var name: String 46 | var key: String 47 | var url: String? 48 | var spdxId: String? 49 | 50 | var licenseKey: String { 51 | return self.spdxId ?? self.key 52 | } 53 | } 54 | 55 | extension Repo { 56 | #if DEBUG 57 | static let sample = Repo( 58 | name: "DJGithub", 59 | fullName: "dyljqq/DJGithub", 60 | forksCount: 10, 61 | stargazersCount: 15, 62 | watchersCount: 3, 63 | openIssuesCount: 200, 64 | size: 10, 65 | updatedAt: "2023-02-17", 66 | language: "Swift", 67 | owner: repoOwner 68 | ) 69 | 70 | static let repoOwner = RepoOwner( 71 | avatarUrl: "https://avatars.githubusercontent.com/u/8120438?v=4", 72 | id: 1, 73 | login: "dyljqq", 74 | type: "User" 75 | ) 76 | 77 | #endif 78 | } 79 | -------------------------------------------------------------------------------- /DJGithub.xcodeproj/xcuserdata/jiqinqiang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DJGithub/Github/Widget/AvatarImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarImageView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/10. 6 | // 7 | 8 | import UIKit 9 | 10 | class AvatarImageView: UIImageView { 11 | 12 | struct AvatarModel { 13 | let imageUrl: String? 14 | let placeholder: UIImage? 15 | let userName: String? 16 | 17 | init(imageUrl: String?, placeholder: UIImage? = nil, userName: String?) { 18 | self.imageUrl = imageUrl 19 | self.placeholder = placeholder 20 | self.userName = userName 21 | } 22 | } 23 | 24 | private var model: AvatarModel? 25 | 26 | init() { 27 | super.init(frame: .zero) 28 | setUp() 29 | } 30 | 31 | func render(with model: AvatarModel) { 32 | self.model = model 33 | self.setImage(with: model.imageUrl, placeHolder: model.placeholder) 34 | } 35 | 36 | @objc func jumpAction() { 37 | jumpToUserViewController(with: model?.userName) 38 | } 39 | 40 | private func setUp() { 41 | let tap = UITapGestureRecognizer(target: self, action: #selector(jumpAction)) 42 | self.isUserInteractionEnabled = true 43 | self.addGestureRecognizer(tap) 44 | } 45 | 46 | required init?(coder: NSCoder) { 47 | fatalError("init(coder:) has not been implemented") 48 | } 49 | 50 | } 51 | 52 | extension AvatarImageView { 53 | fileprivate func jumpToUserViewController(with userName: String?) { 54 | guard let userName = userName, !userName.isEmpty else { return } 55 | var next = self.next 56 | while next != nil { 57 | if let vc = next as? UIViewController { 58 | if let navi = vc.navigationController { 59 | navi.pushToUser(with: userName) 60 | } 61 | return 62 | } 63 | next = next?.next 64 | } 65 | return 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/Resources/Data/Swift/D-Swift 技术.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "t":"Auto-generated documentation for Swift. Command-click no more.", 4 | "d":"自动生成的 Swift 文档", 5 | "id":"https://swiftdoc.org/" 6 | }, 7 | { 8 | "t":"Swift 5.1 Cheat Sheet and Quick Reference", 9 | "id":"https://www.raywenderlich.com/6362977-swift-5-1-cheat-sheet-and-quick-reference" 10 | }, 11 | { 12 | "t":"Learn swift in Y Minutes", 13 | "d":"快速学习 Swift", 14 | "id":"https://learnxinyminutes.com/docs/swift/" 15 | }, 16 | { 17 | "t":"Stanford University's course CS193p (Developing Applications for iOS using SwiftUI)", 18 | "d":"著名斯坦福 iOS 教程", 19 | "id":"https://cs193p.sites.stanford.edu/" 20 | }, 21 | { 22 | "t":"Preface · Beginning iOS Programming with Swift and SwiftUI (iOS 15) - Sample", 23 | "d":"从零到上架", 24 | "id":"https://www.appcoda.com/learnswift/" 25 | }, 26 | { 27 | "t":"Discover The Standard Library on Swift by Sundell", 28 | "d":"探索标准库", 29 | "id":"https://www.swiftbysundell.com/discover/standard-library/" 30 | }, 31 | { 32 | "t":"100 Days of Swift – Hacking with Swift", 33 | "id":"https://www.hackingwithswift.com/100" 34 | }, 35 | { 36 | "t":"Swift Open-Source Projects", 37 | "d":"Discover trending Swift open-source projects", 38 | "id":"https://www.libhunt.com/l/swift" 39 | }, 40 | { 41 | "t":"iOS Example", 42 | "d":"A nice collection of often useful examples done in Objective-C and Swift.", 43 | "id":"https://iosexample.com/" 44 | }, 45 | { 46 | "t":"Custom Controls for iOS, iPadOS, macOS, tvOS, and watchOS - Cocoa Controls", 47 | "id":"https://www.cocoacontrols.com/" 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /DJGithub/Github/ViewController/User/SettingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingView.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2023/1/31. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SettingRow: View { 11 | let type: SettingType 12 | 13 | @State var isSync: Bool = false 14 | 15 | var body: some View { 16 | HStack { 17 | switch type { 18 | case .signUp: 19 | signUp 20 | case .iCloud: 21 | Text(type.title) 22 | Spacer() 23 | Toggle("sync to iCloud", isOn: $isSync) 24 | default: 25 | Text(type.title) 26 | Spacer() 27 | if let image = UIImage(named: type.iconName) { 28 | Image(uiImage: image) 29 | .resizable() 30 | .frame(width: 20, height: 20) 31 | } 32 | } 33 | } 34 | } 35 | 36 | var signUp: some View { 37 | Text(type.title) 38 | .font(.system(size: 18)) 39 | .foregroundColor(.blue) 40 | } 41 | 42 | } 43 | 44 | struct SettingView: View { 45 | 46 | @State var setting = Setting() 47 | 48 | var itemDidSelectedClosure: (SettingType) -> Void 49 | 50 | var body: some View { 51 | List { 52 | ForEach(setting.dataSource, id: \.id) { section in 53 | Section(section.name) { 54 | ForEach(0.. SQLiteDatabase? { 13 | let db: SQLiteDatabase? 14 | do { 15 | let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 16 | .appendingPathComponent("github.sqlite") 17 | db = try SQLiteDatabase.open(path: fileURL.path) 18 | print("Successfully opened connection to database.") 19 | } catch { 20 | print("Unable to open database.") 21 | db = nil 22 | } 23 | 24 | return db 25 | } 26 | 27 | class ConfigManager: @unchecked Sendable { 28 | 29 | static let shared = ConfigManager() 30 | var config: Config = Config() 31 | 32 | let rssFeedManager = RssFeedManager.shared 33 | let languageManager = LanguageManager() 34 | 35 | static var viewer: UserViewer? { 36 | return LocalUserManager.getUser() 37 | } 38 | 39 | var viewerName: String { 40 | return ConfigManager.viewer?.login ?? LocalUserManager.getViewerName() 41 | } 42 | 43 | static var isLoadedViewer: Bool { 44 | return !LocalUserManager.getViewerName().isEmpty 45 | } 46 | 47 | func load() { 48 | loadConfig() 49 | } 50 | 51 | func loadConfig(completionHandler: ((Config) -> Void)? = nil) { 52 | config = loadBundleJSONFile("config") 53 | } 54 | 55 | static func checkOwner(by userName: String) -> Bool { 56 | if let viewer = viewer { 57 | return viewer.login == userName 58 | } 59 | return userName == ConfigManager.shared.config.userName 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /DJGithub/Pamphlet/View/PamphletSimpleCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PamphletSimpleCell.swift 3 | // DJGithub 4 | // 5 | // Created by jiqinqiang on 2022/11/20. 6 | // 7 | 8 | import UIKit 9 | 10 | class PamphletSimpleCell: UITableViewCell { 11 | 12 | lazy var titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.textColor = .textColor 15 | label.font = UIFont.systemFont(ofSize: 14) 16 | return label 17 | }() 18 | 19 | lazy var iconImageView: UIImageView = { 20 | let imageView = UIImageView() 21 | return imageView 22 | }() 23 | 24 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 25 | super.init(style: style, reuseIdentifier: reuseIdentifier) 26 | 27 | setUp() 28 | } 29 | 30 | func render(with model: PamphletViewController.SectionItemModel?) { 31 | guard let model else { return } 32 | if let imageName = model.imageName, !imageName.isEmpty { 33 | iconImageView.image = UIImage(named: imageName) 34 | titleLabel.snp.updateConstraints { make in 35 | make.leading.equalTo(52) 36 | } 37 | } else { 38 | titleLabel.snp.updateConstraints { make in 39 | make.leading.equalTo(12) 40 | } 41 | } 42 | titleLabel.text = model.title 43 | } 44 | 45 | private func setUp() { 46 | contentView.backgroundColor = .white 47 | 48 | contentView.addSubview(titleLabel) 49 | contentView.addSubview(iconImageView) 50 | 51 | iconImageView.snp.makeConstraints { make in 52 | make.centerY.equalToSuperview() 53 | make.leading.equalTo(12) 54 | make.width.height.equalTo(30) 55 | } 56 | titleLabel.snp.makeConstraints { make in 57 | make.leading.equalTo(52) 58 | make.trailing.equalToSuperview().offset(-12) 59 | make.centerY.equalTo(iconImageView) 60 | } 61 | } 62 | 63 | required init?(coder: NSCoder) { 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | 67 | } 68 | --------------------------------------------------------------------------------