Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] #400 - 솝탬프 신고 기능 및 네트워크 오류 모달 추가 #403

Merged
merged 9 commits into from
Oct 16, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public extension String {

// 앞쪽에 띄어쓰기가 없으면, 중간 인덱스 뒷쪽의 첫번째 인덱스
if spaceIndex == nil {
spaceIndex = self[middleIndex...endIndex].firstIndex(of: " ")
spaceIndex = self[middleIndex..<endIndex].firstIndex(of: " ")
}

var result: String = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public struct UserDefaultKeyList {
public struct AppNotice {
@UserDefaultWrapper<String>(key: "checkedAppVersion") public static var checkedAppVersion
}

public struct Soptamp {
@UserDefaultWrapper<String>(key: "reportUrl") public static var reportUrl
}
}

extension UserDefaultKeyList {
Expand Down
15 changes: 14 additions & 1 deletion SOPT-iOS/Projects/Data/Sources/Repository/MainRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ public class MainRepository {
private let configService: ConfigService
private let descriptionService: DescriptionService
private let pokeService: PokeService
private let stampService: StampService

private let cancelBag = CancelBag()

public init(userService: UserService, configService: ConfigService, descriptionService: DescriptionService, pokeService: PokeService) {
public init(userService: UserService,
configService: ConfigService,
descriptionService: DescriptionService,
pokeService: PokeService,
stampService: StampService
) {
self.userService = userService
self.configService = configService
self.descriptionService = descriptionService
self.pokeService = pokeService
self.stampService = stampService
}
}

Expand Down Expand Up @@ -98,4 +105,10 @@ extension MainRepository: MainRepositoryInterface {
.map { $0.toDomain() }
.eraseToAnyPublisher()
}

public func getReportUrl() -> AnyPublisher<SoptampReportUrlModel, Error> {
stampService.getReportUrl()
.map { $0.toDomain() }
.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class MissionListRepository {
}

extension MissionListRepository: MissionListRepositoryInterface {

public func fetchMissionList(type: MissionListFetchType, userName: String?) -> AnyPublisher<[MissionListModel], Error> {
guard let userName else {
return fetchMissionList(type: type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ public extension MissionListEntity {
}
}
}

extension SoptampReportUrlEntity {
func toDomain() -> SoptampReportUrlModel {
return SoptampReportUrlModel(reportUrl: reportUrl)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ extension AppDelegate {
userService: DefaultUserService(),
configService: DefaultConfigService(),
descriptionService: DefaultDescriptionService(),
pokeService: DefaultPokeService()
pokeService: DefaultPokeService(),
stampService: DefaultStampService()
)
}
)
Expand Down
19 changes: 19 additions & 0 deletions SOPT-iOS/Projects/Domain/Sources/Model/SoptampReportUrlModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// SoptampReportURLModel.swift
// Domain
//
// Created by Jae Hyun Lee on 10/14/24.
// Copyright © 2024 SOPT-iOS. All rights reserved.
//

import Foundation

// MARK: - SoptampReportURLModel

public struct SoptampReportUrlModel: Codable {
public let reportUrl: String

public init(reportUrl: String) {
self.reportUrl = reportUrl
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ public protocol MainRepositoryInterface {
func checkPokeNewUser() -> AnyPublisher<Bool, Error>
func appService() -> AnyPublisher<[AppServiceModel], Error>
func hotBoard() -> AnyPublisher<HotBoardModel, Error>
func getReportUrl() -> AnyPublisher<SoptampReportUrlModel, Error>
}
11 changes: 11 additions & 0 deletions SOPT-iOS/Projects/Domain/Sources/UseCase/MainUseCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public protocol MainUseCase {
func checkPokeNewUser()
func getAppService()
func getHotBoard()
func getReportUrl()
}

public class DefaultMainUseCase {
Expand Down Expand Up @@ -140,4 +141,14 @@ extension DefaultMainUseCase: MainUseCase {
}
.store(in: cancelBag)
}

public func getReportUrl() {
repository.getReportUrl()
.withUnretained(self)
.sink { event in
print("GetReportUrl State: \(event)")
} receiveValue: { owner, resultModel in
UserDefaultKeyList.Soptamp.reportUrl = resultModel.reportUrl
}.store(in: cancelBag)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public protocol MissionListUseCase {

var missionListModelsFetched: PassthroughSubject<[MissionListModel], Error> { get set }
var usersActiveGenerationInfo: PassthroughSubject<UsersActiveGenerationStatusViewResponse, Error> { get set }
var errorOccurred: PassthroughSubject<Void, Never> { get set }
}

public class DefaultMissionListUseCase {
Expand All @@ -26,17 +27,22 @@ public class DefaultMissionListUseCase {
private var cancelBag = CancelBag()
public var missionListModelsFetched = PassthroughSubject<[MissionListModel], Error>()
public var usersActiveGenerationInfo = PassthroughSubject<UsersActiveGenerationStatusViewResponse, Error>()

public var errorOccurred = PassthroughSubject<Void, Never>()

public init(repository: MissionListRepositoryInterface) {
self.repository = repository
}
}

extension DefaultMissionListUseCase: MissionListUseCase {

public func fetchMissionList(type: MissionListFetchType) {
repository.fetchMissionList(type: type, userName: nil)
.sink(receiveCompletion: { event in
print("completion: \(event)")
if case Subscribers.Completion.failure = event {
self.errorOccurred.send()
}
}, receiveValue: { model in
self.missionListModelsFetched.send(model)
})
Expand Down Expand Up @@ -73,3 +79,4 @@ extension DefaultMissionListUseCase: MissionListUseCase {
}).store(in: self.cancelBag)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ extension AlertVC.AlertTheme {
case .main:
return isNetworkErr ? DSKitAsset.Colors.white100.color : DSKitAsset.Colors.gray600.color
case .soptamp:
return isNetworkErr ? DSKitAsset.Colors.soptampError200.color : DSKitAsset.Colors.soptampGray300.color
return isNetworkErr ? DSKitAsset.Colors.black60.color : DSKitAsset.Colors.soptampGray300.color
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@ import UIKit
import DSKit

extension CustomProfileImageView {
public func setImage(with url: String) {
func setImage(with url: String) {
self.setImage(with: url, placeholder: DSKitAsset.Assets.iconDefaultProfile.image)
}

// 오늘의 솝마디에서는 Border가 들어가지 않는 것으로 통일
@discardableResult
public func setBorder() -> Self {
self.layer.borderWidth = 0
return self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public final class DailySoptuneResultPokeView: UIView {
.asDriver()

lazy var profileTap = self.profileImageView.gesture()
.filter { _ in self.user?.isAnonymous == false }
.map { _ in self.user }.asDriver()
.filter { [weak self] _ in self?.user?.isAnonymous == false }
.map { [weak self] _ in self?.user }.asDriver()

// MARK: - Properties

Expand Down Expand Up @@ -151,7 +151,7 @@ extension DailySoptuneResultPokeView {
self.nameLabel.text = model.name
self.partLabel.text = "\(model.generation)기 \(model.part)"
self.profileImageView.setImage(with: model.profileImage)
self.profileImageView.setBorder()
self.profileImageView.layer.borderWidth = 0
self.kokButton.isEnabled = !model.isAlreadyPoke
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ extension MainViewModel {
self.requestAuthorizationForNotification()
self.useCase.getServiceState()
self.trackAmplitude(event: .viewAppHome)
self.useCase.getReportUrl()
}.store(in: cancelBag)

input.hotBoardTapped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public protocol MissionListCoordinatable {
var onCurrentGenerationRankingButtonTap: ((RankingViewType) -> Void)? { get set }
var onGuideTap: (() -> Void)? { get set }
var onCellTap: ((MissionListModel, _ username: String?) -> Void)? { get set }
var onReportButtonTap: (() -> Void)? { get set }
}
public protocol ListDetailViewControllable: ViewControllable & ListDetailCoordinatable { }
public protocol ListDetailCoordinatable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Core
import BaseFeatureDependency
import StampFeatureInterface
import Domain
import SafariServices

public
final class StampCoordinator: DefaultCoordinator {
Expand Down Expand Up @@ -50,6 +51,12 @@ final class StampCoordinator: DefaultCoordinator {
missionList.onCellTap = { [weak self] model, username in
self?.runMissionDetailFlow(model, username)
}
missionList.onReportButtonTap = { [weak self] in
guard let url = UserDefaultKeyList.Soptamp.reportUrl else { return }
let safariViewController = SFSafariViewController(url: URL(string: url)!)
safariViewController.playgroundStyle()
self?.rootController?.present(safariViewController, animated: true)
}
rootController = missionList.asNavigationController
router.present(rootController, animated: true, modalPresentationSytle: .overFullScreen)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class MissionListVC: UIViewController, MissionListViewControllable {
public var onCurrentGenerationRankingButtonTap: ((RankingViewType) -> Void)?
public var onGuideTap: (() -> Void)?
public var onCellTap: ((MissionListModel, String?) -> Void)?
public var onReportButtonTap: (() -> Void)?

private var usersActiveGenerationStatus: UsersActiveGenerationStatusViewResponse?

Expand Down Expand Up @@ -265,15 +266,22 @@ extension MissionListVC {
owner.onSwiped?()
}.store(in: self.cancelBag)
}

private func bindViewModels() {
let input = MissionListViewModel.Input(
viewDidLoad: Driver<Void>.just(()),
viewWillAppear: viewWillAppear.asDriver(),
missionTypeSelected: missionTypeMenuSelected
)

let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag)

naviBar.reportButtonTapped
.withUnretained(self)
.sink { owner, _ in
owner.onReportButtonTap?()
}.store(in: cancelBag)

output.$missionListModel
.compactMap { $0 }
.sink { [weak self] model in
Expand All @@ -289,6 +297,12 @@ extension MissionListVC {
self?.remakeButtonConstraint()
self?.configureCurrentGenerationButton(with: String(describing: generationStatus.currentGeneration))
}.store(in: self.cancelBag)

output.needNetworkAlert
.withUnretained(self)
.sink { owner, _ in
owner.showNetworkAlert()
}.store(in: self.cancelBag)
}
}

Expand Down Expand Up @@ -397,6 +411,13 @@ extension MissionListVC {
attributedStr.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.white, range: NSMakeRange(0, attributedStr.length))
self.currentGenerationRankFloatingButton.setAttributedTitle(attributedStr, for: .normal)
}

private func showNetworkAlert() {
AlertUtils.presentNetworkAlertVC(
theme: .soptamp,
animated: true
)
}
}

// MARK: - UICollectionViewDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class MissionListViewModel: ViewModelType {
public class Output: NSObject {
@Published var missionListModel: [MissionListModel]?
@Published var usersActivateGenerationStatus: UsersActiveGenerationStatusViewResponse?
@Published var reportUrl: SoptampReportUrlModel?
var needNetworkAlert = PassthroughSubject<Void, Never>()
}

// MARK: - init
Expand Down Expand Up @@ -65,7 +67,7 @@ extension MissionListViewModel {
.sink { owner, fetchType in
owner.useCase.fetchMissionList(type: fetchType)
}.store(in: cancelBag)

return output
}

Expand Down Expand Up @@ -100,5 +102,11 @@ extension MissionListViewModel {
.sink { usersActivateGenerationStatus in
output.usersActivateGenerationStatus = usersActivateGenerationStatus
}.store(in: self.cancelBag)

self.useCase.errorOccurred
.asDriver()
.sink { _ in
output.needNetworkAlert.send()
}.store(in: cancelBag)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_report.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading