Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3b55215
[Add] #161 - 셀 추가
JinUng41 Apr 8, 2025
6a09153
[Add] #161 - UI 추가 및 기타 수정
JinUng41 Apr 10, 2025
56d8026
[Refactor] #161 - PreRegister를 Register로 변경
JinUng41 Apr 10, 2025
c2964d4
[Chore] #161 - 변수명 변경
JinUng41 Apr 10, 2025
5dcc6f6
[Feat] #161 - 유즈케이스, 모델 구현
JinUng41 Apr 10, 2025
65dec68
[Feat] #161 - 뷰모델 로직 연결
JinUng41 Apr 11, 2025
6ba6fb6
[Style] #161 - 셀 UI 수정
JinUng41 Apr 11, 2025
629fe20
[Feat] #161 - 뷰모델 로직 연결
JinUng41 Apr 11, 2025
0a1c883
[Refactor] #161 - 루트뷰로 분리
JinUng41 Apr 11, 2025
0cf8cb6
[Feat] #161 - 커뮤니티 기능 구현
JinUng41 Apr 11, 2025
9331ce1
[Chore] #161 - 오타 수정
JinUng41 Apr 11, 2025
e9acb14
[Add] 커뮤니티 기능 구현 시 필요한 이미지 추가
youz2me Apr 11, 2025
df5c1f8
[Setting] 테스트플라이트 업데이트를 위해 기본 빌드 방식 Release로 변경
youz2me Apr 11, 2025
50cb4dc
[Setting] 빌드 번호 변경
youz2me Apr 11, 2025
d1a9d31
[Setting] 인증서 AppStore 버전으로 변경
youz2me Apr 11, 2025
5e255c0
Merge remote-tracking branch 'refs/remotes/origin/develop'
youz2me Apr 12, 2025
220cd87
[Fix] #175 - 글 작성 화면 backgroundColor와 TextView의 backgroundColor 불일치 이…
youz2me Apr 16, 2025
18a713d
[Fix] #175 - 홈 화면 좋아요 누르고 스크롤 시 좋아요가 사라지는 이슈 해결
youz2me Apr 16, 2025
d660a52
[Fix] #175 - 상세 페이지 댓글 표시가 안되는 이슈 해결
youz2me Apr 16, 2025
5023fc8
[Fix] #175 - 홈 제목 링크 인식 안되는 이슈 해결
youz2me Apr 16, 2025
5d35609
[Fix] #175 - 로그인 시 닉네임, 프로필 URL 없을 경우 온보딩 과정 거치도록 수정
youz2me Apr 16, 2025
bfe9648
[Fix] #175 - 온보딩 시 자음도 입력 가능하도록 설정
youz2me Apr 16, 2025
b9b5018
[Fix] #175 - 글 작성 유효성 검사 이슈 해결
youz2me Apr 16, 2025
7144d60
[Fix] #175 - 댓글 작성 시 플레이스홀더가 댓글로 인식되는 문제 해결
youz2me Apr 17, 2025
eec98be
[Fix] # 175 - 댓글 버튼 터치 영역 반영 이슈 해결
youz2me Apr 18, 2025
96edde7
[Fix] #175 - 리뷰 반영
youz2me Apr 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Wable-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2421,7 +2421,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Wable-iOS/Wable-iOS.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = "";
Expand All @@ -2444,7 +2444,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.wable.Wable-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Wable_AppStore;
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Wable_Debug;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
Expand Down
12 changes: 10 additions & 2 deletions Wable-iOS/Presentation/Helper/Extension/UITextView+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,22 @@ extension UITextView {
/// ```
func setPretendard(with style: UIFont.Pretendard, text: String = " ") {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = (style.lineHeight - style.size) / 1.6
paragraphStyle.minimumLineHeight = style.size * 1.8
paragraphStyle.maximumLineHeight = style.size * 1.8

let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.pretendard(style),
.kern: style.kerning,
.paragraphStyle: paragraphStyle
.paragraphStyle: paragraphStyle,
.baselineOffset: style.baselineOffset
]

self.attributedText = NSAttributedString(string: text, attributes: attributes)

self.textContainerInset = .zero
self.textContainer.lineFragmentPadding = 0

self.setNeedsLayout()
self.layoutIfNeeded()
}
}
51 changes: 43 additions & 8 deletions Wable-iOS/Presentation/Home/View/HomeDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,17 @@ private extension HomeDetailViewController {
}

func setupDataSource() {
let contentCellRegistration = UICollectionView.CellRegistration<ContentCollectionViewCell, Content> { [weak self] cell, indexPath, item in
let contentCellRegistration = UICollectionView.CellRegistration<
ContentCollectionViewCell,
Content
> { [weak self] cell, indexPath, item in
guard let self = self else { return }

cell.configureCell(info: item.content.contentInfo, postType: .mine, cellType: .detail, likeButtonTapHandler: {
cell.configureCell(
info: item.content.contentInfo,
postType: .mine,
cellType: .detail,
likeButtonTapHandler: {
self.didContentHeartTappedSubject.send(cell.likeButton.isLiked)
})

Expand All @@ -188,13 +195,20 @@ private extension HomeDetailViewController {

self.commentTextView.text = item.content.contentInfo.author.nickname + Constant.ripplePlaceholder
self.commentTextView.textColor = .gray700

self.commentTextView.endEditing(true)
}), for: .touchUpInside)

commentTextView.text = item.content.contentInfo.author.nickname + Constant.ripplePlaceholder
self.commentTextView.textColor = .gray700
}

let commentCellRegistration = UICollectionView.CellRegistration<CommentCollectionViewCell, ContentComment> { cell, indexPath, item in
let commentCellRegistration = UICollectionView.CellRegistration<
CommentCollectionViewCell,
ContentComment
> { [weak self] cell, indexPath, item in
guard let self = self else { return }

self.userInformationUseCase.fetchActiveUserID()
.sink { id in
cell.configureCell(
Expand All @@ -217,18 +231,36 @@ private extension HomeDetailViewController {
self.commentTextView.text = item.comment.author.nickname + Constant.replyPlaceholder
self.commentTextView.textColor = .gray700

self.commentTextView.endEditing(true)
}), for: .touchUpInside)

cell.infoView.profileImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.profileImageViewDidTap)))
cell.infoView.profileImageView.addGestureRecognizer(
UITapGestureRecognizer(
target: self,
action: #selector(self.profileImageViewDidTap)
)
)
}

dataSource = DataSource(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
dataSource = DataSource(collectionView: collectionView) { (
collectionView,
indexPath,
item
) -> UICollectionViewCell? in
let section = Section.allCases[indexPath.section]
switch (section, item) {
case (.content, .content(let content)):
return collectionView.dequeueConfiguredReusableCell(using: contentCellRegistration, for: indexPath, item: content)
return collectionView.dequeueConfiguredReusableCell(
using: contentCellRegistration,
for: indexPath,
item: content
)
case (.comment, .comment(let comment)):
return collectionView.dequeueConfiguredReusableCell(using: commentCellRegistration, for: indexPath, item: comment)
return collectionView.dequeueConfiguredReusableCell(
using: commentCellRegistration,
for: indexPath,
item: comment
)
default:
return nil
}
Expand Down Expand Up @@ -377,7 +409,10 @@ extension HomeDetailViewController: UICollectionViewDelegate {

private extension HomeDetailViewController {
var collectionViewLayout: UICollectionViewCompositionalLayout {
return UICollectionViewCompositionalLayout { [weak self] (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
return UICollectionViewCompositionalLayout { [weak self] (
sectionIndex,
layoutEnvironment
) -> NSCollectionLayoutSection? in
guard let self = self else { return nil }

let section = Section.allCases[sectionIndex]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ final class WritePostViewController: NavigationViewController {
$0.textContainerInset = .zero
$0.setPretendard(with: .head1, text: Constant.titlePlaceholder)
$0.textColor = .gray700
$0.backgroundColor = .clear
}

private lazy var contentTextView: UITextView = .init().then {
$0.isScrollEnabled = false
$0.textContainerInset = .zero
$0.setPretendard(with: .body2, text: Constant.contentPlaceholder)
$0.textColor = .gray500
$0.backgroundColor = .clear
}

private lazy var imageView: UIImageView = .init().then {
Expand Down Expand Up @@ -239,7 +241,8 @@ private extension WritePostViewController {
return
}

let isEnabled = totalCount > 0 && totalCount <= 500 && titleTextView.text != Constant.titlePlaceholder
let isEnabled = totalCount > 0 && totalCount <= 500 && titleTextView.text != Constant.titlePlaceholder && !titleTextView.text.isEmpty

postButton.isEnabled = isEnabled
postButton.configuration?.baseBackgroundColor = isEnabled ? .purple50 : .gray400
}
Expand Down Expand Up @@ -342,7 +345,7 @@ extension WritePostViewController: UITextViewDelegate {
return false
}

return (newTextCount - currentCount) + otherCount + currentCount <= 500
return (newTextCount - currentCount) + otherCount + currentCount < 500
}
}

Expand Down
52 changes: 42 additions & 10 deletions Wable-iOS/Presentation/Home/ViewModel/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ final class HomeViewModel {
private let createContentLikedUseCase: CreateContentLikedUseCase
private let deleteContentLikedUseCase: DeleteContentLikedUseCase

init(fetchContentListUseCase: FetchContentListUseCase, createContentLikedUseCase: CreateContentLikedUseCase, deleteContentLikedUseCase: DeleteContentLikedUseCase) {
init(
fetchContentListUseCase: FetchContentListUseCase,
createContentLikedUseCase: CreateContentLikedUseCase,
deleteContentLikedUseCase: DeleteContentLikedUseCase
) {
self.fetchContentListUseCase = fetchContentListUseCase
self.createContentLikedUseCase = createContentLikedUseCase
self.deleteContentLikedUseCase = deleteContentLikedUseCase
Expand Down Expand Up @@ -97,16 +101,44 @@ extension HomeViewModel: ViewModelType {

input.didHeartTappedItem
.withUnretained(self)
.flatMap { owner, info -> AnyPublisher<Void, Never> in
if info.1 {
return owner.createContentLikedUseCase.execute(contentID: info.0)
.asDriver(onErrorJustReturn: ())
} else {
return owner.deleteContentLikedUseCase.execute(contentID: info.0)
.asDriver(onErrorJustReturn: ())
}
.flatMap { owner, info -> AnyPublisher<(Int, Bool), Never> in
return (info.1 ? owner.createContentLikedUseCase.execute(contentID: info.0)
: owner.deleteContentLikedUseCase.execute(contentID: info.0))
.map { _ in info }
.asDriver(onErrorJustReturn: info)
}
.sink(receiveValue: { _ in })
.sink(receiveValue: { contentID, isLiked in
var updatedContents = contentsSubject.value

guard let index = updatedContents.firstIndex(where: { $0.content.id == contentID }) else { return }

let originalContent = updatedContents[index]
let originalUserContent = originalContent.content
let originalContentInfo = originalUserContent.contentInfo
let originalLike = originalContentInfo.like

let updatedLike = isLiked
? Like(status: true, count: originalLike.count + 1)
: Like(status: false, count: max(0, originalLike.count - 1))

let updatedContent = Content(
content: UserContent(
id: originalUserContent.id,
contentInfo: ContentInfo(
author: originalContentInfo.author,
createdDate: originalContentInfo.createdDate,
title: originalContentInfo.title,
imageURL: originalContentInfo.imageURL,
text: originalContentInfo.text,
status: originalContentInfo.status,
like: updatedLike,
opacity: originalContentInfo.opacity,
commentCount: originalContentInfo.commentCount
)
),
isDeleted: originalContent.isDeleted
)
})
.store(in: cancelBag)

let selectedContent = input.didSelectedItem
Expand Down
8 changes: 5 additions & 3 deletions Wable-iOS/Presentation/Login/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,11 @@ private extension LoginViewController {
.receive(on: DispatchQueue.main)
.withUnretained(self)
.sink { owner, sessionInfo in
WableLogger.log("새로운 유저인가요? : \(sessionInfo.isNewUser)", for: .debug)
// TODO: 서버 밀고 한번 더 확인 필요
sessionInfo.isNewUser && sessionInfo.user.nickname != "" ? owner.navigateToOnboarding() : owner.navigateToHome()
let condition = sessionInfo.isNewUser && sessionInfo.user.nickname != "" && sessionInfo.user.profileURL != nil

WableLogger.log("새로운 유저인가요? : \(sessionInfo.isNewUser && sessionInfo.user.nickname != "")", for: .debug)

condition ? owner.navigateToOnboarding() : owner.navigateToHome()
Comment on lines +177 to +181
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Added profile URL validation when determining navigation path.

The code now requires that a user's profile URL must not be nil in addition to checking if they're a new user with a non-empty nickname. This adds more robust validation before deciding whether to navigate to onboarding or home screens.

However, the ternary operator is not ideal for calling void functions.

Replace the ternary with a clearer if-else statement:

-condition ? owner.navigateToOnboarding() : owner.navigateToHome()
+if condition {
+    owner.navigateToOnboarding()
+} else {
+    owner.navigateToHome()
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let condition = sessionInfo.isNewUser && sessionInfo.user.nickname != "" && sessionInfo.user.profileURL != nil
WableLogger.log("새로운 유저인가요? : \(sessionInfo.isNewUser && sessionInfo.user.nickname != "")", for: .debug)
condition ? owner.navigateToOnboarding() : owner.navigateToHome()
let condition = sessionInfo.isNewUser && sessionInfo.user.nickname != "" && sessionInfo.user.profileURL != nil
WableLogger.log("새로운 유저인가요? : \(sessionInfo.isNewUser && sessionInfo.user.nickname != "")", for: .debug)
if condition {
owner.navigateToOnboarding()
} else {
owner.navigateToHome()
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 181-181: Using ternary to call Void functions should be avoided

(void_function_in_ternary)

}
.store(in: cancelBag)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ final class LCKYearViewController: NavigationViewController {

var isPullDownEnabled = false

private var selectedYearIndex: Int?
private var yearCount: Int {
return Calendar.current.component(.year, from: .now) - Constant.startYear + 1
}

// MARK: - UIComponent

private let rootView = LCKYearView()
Expand All @@ -27,6 +32,7 @@ final class LCKYearViewController: NavigationViewController {
setupConstraint()
setupAction()
setupDelegate()
setDefaultYear()
}
}

Expand Down Expand Up @@ -57,6 +63,22 @@ private extension LCKYearViewController {
rootView.yearCollectionView.delegate = self
}

func setDefaultYear() {
let defaultIndex = yearCount - 1
let defaultYear = Constant.startYear + defaultIndex

selectedYearIndex = defaultIndex
rootView.pullDownButton.configuration?.attributedTitle = "\(defaultYear)".pretendardString(with: .body1)

DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let indexPath = IndexPath(item: defaultIndex, section: 0)

self.rootView.yearCollectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
self.updateCellAppearance(indexPath: indexPath)
}
}

// MARK: - @objc Method

@objc func pullDownButtonDidTap() {
Expand All @@ -81,45 +103,55 @@ private extension LCKYearViewController {
}

@objc func nextButtonDidTap() {
guard let pullDownButtonLabel = rootView.pullDownButton.titleLabel?.text,
let lckYear = Int(pullDownButtonLabel)
else {
guard let selectedIndex = selectedYearIndex else {
setDefaultYear()
return
}

navigationController?.pushViewController(LCKTeamViewController(lckYear: lckYear), animated: true)
navigationController?.pushViewController(LCKTeamViewController(lckYear: Constant.startYear + selectedIndex), animated: true)
}
}

// MARK: - UICollectionViewDataSource
// MARK: - Helper Method

extension LCKYearViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
for visibleCell in collectionView.visibleCells {
private extension LCKYearViewController {
func updateCellAppearance(indexPath: IndexPath) {
for visibleCell in rootView.yearCollectionView.visibleCells {
if let yearCell = visibleCell as? LCKYearCollectionViewCell {
yearCell.backgroundColor = .clear
yearCell.yearLabel.textColor = .black
yearCell.yearLabel.textColor = .wableBlack
yearCell.yearLabel.attributedText = yearCell.yearLabel.text?.pretendardString(with: .body2)
}
}

guard let cell = collectionView.cellForItem(at: indexPath) as? LCKYearCollectionViewCell else {
return
if let cell = rootView.yearCollectionView.cellForItem(at: indexPath) as? LCKYearCollectionViewCell {
cell.backgroundColor = .purple10
cell.yearLabel.attributedText = cell.yearLabel.text?.pretendardString(with: .body1)
cell.yearLabel.textColor = .purple50
}
}
}


// MARK: - UICollectionViewDataSource

extension LCKYearViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedYearIndex = indexPath.item

cell.backgroundColor = .purple10
cell.yearLabel.attributedText = cell.yearLabel.text?.pretendardString(with: .body1)
cell.yearLabel.textColor = .purple50
updateCellAppearance(indexPath: indexPath)

rootView.pullDownButton.configuration?.attributedTitle = cell.yearLabel.text?
.pretendardString(with: .body1)
rootView.pullDownButton.configuration?.attributedTitle = String(Constant.startYear + indexPath.item).pretendardString(with: .body1)

collectionView.layoutIfNeeded()
}
}

// MARK: - UICollectionViewDataSource

extension LCKYearViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Calendar.current.component(.year, from: Date()) - 2012 + 1
return yearCount
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
Expand All @@ -130,8 +162,20 @@ extension LCKYearViewController: UICollectionViewDataSource {
return UICollectionViewCell()
}

cell.yearLabel.attributedText = String(2012 + indexPath.row).pretendardString(with: .body2)
if let selectedIndex = selectedYearIndex {
let condition = indexPath.item == selectedIndex

cell.backgroundColor = condition ? .purple10 : .clear
cell.yearLabel.attributedText = String(Constant.startYear + indexPath.item).pretendardString(with: condition ? .body1 : .body2)
cell.yearLabel.textColor = condition ? .purple50 : .wableBlack
}

return cell
}
}

private extension LCKYearViewController {
enum Constant {
static let startYear: Int = 2012
}
}
Loading