Skip to content

Commit

Permalink
Merge pull request #101 from 87kangsw/feature/fix-buddy-parsing-error
Browse files Browse the repository at this point in the history
Feature/fix buddy parsing error
  • Loading branch information
87kangsw authored May 15, 2024
2 parents d376294 + 45383aa commit ec7ec0b
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 128 deletions.
28 changes: 16 additions & 12 deletions GitTime/Sources/CompositionRoot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ final class CompositionRoot {
userDefaultService: userDefaultService,
realmService: realmService)

let buddyController = configureBuddyScreen(crawlerService: crawlerService,
realmService: realmService,
userDefaultService: userDefaultService,
followService: followService,
userService: userService,
githubService: gitHubService)
let buddyController = configureBuddyScreen(
crawlerService: crawlerService,
realmService: realmService,
followService: followService,
userService: userService,
githubService: gitHubService,
keychainService: keychainService
)

let settingController = configureSettingScreen(authService: authService,
githubService: gitHubService,
Expand Down Expand Up @@ -231,10 +233,10 @@ extension CompositionRoot {
static func configureBuddyScreen(
crawlerService: GitTimeCrawlerServiceType,
realmService: RealmServiceType,
userDefaultService: UserDefaultsServiceType,
followService: FollowServiceType,
userService: UserServiceType,
githubService: GitHubServiceType
githubService: GitHubServiceType,
keychainService: KeychainServiceType
) -> BuddyViewController {

var presentFollowScreen: () -> FollowViewController
Expand All @@ -245,10 +247,12 @@ extension CompositionRoot {
return controller
}

let reactor = BuddyViewReactor(crawlerService: crawlerService,
realmService: realmService,
userDefaultService: userDefaultService,
githubService: githubService)
let reactor = BuddyViewReactor(
crawlerService: crawlerService,
realmService: realmService,
githubService: githubService,
keychainService: keychainService
)
let controller = BuddyViewController(reactor: reactor,
presentFollowScreen: presentFollowScreen)
controller.title = "Buddys"
Expand Down
10 changes: 10 additions & 0 deletions GitTime/Sources/Utils/Extensions/Date+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,13 @@ extension Date {
return now.compare(afterHour) == .orderedDescending
}
}

extension Date {
static func todayStringFormatted() -> String {
let now = Date()
let format = "yyyy-MM-dd"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.string(from: now)
}
}
68 changes: 0 additions & 68 deletions GitTime/Sources/ViewControllers/Activity/ActivityViewReactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import ReactorKit
import RxCocoa
import RxMoya
import RxSwift
import Kanna
import Kingfisher

final class ActivityViewReactor: ReactorKit.Reactor {
Expand Down Expand Up @@ -304,74 +303,7 @@ final class ActivityViewReactor: ReactorKit.Reactor {
return .fetchActivityMore(events, nextPage: 1, canLoadMore: false)
}
}

private func parseContribution(response: Response) -> ContributionInfo {
var contributionCount: Int = 0
var contributions: [Contribution] = .init()
var userName: String = ""
var additionalName: String = ""
var profileURL: String = ""

if let doc = try? HTML(html: response.data, encoding: .utf8) {
for rect in doc.css("td") {
if var date = rect["data-date"],
let dataLevel = rect["data-level"] {

date = date.replacingOccurrences(of: "\\", with: "")
.replacingOccurrences(of: "/", with: "")
.replacingOccurrences(of: "\"", with: "")

let colorType = ContributionHexColorTypes.allCases.first { $0.rawValue == Int(dataLevel) }
if let hexString = colorType?.hexString {
contributions.append(Contribution(date: date, contribution: Int(dataLevel)!, hexColor: hexString))
}
}
}


for count in doc.css("h2, f4 text-normal mb-2") {
let decimalCharacters = CharacterSet.decimalDigits
let decimalRange = count.text?.rangeOfCharacter(from: decimalCharacters)

if decimalRange != nil {
if var countText = count.text {
countText = countText.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
contributionCount = Int(countText)!
}
}
}

/*
/html/body[@class='logged-in env-production page-responsive page-profile']/div[@class='application-main ']/main[@id='js-pjax-container']/div[@class='container-xl px-3 px-md-4 px-lg-5']/div[@class='gutter-condensed gutter-lg flex-column flex-md-row d-flex']/div[@class='flex-shrink-0 col-12 col-md-3 mb-4 mb-md-0']/div[@class='h-card mt-md-n5']/div[@class='clearfix d-flex d-md-block flex-items-center mb-4 mb-md-0']/div[@class='position-relative d-inline-block col-2 col-md-12 mr-3 mr-md-0 flex-shrink-0']/a/img[@class='avatar avatar-user width-full border color-bg-primary']/@src
*/
for link in doc.css("img") {
if let imgClass = link["class"], imgClass == "avatar avatar-user width-full border color-bg-default" {
profileURL = link["src"] ?? ""
}
}

//
for span in doc.css("span") {
if let itemProp = span["itemprop"], itemProp.isNotEmpty {
if itemProp == "name" {
userName = span.content ?? ""
} else if itemProp == "additionalName" {
additionalName = span.content ?? ""
}
}
}
}

// sort by date
contributions.sort(by: { $0.date < $1.date })

return ContributionInfo(count: contributionCount,
contributions: contributions,
userName: userName,
additionalName: additionalName,
profileImageURL: profileURL)
}

private func downloadProfileImage() -> Observable<Mutation> {
return self.profileImageDownload()
.map { image -> Mutation in
Expand Down
19 changes: 2 additions & 17 deletions GitTime/Sources/ViewControllers/Buddy/BuddyViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ final class BuddyViewController: BaseViewController, ReactorKit.View {

// MARK: Views
private let addBuddyButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil)
private let modeButton = UIButton().then {
$0.setImage(UIImage(systemName: "square.grid.2x2"), for: .normal)
}
lazy var modeButtonItem = UIBarButtonItem(customView: modeButton)

private let editButton = UIBarButtonItem(barButtonSystemItem: .edit, target: nil, action: nil)

private let tableView = UITableView(frame: .zero, style: .insetGrouped).then {
Expand Down Expand Up @@ -104,7 +101,7 @@ final class BuddyViewController: BaseViewController, ReactorKit.View {
super.viewDidLoad()
self.title = "Buddys"
self.navigationItem.leftBarButtonItem = editButton
self.navigationItem.rightBarButtonItems = [addBuddyButtonItem, modeButtonItem]
self.navigationItem.rightBarButtonItems = [addBuddyButtonItem]
addNotifications()
}

Expand Down Expand Up @@ -163,11 +160,6 @@ final class BuddyViewController: BaseViewController, ReactorKit.View {

}).disposed(by: self.disposeBag)

self.modeButton.rx.tap
.map { Reactor.Action.changeViewMode }
.bind(to: reactor.action)
.disposed(by: self.disposeBag)

self.editButton.rx.tap
.subscribe(onNext: { [weak self] _ in
guard let self = self else { return }
Expand Down Expand Up @@ -204,13 +196,6 @@ final class BuddyViewController: BaseViewController, ReactorKit.View {
.bind(to: reactor.action)
.disposed(by: self.disposeBag)

reactor.state.map { $0.viewMode }
.distinctUntilChanged()
.map { $0.systemIconName }
.map { UIImage(systemName: $0) }
.bind(to: self.modeButton.rx.image(for: .normal))
.disposed(by: self.disposeBag)

reactor.state.map { $0.alreadyExistUser }
.filterNil()
.subscribe(onNext: { [weak self] (exist, userName) in
Expand Down
79 changes: 49 additions & 30 deletions GitTime/Sources/ViewControllers/Buddy/BuddyViewReactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import Foundation

import GitHubKit

import ReactorKit
import RxCocoa
import RxSwift
Expand Down Expand Up @@ -44,7 +46,6 @@ final class BuddyViewReactor: Reactor {
case checkUserExist(String?)
case clearCheckExist
case addGitHubUsername(String?)
case changeViewMode
case removeGitHubUsername(String?)
case toastMessage(String?)
case checkUpdate
Expand All @@ -55,7 +56,6 @@ final class BuddyViewReactor: Reactor {
enum Mutation {
case setBuddys([ContributionInfoObject])
case addBuddy(ContributionInfoObject)
case setViewMode(BuddyViewMode)
case removeBuddy(Int)
case setAlreadyExistUser((Bool, String)?)
case setToastMessage(String?)
Expand All @@ -66,7 +66,7 @@ final class BuddyViewReactor: Reactor {
struct State {
var isLoading: Bool = false
var isRefreshing: Bool = false
var viewMode: BuddyViewMode
var viewMode: BuddyViewMode = .daily
var buddys: [ContributionInfoObject] = []
var sections: [BuddySection] {
var sectionItems: [BuddySectionItem] = []
Expand All @@ -88,26 +88,24 @@ final class BuddyViewReactor: Reactor {

private let crawlerService: GitTimeCrawlerServiceType
private let realmService: RealmServiceType
private let userDefaultService: UserDefaultsServiceType
private let githubService: GitHubServiceType
fileprivate let keychainService: KeychainServiceType

let initialState: State

// MARK: Initializing
init(
crawlerService: GitTimeCrawlerServiceType,
realmService: RealmServiceType,
userDefaultService: UserDefaultsServiceType,
githubService: GitHubServiceType
githubService: GitHubServiceType,
keychainService: KeychainServiceType
) {
self.crawlerService = crawlerService
self.realmService = realmService
self.userDefaultService = userDefaultService
self.githubService = githubService
self.keychainService = keychainService

// let period: PeriodTypes = PeriodTypes(rawValue: userdefaultsService.value(forKey: UserDefaultsKey.period) ?? "") ?? PeriodTypes.daily
let viewMode = BuddyViewMode(rawValue: userDefaultService.value(forKey: UserDefaultsKey.buddyViewMode) ?? "yearly") ?? .yearly
initialState = State(viewMode: viewMode)
initialState = State()
}

// MARK: Mutate
Expand All @@ -128,10 +126,6 @@ final class BuddyViewReactor: Reactor {
let endLoading: Observable<Mutation> = .just(.setLoading(false))
let request = self.requestContribution(userName: userName)
return .concat(startLoading, request, endLoading)
case .changeViewMode:
let newViewMode: BuddyViewMode = (self.currentState.viewMode == .yearly) ? .daily : .yearly
self.userDefaultService.set(value: newViewMode.rawValue, forKey: UserDefaultsKey.buddyViewMode)
return .just(.setViewMode(newViewMode))
case .removeGitHubUsername(let userName):
guard let userName = userName else { return .empty() }

Expand Down Expand Up @@ -190,8 +184,6 @@ final class BuddyViewReactor: Reactor {
state.buddys = addedBuddys
case .setBuddys(let buddys):
state.buddys = buddys
case .setViewMode(let viewMode):
state.viewMode = viewMode
case .removeBuddy(let index):
var buddys = state.buddys
buddys.remove(at: index)
Expand Down Expand Up @@ -223,25 +215,52 @@ final class BuddyViewReactor: Reactor {
}
}

private func requestContribution(userName: String) -> Observable<Mutation> {
return self.crawlerService.fetchContributionsRawdata(userName: userName)
.map { response -> ContributionInfo in
let contributionInfo = self.parseContribution(response: response)
return contributionInfo
private func fetchContributions(userName: String) -> Observable<GraphQLResponse.UserContribution> {
guard let accessToken = keychainService.getAccessToken() else { return .empty() }

let githubKit = GitHubKit(config: .init(token: accessToken))

return Observable.create { observer -> Disposable in
async {
do {
let contribution = try await githubKit.contributions(
userName: userName,
from: Date.todayStringFormatted(),
to: Date.todayStringFormatted()
)
observer.onNext(contribution)
observer.onCompleted()
} catch {
observer.onError(error)
}
}

return Disposables.create {}
}
}

private func requestContribution(userName: String) -> Observable<Mutation> {

return self.fetchContributions(userName: userName)
.observe(on: MainScheduler.asyncInstance)
.flatMap { [weak self] contributionInfo -> Observable<Mutation> in
guard let self = self else { return .empty() }
guard self.checkUserNameIsValid(originalName: userName,
userName: contributionInfo.userName,
additionalName: contributionInfo.additionalName) == true else { return .empty() }

return self.realmService.addBuddy(userName: contributionInfo.userName,
additionalName: contributionInfo.additionalName,
profileURL: contributionInfo.profileImageURL,
contribution: contributionInfo.contributions)
.map { buddy -> Mutation in
.addBuddy(buddy)
return self.realmService.addBuddy(
userName: contributionInfo.profile.userName ?? "",
additionalName: contributionInfo.profile.login,
profileURL: contributionInfo.profile.profileURL ?? "",
contribution: contributionInfo.contributions.map {
Contribution(
date: $0.date,
contribution: $0.contributionCount,
hexColor: $0.color
)
}
)
.map { buddy -> Mutation in
.addBuddy(buddy)
}
}
.catch { error -> Observable<Mutation> in
log.error(error.localizedDescription)
Expand Down
2 changes: 1 addition & 1 deletion project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ packages:
branch: master
GitHubKit:
url: https://github.com/87kangsw/GitHubKit
from: 1.0.0
from: 1.0.2
fileGroups:
- GitTime/Supporting Files
targets:
Expand Down

0 comments on commit ec7ec0b

Please sign in to comment.