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

[refactor] 모임 내 약속 조회 기능 수정 및 QA 반영 #374

Merged
merged 9 commits into from
Sep 10, 2024
2 changes: 1 addition & 1 deletion KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2270,7 +2270,7 @@
PRODUCT_BUNDLE_IDENTIFIER = KkuMulKum.yizihn;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = KkumulkumRelease;
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = KkumulkumRelease1;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
1 change: 0 additions & 1 deletion KkuMulKum/Network/DTO/ResponseBody/ResponseBodyDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@ struct ErrorResponse: Codable {
let code: Int
let message: String
}

161 changes: 18 additions & 143 deletions KkuMulKum/Network/Service/MeetingService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,25 @@ extension MeetingService: MeetingInfoServiceProtocol {
return try await request(with: .fetchMeetingMember(meetingID: meetingID))
}

func fetchMeetingPromiseList(
func fetchMeetingPromiseList(with meetingID: Int) -> Single<ResponseBodyDTO<MeetingPromisesModel>> {
return provider.rx.request(.fetchMeetingPromiseList(meetingID: meetingID))
.map(ResponseBodyDTO<MeetingPromisesModel>.self)
.catch { error in
print(">>> 에러 발생: \(error.localizedDescription) : \(#function) : \(Self.self)")
return .error(error)
}
}

func fetchParticipatedPromiseList(
with meetingID: Int,
isParticipant: Bool?
) async throws -> ResponseBodyDTO<MeetingPromisesModel>? {
guard let isParticipant else {
return try await request(with: .fetchMeetingPromiseList(meetingID: meetingID))
}
return try await request(with: .fetchParticipatedPromiseList(meetingID: meetingID, isParticipant: isParticipant))
isParticipant: Bool
) -> Single<ResponseBodyDTO<MeetingPromisesModel>> {
return provider.rx.request(.fetchParticipatedPromiseList(meetingID: meetingID, isParticipant: isParticipant))
.map(ResponseBodyDTO<MeetingPromisesModel>.self)
.catch { error in
print(">>> 에러 발생: \(error.localizedDescription) : \(#function) : \(Self.self)")
return .error(error)
}
}

func exitMeeting(with meetingID: Int) -> Single<ResponseBodyDTO<EmptyModel>> {
Expand Down Expand Up @@ -90,139 +101,3 @@ extension MeetingService: InviteCodeServiceProtocol {
return try await self.request(with: .joinMeeting(request: request))
}
}

final class MockMeetingInfoService: MeetingInfoServiceProtocol {
func fetchMeetingInfo(with meetingID: Int) -> ResponseBodyDTO<MeetingInfoModel>? {
let mockData = MeetingInfoModel(
meetingID: 1,
name: "웅웅난진웅",
createdAt: "2024.06.08",
metCount: 3,
invitationCode: "WD56CQ"
)

return ResponseBodyDTO(success: true, data: mockData, error: nil)
}

func fetchMeetingMemberList(with meetingID: Int) -> ResponseBodyDTO<MeetingMembersModel>? {
let mockData = MeetingMembersModel(
memberCount: 14,
members: [
Member(
memberID: 1,
name: "김진웅",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 2,
name: "김수연",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 3,
name: "이지훈",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 4,
name: "이유진",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 5,
name: "이승현",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 6,
name: "허준혁",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 7,
name: "배차은우",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 8,
name: "김윤서",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 9,
name: "정혜진",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 10,
name: "주효은",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 11,
name: "박상준",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 12,
name: "김채원",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 13,
name: "류희재",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
),
Member(
memberID: 14,
name: "김민지",
profileImageURL: "https://reqres.in/img/faces/\(Int.random(in: 1...10))-image.jpg"
)
]
)

return ResponseBodyDTO(success: true, data: mockData, error: nil)
}

func fetchMeetingPromiseList(
with meetingID: Int,
isParticipant: Bool?
) async throws -> ResponseBodyDTO<MeetingPromisesModel>? {
let mockData = MeetingPromisesModel(
promises: [
MeetingPromise(promiseID: 1,name: "꾸물 리프레시 데이",dDay: 0,time: "PM 2:00",placeName: "DMC역"),
MeetingPromise(promiseID: 2,name: "꾸물 잼얘 나이트",dDay: 10,time: "PM 6:00",placeName: "홍대입구"),
MeetingPromise(promiseID: 3,name: "친구 생일 파티",dDay: 5,time: "PM 7:00",placeName: "강남역"),
MeetingPromise(promiseID: 4,name: "주말 산책",dDay: 3,time: "AM 10:00",placeName: "서울숲"),
MeetingPromise(promiseID: 5,name: "프로젝트 미팅",dDay: 1,time: "AM 9:00",placeName: "삼성역"),
MeetingPromise(promiseID: 6,name: "독서 모임",dDay: 7,time: "PM 3:00",placeName: "합정역"),
MeetingPromise(promiseID: 7,name: "헬스클럽 모임",dDay: 2,time: "AM 8:00",placeName: "신촌역"),
MeetingPromise(promiseID: 8,name: "영화 관람",dDay: 4,time: "PM 8:00",placeName: "잠실역"),
MeetingPromise(promiseID: 9,name: "저녁 식사",dDay: 6,time: "PM 7:30",placeName: "이태원역"),
MeetingPromise(promiseID: 10,name: "아침 조깅",dDay: 14,time: "AM 6:00",placeName: "한강공원"),
MeetingPromise(promiseID: 11,name: "커피 브레이크",dDay: 8,time: "PM 4:00",placeName: "을지로입구"),
MeetingPromise(promiseID: 12,name: "스터디 그룹",dDay: 12,time: "PM 5:00",placeName: "강남역"),
MeetingPromise(promiseID: 13,name: "뮤직 페스티벌",dDay: 9,time: "PM 2:00",placeName: "난지공원"),
MeetingPromise(promiseID: 14, name: "낚시 여행", dDay: 11, time: "AM 5:00", placeName: "속초항"),
MeetingPromise(promiseID: 15, name: "가족 모임", dDay: 13, time: "PM 1:00", placeName: "광화문역")
]
)

return ResponseBodyDTO(success: true, data: mockData, error: nil)
}

func exitMeeting(with meetingID: Int) -> Single<ResponseBodyDTO<EmptyModel>> {
let falseResponse = ResponseBodyDTO<EmptyModel>(success: false, data: nil, error: nil)
return .just(falseResponse)
}
}

final class MockInviteCodeService: InviteCodeServiceProtocol {
func joinMeeting(with request: RegisterMeetingsModel) -> ResponseBodyDTO<RegisterMeetingsResponseModel>? {
let mockData = RegisterMeetingsResponseModel(
meetingID: 1
)

return ResponseBodyDTO<RegisterMeetingsResponseModel>.init(success: true, data: mockData, error: nil)
}
}
7 changes: 7 additions & 0 deletions KkuMulKum/Resource/Base/BaseViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ extension BaseViewController {
.font: UIFont.pretendard(.body03)
]

let barAppearance = UINavigationBarAppearance()
barAppearance.backgroundColor = .white
barAppearance.shadowColor = nil

navigationController?.navigationBar.standardAppearance = barAppearance
navigationController?.navigationBar.scrollEdgeAppearance = barAppearance

if !isBorderHidden {
addBorder()
}
Expand Down
56 changes: 20 additions & 36 deletions KkuMulKum/Source/MeetingInfo/Cell/MeetingMemberCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import SnapKit
import Then

protocol MeetingMemberCellDelegate: AnyObject {
func profileImageButtonDidTap()
func profileImageViewDidTap()
}

final class MeetingMemberCell: BaseCollectionViewCell {
private let profileImageButton = UIButton().then {
private let profileImageView: UIImageView = UIImageView().then {
$0.isUserInteractionEnabled = true
$0.layer.cornerRadius = Screen.height(64) / 2
$0.isEnabled = false
$0.clipsToBounds = true
}

Expand All @@ -28,47 +28,34 @@ final class MeetingMemberCell: BaseCollectionViewCell {
}

private weak var delegate: MeetingMemberCellDelegate?
private lazy var tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageViewDidTap(_:)))

override func prepareForReuse() {
super.prepareForReuse()

profileImageButton.do {
var config = UIButton.Configuration.plain()
config.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)

$0.imageView?.image = nil
$0.backgroundColor = .clear
$0.isEnabled = false
}
profileImageView.image = nil
profileImageView.removeGestureRecognizer(tapGesture)

nameLabel.setText(style: .caption02, color: .gray6)
}

override func setupView() {
contentView.addSubviews(profileImageButton, nameLabel)
contentView.addSubviews(profileImageView, nameLabel)
}

override func setupAutoLayout() {
profileImageButton.snp.makeConstraints {
profileImageView.snp.makeConstraints {
$0.top.centerX.equalToSuperview()
$0.height.equalTo(Screen.height(64))
$0.width.equalTo(profileImageButton.snp.height)
$0.width.equalTo(profileImageView.snp.height)
}

nameLabel.snp.makeConstraints {
$0.top.equalTo(profileImageButton.snp.bottom).offset(4)
$0.centerX.equalTo(profileImageButton)
$0.top.equalTo(profileImageView.snp.bottom).offset(4)
$0.centerX.equalTo(profileImageView)
$0.bottom.equalToSuperview()
}
}

override func setupAction() {
profileImageButton.addTarget(
self,
action: #selector(profileImageButtonDidTap(_:)),
for: .touchUpInside
)
}
}

extension MeetingMemberCell {
Expand All @@ -91,15 +78,12 @@ private extension MeetingMemberCell {
func configureForAdd(with delegate: MeetingMemberCellDelegate) {
self.delegate = delegate

profileImageButton.do {
var config = UIButton.Configuration.plain()
config.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0)

$0.configuration = config
profileImageView.do {
$0.image = .iconPlusDark.withRenderingMode(.alwaysOriginal)
$0.contentMode = .center
$0.backgroundColor = .gray1
$0.setImage(.iconPlus.withTintColor(.gray4), for: .normal)
$0.isEnabled = true
}
profileImageView.addGestureRecognizer(tapGesture)

nameLabel.setText(style: .caption02, color: .gray6)
}
Expand All @@ -110,15 +94,15 @@ private extension MeetingMemberCell {

nameLabel.setText(name ?? " ", style: .caption02, color: .gray6)

profileImageButton.kf.setImage(
profileImageView.kf.setImage(
with: imageURL,
for: .disabled,
placeholder: .imgProfile.withRenderingMode(.alwaysOriginal)
placeholder: UIImage(resource: .imgProfile).withRenderingMode(.alwaysOriginal)
)
profileImageView.contentMode = .scaleAspectFill
}

@objc
func profileImageButtonDidTap(_ sender: UIButton) {
delegate?.profileImageButtonDidTap()
func profileImageViewDidTap(_ sender: UIImageView) {
delegate?.profileImageViewDidTap()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import RxSwift
protocol MeetingInfoServiceProtocol {
func fetchMeetingInfo(with meetingID: Int) async throws -> ResponseBodyDTO<MeetingInfoModel>?
func fetchMeetingMemberList(with meetingID: Int) async throws -> ResponseBodyDTO<MeetingMembersModel>?
func fetchMeetingPromiseList(with meetingID: Int, isParticipant: Bool?) async throws -> ResponseBodyDTO<MeetingPromisesModel>?
func fetchMeetingPromiseList(with meetingID: Int) -> Single<ResponseBodyDTO<MeetingPromisesModel>>
func fetchParticipatedPromiseList(
with meetingID: Int,
isParticipant: Bool
) -> Single<ResponseBodyDTO<MeetingPromisesModel>>
func exitMeeting(with meetingID: Int) -> Single<ResponseBodyDTO<EmptyModel>>
}
4 changes: 3 additions & 1 deletion KkuMulKum/Source/MeetingInfo/View/MeetingInfoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ final class MeetingInfoView: BaseView {
$0.setText("남은 약속을 확인해보세요", style: .body01, color: .gray7)
}

private let segmentedControl = UnderlineSegmentedControl(items: ["내가 속한 약속", "모든 약속"])
private let segmentedControl = UnderlineSegmentedControl(items: ["내가 속한 약속", "모든 약속"]).then {
$0.selectedSegmentIndex = 0
}

private let emptyDescriptionView = UIView(backgroundColor: .white).then {
$0.layer.cornerRadius = 8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private extension MeetingInfoViewController {

output.navigateToPromiseInfo
.drive(with: self) { owner, promiseID in
guard let promiseID else { return }
guard promiseID > 0 else { return }

let pagePromiseViewController = PromiseViewController(
viewModel: PromiseViewModel(promiseID: promiseID, service: PromiseService())
Expand Down Expand Up @@ -223,7 +223,7 @@ extension MeetingInfoViewController: CustomActionSheetDelegate {
// MARK: - MeetingMemberCellDelegate

extension MeetingInfoViewController: MeetingMemberCellDelegate {
func profileImageButtonDidTap() {
func profileImageViewDidTap() {
guard let code = viewModel.meetingInvitationCode else { return }

let viewController = InvitationCodePopUpViewController(
Expand Down
Loading