Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
b6d8c5b
refactor: 운영시간 DTO를 파일 단위로 분리
hgjwilly Oct 11, 2026
145f005
refactor: Dining의 Domain 모델과 Data DTO 분리
hgjwilly Oct 11, 2026
6f1dcac
refactor: DateProvider 내부 미사용 메서드 삭제
hgjwilly Oct 11, 2026
48eb8ea
Merge pull request #622 from BCSDLab/refactor/dining-domain-data
hgjwilly Oct 11, 2026
33e41bc
chore: 식단 리팩토링에 영향받는 테스트 비활성화
hgjwilly Oct 15, 2026
3cff48c
refactor: 식단 좋아요 미사용 코드 삭제
hgjwilly Oct 11, 2026
5b8200b
refactor: 식단 Domain 모델의 품절·변경 상태와 날짜 타입 변경
hgjwilly Oct 11, 2026
9b2ea01
refactor: 식단 이미지 판정 모델 추가
hgjwilly Oct 11, 2026
3c3e52e
chore: SwiftUIIntrospect 패키지 추가
hgjwilly Oct 15, 2026
3922c5c
feat: 식단 신고 아이콘 이미지 추가
hgjwilly Oct 15, 2026
576d764
refactor: Date 요일·주말 헬퍼를 프로퍼티로 변경
hgjwilly Oct 15, 2026
561fa0a
refactor: 식단 DTO 미사용 필드 삭제
hgjwilly Oct 15, 2026
c1e58a6
refactor: 식단 Domain 모델을 새 화면 구조에 맞게 변경
hgjwilly Oct 15, 2026
349bbf0
feat: 식단 조회 UseCase에 날짜별 전체 시간대 조회 추가
hgjwilly Oct 15, 2026
ad9b40b
refactor: Home 식단 뷰 이름을 HomeDiningView로 변경
hgjwilly Oct 15, 2026
c5ea4d3
refactor: 식단 Domain 모델 Equatable 채택
hgjwilly Oct 18, 2026
2b6703a
feat: 식단 공유 딥링크용 DiningType 문자열 초기화 추가
hgjwilly Oct 18, 2026
77569c5
design: 새 식단 화면 날짜 선택 뷰 구현
hgjwilly Oct 18, 2026
7213b6f
design: 새 식단 화면 시간대 선택 뷰 구현
hgjwilly Oct 18, 2026
9d597a6
design: 새 식단 화면 메뉴 카드·리스트 뷰 구현
hgjwilly Oct 18, 2026
686f657
feat: 새 식단 화면 ViewModel 구현
hgjwilly Oct 18, 2026
4bc7785
design: 새 식단 화면 및 HostingController 구현
hgjwilly Oct 18, 2026
6c2f0e2
design: SwiftUI CancelableImage 컴포넌트 구현
hgjwilly Oct 18, 2026
330a1dd
design: 식단 공유 툴팁 구현
hgjwilly Oct 18, 2026
f16ff6d
feat: 식단 알림 설정 조회 UseCase 추가
hgjwilly Oct 18, 2026
e936151
design: 식단 알림 설정 바텀시트 뷰 구현
hgjwilly Oct 18, 2026
bbb23d0
feat: 식단 화면에 알림 설정 바텀시트 연결
hgjwilly Oct 18, 2026
e1c6f9c
feat: 식단 진입점을 새 식단 화면으로 교체
hgjwilly Oct 18, 2026
e3fa7e6
delete: AB 테스트 미사용 실험군 제거
hgjwilly Oct 18, 2026
a192fb9
feat: 식단 품절 제보 AB 테스트 실험군 추가
hgjwilly Oct 18, 2026
8cf0618
design: 식단 제보 AB 테스트 모델 및 툴팁 에셋 추가
hgjwilly Oct 18, 2026
fd9588d
feat: 식단 공유 딥링크를 새 식단 화면으로 연결
hgjwilly Oct 18, 2026
11ca05a
feat: 식단 품절 제보 API 및 UseCase 추가
hgjwilly Oct 18, 2026
3faeb0c
delete: 기존 UIKit 식단 화면 삭제
hgjwilly Oct 18, 2026
15f468b
feat: 현재 운영 중인 식단 조회 UseCase 추가
hgjwilly Oct 18, 2026
92b599c
chore: 식단 리팩토링에 영향 받는 테스트 비활성화
hgjwilly Oct 18, 2026
133ce74
chore: 공통 오류 메시지 문구 정리
hgjwilly Oct 18, 2026
ac6c3e8
chore: 새 식단 화면의 New 접두사 제거
hgjwilly Oct 18, 2026
941a870
design: 식단 품절 제보 모달 뷰 구현
hgjwilly Oct 18, 2026
9fd226f
delete: 미사용 폰트 파일 삭제
hgjwilly Oct 18, 2026
4607b7a
design: 식단 카드 제보 버튼·툴팁 UI 구현
hgjwilly Oct 18, 2026
8d71b0f
style: ErrorResponse 정적 에러 정의 정리
hgjwilly Oct 18, 2026
86b52c4
feat: 식단 제보 AB 테스트 적용 및 제보 모달 연결
hgjwilly Oct 18, 2026
48665da
Merge pull request #625 from BCSDLab/refactor/dining-swiftui
hgjwilly Oct 18, 2026
7dad976
Merge pull request #626 from BCSDLab/feat/dining-soldout-report
hgjwilly Oct 18, 2026
bbe1037
refactor: 문자열 로깅 제거
hgjwilly Oct 18, 2026
3c43cca
test: 컴파일 불가 테스트 대역 비활성화
hgjwilly Oct 18, 2026
ba98368
delete: 전체 주석 처리된 미사용 Shop 파일 삭제
hgjwilly Oct 19, 2026
d2d659f
refactor: Business 미사용 로깅 라벨 제거
hgjwilly Oct 20, 2026
e1b98c3
refactor: Campus 로깅 라벨 도메인 분리
hgjwilly Oct 18, 2026
bdf972f
refactor: User 미사용 로깅 라벨 제거
hgjwilly Oct 20, 2026
3f542b5
refactor: Recruit 로깅 라벨 case 이름 변경
hgjwilly Oct 20, 2026
05341ab
chore: 미사용 case 삭제
hgjwilly Oct 20, 2026
3ae731a
Merge pull request #628 from BCSDLab/refactor/event-label
hgjwilly Oct 20, 2026
2272bc0
fix: card 제보버튼 항상 보이도록 변경
hgjwilly Oct 21, 2026
525d4f7
feat: 품절 증거사진 촬영 기능 구현
hgjwilly Oct 21, 2026
ff3fb13
fix: 품절제보 모달 UI 수정
hgjwilly Oct 21, 2026
c69d566
design: 식단 EmptyView 구현
hgjwilly Oct 21, 2026
59651af
Merge pull request #630 from BCSDLab/fix/dining
hgjwilly Oct 21, 2026
a6d1c19
feat: 공지 탭별 NoticePageViewModel 추가
hgjwilly Oct 21, 2026
78a5fe5
feat: 공지 화면 NoticeViewModel 추가
hgjwilly Oct 21, 2026
5eeedbb
feat: 공지 탭바·테이블 UIViewRepresentable 추가 및 DTO Equatable 적용
hgjwilly Oct 21, 2026
758d97d
feat: 공지 목록을 SwiftUI TabView 페이징으로 교체
hgjwilly Oct 21, 2026
2ae2aec
feat: 공지 화면 최초 진입 시 전체 탭 글 목록 미리 불러오기
hgjwilly Oct 21, 2026
cfeef7b
refactor: NoticeListHostingController 팩토리 제거 및 호출부에서 init(rootView:) 주입
hgjwilly Oct 24, 2026
40bde22
refactor: TabBarCollectionView를 NoticeTabBarCollectionView로 이름 변경
hgjwilly Oct 24, 2026
a9ddce4
refactor: 공지 목록 SubViews 디렉토리 구조 정리
hgjwilly Oct 24, 2026
6ab0540
refactor: 공지 키워드 버튼 메서드·이니셜라이저 컨벤션 정리
hgjwilly Oct 24, 2026
ed65112
refactor: NoticeViewModel 로깅을 logEvent Input으로 통일하고 키워드 조회 async 전환
hgjwilly Oct 24, 2026
c9f4997
refactor: NoticeListTableView 이벤트를 단일 Event Publisher로 통합
hgjwilly Oct 24, 2026
17c04ab
feat: 공지 검색 결과 추가 로드 시 insertRows 애니메이션 적용
hgjwilly Oct 24, 2026
770813f
chore: plus 아이콘 png를 svg로 교체
hgjwilly Oct 24, 2026
2e61d9b
feat: 키워드 관리 화면 스크롤 지원 및 키워드 추가 버튼 활성 조건 적용
hgjwilly Oct 24, 2026
503f49c
fix: 코드 리뷰 반영
hgjwilly Oct 24, 2026
64a79fa
Merge pull request #632 from BCSDLab/refactor/notice-swiftui-tabview
hgjwilly Oct 24, 2026
b45e2b8
chore: 미사용 EventLabel case 삭제
hgjwilly Oct 20, 2026
1f36115
chore: Dining 로깅 team DA1 적용
hgjwilly Oct 21, 2026
328050b
chore: 앱 최소 지원 버전 iOS 18로 변경
hgjwilly Oct 21, 2026
25bfdf3
log: 식단 식사시간 클릭 로깅 추가
hgjwilly Oct 21, 2026
949633e
log: 식단 식사시간 스크롤 로깅 추가
hgjwilly Oct 21, 2026
c933973
log: 식단 학생식당정보 클릭 로깅 추가
hgjwilly Oct 21, 2026
ad1a98a
log: 식단 공유하기 클릭 로깅 추가
hgjwilly Oct 21, 2026
1e508d6
log: 식단 코너 사진 클릭 로깅 추가
hgjwilly Oct 21, 2026
8e1952d
log: 설정 알림설정 클릭 로깅 추가
hgjwilly Oct 21, 2026
1c332b0
log: 식단 품절 제보 AB 테스트 노출 로깅 추가
hgjwilly Oct 21, 2026
a59fd50
log: 식단 품절 제보 버튼 클릭 로깅 추가
hgjwilly Oct 21, 2026
4f3bd57
log: 식단 품절 제보 코너 선택 로깅 추가
hgjwilly Oct 21, 2026
0b5a8f3
log: 식단 품절 제보 사진 촬영 로깅 추가
hgjwilly Oct 21, 2026
b137b30
log: 식단 품절 제보 제출 로깅 추가
hgjwilly Oct 21, 2026
ef2051b
log: 식단 품절 제보 이탈 로깅 추가
hgjwilly Oct 21, 2026
c9af085
fix: 코드 리뷰 반영
hgjwilly Oct 24, 2026
38b3ed7
fix: 코드 리뷰 반영
hgjwilly Oct 24, 2026
43d002b
Merge pull request #634 from BCSDLab/log/dining
hgjwilly Oct 24, 2026
5fcfef5
feat: 품절 제보 모달 코너 기본 선택 및 카드 제보 코너 전달
hgjwilly Oct 24, 2026
aa4ad3d
Merge pull request #636 from BCSDLab/feat/dining-report-modal-default…
hgjwilly Oct 24, 2026
47735e4
fix: 식단 안내 최초 노출 처리 및 DEBUG 강제 노출 제거
hgjwilly Oct 25, 2026
1c269a7
fix: 식단 로딩 표시 및 Task 취소 처리 개선
hgjwilly Oct 25, 2026
8c7819d
fix: 코드 리뷰 반영
hgjwilly Oct 25, 2026
5cb22d5
Merge pull request #638 from BCSDLab/fix/dining
hgjwilly Oct 25, 2026
b17875e
update 5.2.2
hgjwilly Oct 25, 2026
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
124 changes: 82 additions & 42 deletions Koin/Apps/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ extension SceneDelegate {
if let date = parameters["date"], let type = parameters["type"], let place = parameters["place"] {
handleDiningNavigation(date: date, type: type, place: place, navigationController: navigationController)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
logAnalyticsEventUseCase.execute(label: EventParameter.EventLabel.Campus.menuShare, category: .click, value: "코인으로 이동")
logAnalyticsEventUseCase.execute(label: EventParameter.EventLabel.Campus.Dining.menuShare, category: .click, value: "코인으로 이동")
return
}
}
Expand Down Expand Up @@ -183,7 +183,7 @@ extension SceneDelegate {
private func makeHomeTabBarController() -> HomeTabBarController {
let homeViewController = makeHomeHostingController()
let categoryViewController = makeCategoryHostingController()
let noticeViewController = makeNoticeListViewController()
let noticeViewController = makeNoticeListHostingController()
let profileViewController = makeProfileHostingController()

let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
Expand Down Expand Up @@ -255,21 +255,20 @@ extension SceneDelegate {
logAnalyticsEventUseCase: logAnalyticsEventUseCase))
return CategoryHostingController(rootView: categoryRootView)
}

private func makeNoticeListViewController() -> UIViewController {
let service = DefaultNoticeService()
let repository = DefaultNoticeListRepository(service: service)
let fetchArticleListUseCase = DefaultFetchNoticeArticlesUseCase(noticeListRepository: repository)
let fetchMyKeywordUseCase = DefaultFetchNotificationKeywordUseCase(noticeListRepository: repository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(
repository: GA4AnalyticsRepository(service: GA4AnalyticsService())
)
let viewModel = NoticeListViewModel(
fetchNoticeArticlesUseCase: fetchArticleListUseCase,

private func makeNoticeListHostingController() -> UIViewController {
let noticeService = DefaultNoticeService()
let noticeListRepository = DefaultNoticeListRepository(service: noticeService)
let fetchNoticeArticlesUseCase = DefaultFetchNoticeArticlesUseCase(noticeListRepository: noticeListRepository)
let fetchMyKeywordUseCase = DefaultFetchNotificationKeywordUseCase(noticeListRepository: noticeListRepository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
let viewModel = NoticeViewModel(
fetchNoticeArticlesUseCase: fetchNoticeArticlesUseCase,
fetchMyKeywordUseCase: fetchMyKeywordUseCase,
logAnalyticsEventUseCase: logAnalyticsEventUseCase
)
return NoticeListViewController(viewModel: viewModel)
let noticeListView = NoticeListView(viewModel: viewModel)
return NoticeListHostingController(rootView: noticeListView)
}

private func makeProfileHostingController() -> UIViewController {
Expand Down Expand Up @@ -320,32 +319,55 @@ extension SceneDelegate {
isPresentingErrorViewController = true
}

private func handleDiningNavigation(date: String, type: String, place: String, navigationController: UINavigationController?) {
private func handleDiningNavigation(
date: String,
type: String,
place: String,
navigationController: UINavigationController?
) {
let date = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyMMdd"
dateFormatter.locale = Locale(identifier: "ko_KR")
return dateFormatter.date(from: date)
}()
let type = DiningType(rawValue: type)

let diningService = DefaultDiningService()
let shareService = KakaoShareService()
let diningRepository = DefaultDiningRepository(diningService: diningService, shareService: shareService)
let notiRepository = DefaultNotiRepository(service: DefaultNotiService())
let fetchDiningListUseCase = DefaultFetchDiningListUseCase(diningRepository: diningRepository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
let diningRepository = DefaultDiningRepository(
diningService: diningService,
shareService: shareService
)
let coreRepository = DefaultCoreRepository(service: DefaultCoreService())
let abTestRepository = DefaultAbTestRepository(service: DefaultAbTestService())
let notiReposotiry = DefaultNotiRepository(service: DefaultNotiService())
let dateProvider = DefaultDateProvider()
let fetchDiningListUseCase = DefaultFetchDiningListUseCase(diningRepository: diningRepository)
let shareMenuListUseCase = DefaultShareMenuListUseCase(diningRepository: diningRepository)
let changeNotiUseCase = DefaultChangeNotiUseCase(notiRepository: notiRepository)
let changeNotiDetailUseCase = DefaultChangeNotiDetailUseCase(notiRepository: notiRepository)
let fetchNotiListUseCase = DefaultFetchNotiListUseCase(notiRepository: notiRepository)
let fetchDiningNotiUseCase = DefaultFetchDiningNotiUseCase(notiRepository: notiReposotiry)
let changeNotiUseCase = DefaultChangeNotiUseCase(notiRepository: notiReposotiry)
let fetchCurrentDiningItemsUseCase = DefaultFetchCurrentDiningItemsUseCase(repository: diningRepository)
let reportSoldoutDiningUseCase = DefaultReportSoldoutDiningUseCase(repository: diningRepository)
let uploadFileUseCase = DefaultUploadFileUseCase(coreRepository: coreRepository)
let assignAbTestUseCase = DefaultAssignAbTestUseCase(abTestRepository: abTestRepository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
let viewModel = DiningViewModel(
dateProvider: dateProvider,
fetchDiningListUseCase: fetchDiningListUseCase,
logAnalyticsEventUseCase: logAnalyticsEventUseCase,
dateProvder: dateProvider,
shareMenuListUseCase: shareMenuListUseCase,
fetchDiningNotiUseCase: fetchDiningNotiUseCase,
changeNotiUseCase: changeNotiUseCase,
fetchNotiListUsecase: fetchNotiListUseCase,
changeNotiDetailUseCase: changeNotiDetailUseCase,
sharedDiningItem: CurrentDiningTime(date: date.toDateFromYYMMDD() ?? Date(), diningType: DiningType(rawValue: "\(type)") ?? .breakfast)
fetchCurrentDiningItemsUseCase: fetchCurrentDiningItemsUseCase,
reportSoldoutDiningUseCase: reportSoldoutDiningUseCase,
uploadFileUseCase: uploadFileUseCase,
assignAbTestUseCase: assignAbTestUseCase,
logAnalyticsEventUseCase: logAnalyticsEventUseCase
)
let diningViewController = DiningViewController(viewModel: viewModel)
diningViewController.title = "식단"
let rootView = DiningView(viewModel: viewModel)
let diningHostingController = DiningHostingController(rootView: rootView)

navigationController?.pushViewController(diningViewController, animated: true)
navigationController?.pushViewController(diningHostingController, animated: true)
}

private func makeRecruitNotificationListViewController(notificationId: Int) -> RecruitNotificationListViewController {
Expand Down Expand Up @@ -434,22 +456,40 @@ extension SceneDelegate {
return NoticeDataViewController(viewModel: viewModel)
}

private func makeDiningViewController() -> DiningViewController {
private func makeDiningViewController() -> UIViewController {
let diningService = DefaultDiningService()
let shareService = KakaoShareService()
let diningRepository = DefaultDiningRepository(diningService: diningService, shareService: shareService)
let notiRepository = DefaultNotiRepository(service: DefaultNotiService())
let fetchDiningListUseCase = DefaultFetchDiningListUseCase(diningRepository: diningRepository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
let diningRepository = DefaultDiningRepository(
diningService: diningService,
shareService: shareService
)
let coreRepository = DefaultCoreRepository(service: DefaultCoreService())
let abTestRepository = DefaultAbTestRepository(service: DefaultAbTestService())
let notiReposotiry = DefaultNotiRepository(service: DefaultNotiService())
let dateProvider = DefaultDateProvider()
let fetchDiningListUseCase = DefaultFetchDiningListUseCase(diningRepository: diningRepository)
let shareMenuListUseCase = DefaultShareMenuListUseCase(diningRepository: diningRepository)
let changeNotiUseCase = DefaultChangeNotiUseCase(notiRepository: notiRepository)
let changeNotiDetailUseCase = DefaultChangeNotiDetailUseCase(notiRepository: notiRepository)
let fetchNotiListUseCase = DefaultFetchNotiListUseCase(notiRepository: notiRepository)
let viewModel = DiningViewModel(fetchDiningListUseCase: fetchDiningListUseCase, logAnalyticsEventUseCase: logAnalyticsEventUseCase, dateProvder: dateProvider, shareMenuListUseCase: shareMenuListUseCase, changeNotiUseCase: changeNotiUseCase, fetchNotiListUsecase: fetchNotiListUseCase, changeNotiDetailUseCase: changeNotiDetailUseCase)
let diningViewController = DiningViewController(viewModel: viewModel)
diningViewController.title = "식단"
return diningViewController
let fetchDiningNotiUseCase = DefaultFetchDiningNotiUseCase(notiRepository: notiReposotiry)
let changeNotiUseCase = DefaultChangeNotiUseCase(notiRepository: notiReposotiry)
let fetchCurrentDiningItemsUseCase = DefaultFetchCurrentDiningItemsUseCase(repository: diningRepository)
let reportSoldoutDiningUseCase = DefaultReportSoldoutDiningUseCase(repository: diningRepository)
let uploadFileUseCase = DefaultUploadFileUseCase(coreRepository: coreRepository)
let assignAbTestUseCase = DefaultAssignAbTestUseCase(abTestRepository: abTestRepository)
let logAnalyticsEventUseCase = DefaultLogAnalyticsEventUseCase(repository: GA4AnalyticsRepository(service: GA4AnalyticsService()))
let viewModel = DiningViewModel(
dateProvider: dateProvider,
fetchDiningListUseCase: fetchDiningListUseCase,
shareMenuListUseCase: shareMenuListUseCase,
fetchDiningNotiUseCase: fetchDiningNotiUseCase,
changeNotiUseCase: changeNotiUseCase,
fetchCurrentDiningItemsUseCase: fetchCurrentDiningItemsUseCase,
reportSoldoutDiningUseCase: reportSoldoutDiningUseCase,
uploadFileUseCase: uploadFileUseCase,
assignAbTestUseCase: assignAbTestUseCase,
logAnalyticsEventUseCase: logAnalyticsEventUseCase
)
let rootView = DiningView(viewModel: viewModel)
return DiningHostingController(rootView: rootView)
}

private func makeShopSummaryViewController(shopId: Int) -> UIViewController {
Expand Down
5 changes: 5 additions & 0 deletions Koin/Core/Asset/ImageAsset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,15 @@ public enum ImageAsset: String {

// MARK: - Dining
case coopInfo
case diningDelete
case diningShare
case diningTooltip
case nonMenuImage
case nonMenuWeekendImage
case diningReport
case diningReportTooltipNav
case diningReportTooltipCard
case diningReportImageEvidence

// MARK: - Icon
case add
Expand Down
17 changes: 9 additions & 8 deletions Koin/Core/Extensions/Common/Date+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ extension Date {
return Calendar.current.component(.day, from: self)
}

func isWeekend() -> Bool {
var weekday: String {
self.formatted(
.dateTime
.locale(Locale(identifier: "ko_KR"))
.weekday(.abbreviated)
)
}

var isWeekend: Bool {
let calendar = Calendar.current
let components = calendar.dateComponents([.weekday], from: self)
if let weekday = components.weekday {
Expand Down Expand Up @@ -91,13 +99,6 @@ extension Date {
return dateFormatter.string(from: self)
}

func dayOfWeek() -> String {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "ko_KR")
dateFormatter.dateFormat = "EEE"
return dateFormatter.string(from: self)
}

func dayOfMonth() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "d"
Expand Down
Loading
Loading