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

선호 학과 정보 저장 #323

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion SNUTT-2022/SNUTT/AppState/AppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ extension AppEnvironment {
let timetableService = TimetableService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories)
let userService = UserService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories)
let lectureService = LectureService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories)
let searchService = SearchService(appState: appState, webRepositories: webRepositories)
let searchService = SearchService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories)
let globalUIService = GlobalUIService(appState: appState, localRepositories: localRepositories, webRepositories: webRepositories)
let courseBookService = CourseBookService(appState: appState, webRepositories: webRepositories)
let authService = AuthService(appState: appState, webRepositories: webRepositories, localRepositories: localRepositories)
Expand Down
1 change: 1 addition & 0 deletions SNUTT-2022/SNUTT/AppState/States/SearchState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class SearchState {
@Published var isFilterOpen = false
@Published var searchTagList: SearchTagList?
@Published var selectedTagList: [SearchTag] = []
@Published var pinnedTagList: [SearchTag] = []
@Published var selectedTimeRange: [SearchTimeMaskDto] = []

@Published var displayMode: SearchDisplayMode = .search
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "exit@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "exit@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum STDefaultsKey: String {
case userDto
case fcmToken
case preferredColorScheme
case recentDepartmentTags

case currentTimetable
case timetableConfig
Expand Down
21 changes: 21 additions & 0 deletions SNUTT-2022/SNUTT/Services/SearchService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@ protocol SearchServiceProtocol: Sendable {
struct SearchService: SearchServiceProtocol {
var appState: AppState
var webRepositories: AppEnvironment.WebRepositories
var localRepositories: AppEnvironment.LocalRepositories

var searchRepository: SearchRepositoryProtocol {
webRepositories.searchRepository
}

var userDefaultsRepository: UserDefaultsRepositoryProtocol {
localRepositories.userDefaultsRepository
}

var searchState: SearchState {
appState.search
}
Expand Down Expand Up @@ -61,11 +66,27 @@ struct SearchService: SearchServiceProtocol {
let dto = try await searchRepository.fetchTags(quarter: quarter)
let model = SearchTagList(from: dto)
appState.search.searchTagList = model
guard let recentTagNames = userDefaultsRepository.get([String].self, key: .recentDepartmentTags) else { return }
appState.search.pinnedTagList = model?.tagList.filter { $0.type == .department && recentTagNames.contains($0.text) } ?? []
}

private func _saveDepartmentTagsToUserDefaults(from tagList: [SearchTag]) async throws {
let departmentTags = tagList.filter { tag in tag.type == .department &&
!appState.search.pinnedTagList.contains { $0.text == tag.text }
}
var updatedTags = appState.search.pinnedTagList + departmentTags
Copy link
Member

Choose a reason for hiding this comment

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

스펙상 가장 최근에 선택한 학과가 최상단에 노출되어야하는데 지금은 맨 뒤에 붙는 듯..!

if updatedTags.count > 5 {
updatedTags = Array(updatedTags.suffix(5))
}
appState.search.pinnedTagList = updatedTags
let savedTags = updatedTags.map { $0.text }
userDefaultsRepository.set([String].self, key: .recentDepartmentTags, value: savedTags)
}

private func _fetchSearchResult() async throws {
guard let currentTimetable = timetableState.current else { return }
let tagList = searchState.selectedTagList
try await _saveDepartmentTagsToUserDefaults(from: tagList)
let timeList = tagList.contains(where: { $0.type == .time && TimeType(rawValue: $0.text) == .range }) ? searchState.selectedTimeRange : nil
let excludedTimeList = tagList.contains(where: { $0.type == .time && TimeType(rawValue: $0.text) == .empty }) ? currentTimetable.timeMask : nil
let offset = searchState.perPage * searchState.pageNum
Expand Down
7 changes: 7 additions & 0 deletions SNUTT-2022/SNUTT/ViewModels/FilterSheetViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI
class FilterSheetViewModel: BaseViewModel, ObservableObject {
@Published var selectedTagList: [SearchTag] = []
@Published var searchTagList: SearchTagList?
@Published var pinnedTagList: [SearchTag] = []
@Published private var _selectedTimeRange: [SearchTimeMaskDto] = []
@Published private var _isFilterOpen: Bool = false

Expand All @@ -36,6 +37,7 @@ class FilterSheetViewModel: BaseViewModel, ObservableObject {
appState.search.$selectedTagList.assign(to: &$selectedTagList)
appState.search.$selectedTimeRange.assign(to: &$_selectedTimeRange)
appState.search.$searchTagList.assign(to: &$searchTagList)
appState.search.$pinnedTagList.assign(to: &$pinnedTagList)
appState.search.$isFilterOpen.assign(to: &$_isFilterOpen)
}

Expand Down Expand Up @@ -63,4 +65,9 @@ class FilterSheetViewModel: BaseViewModel, ObservableObject {
func isSelected(tag: SearchTag) -> Bool {
return appState.search.selectedTagList.contains(where: { $0.id == tag.id })
}

func removePin(tag: SearchTag) {
guard let index = appState.search.pinnedTagList.firstIndex(where: { $0.id == tag.id }) else { return }
appState.search.pinnedTagList.remove(at: index)
}
}
102 changes: 59 additions & 43 deletions SNUTT-2022/SNUTT/Views/Components/FilterSheetContent.swift
Copy link
Member

Choose a reason for hiding this comment

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

변경사항이 없어서 line에 직접 못적는데 좌측 태그 카테고리쪽 UI 변경사항도 같이 반영해주세용

Copy link
Member

Choose a reason for hiding this comment

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

그리고 필터 내부 스크롤이 안되는데(실기기&시뮬레이터 둘다 iOS 18.0+) 혹시 재현돼?
시뮬레이터 iOS 17.2에서는 문제 없는거같긴해

Copy link
Contributor

Choose a reason for hiding this comment

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

xcode 16 + ios 18 에서 highPriorityGesture 문제 있는듯..
snutt 2024에서는 고쳐놨는데 ㅋㅋ..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ㅇㅋ 색상이랑 폰트 크기 이런거 말하는거 맞지?

Copy link
Member

Choose a reason for hiding this comment

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

엉 그거랑 내부 스크롤 안되는 문제도..!

Copy link
Contributor

Choose a reason for hiding this comment

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

스크롤 문제는

func sheetGesture(_ translation: Binding<CGFloat>, dismiss: @escaping @MainActor () -> Void) -> some View {
if #available(iOS 18.0, *) {
gesture(SheetGestureRecognizer(translation: translation, dismiss: dismiss))
} else {
highPriorityGesture(DragGesture().onChanged({ value in
translation.wrappedValue = value.translation.width
}).onEnded({ value in
translation.wrappedValue = 0
if value.velocity.width < -300 || value.translation.width < -100 {
dismiss()
}
}))

여기 참고하면 될듯!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

참고해서 고쳤는데 이게 위로 스크롤 시에 sheet가 dismiss되는 문제가 있는데 .. 실기기에서도 똑같겠지 ..?ㅜㅜ

Copy link
Contributor

Choose a reason for hiding this comment

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

아하. 이거는 bottom sheet구나. 그러면 저 코드에서 value.velocity.width 랑 translation.width 를 height로 바꿔야할듯! x는 y로 바꾸고.

Original file line number Diff line number Diff line change
Expand Up @@ -43,53 +43,17 @@ struct FilterSheetContent: View {
.padding(.horizontal, 10)
.padding(.vertical, 5)

Divider()

ScrollView {
LazyVStack {
Group {
ForEach(viewModel.filterTags(with: selectedCategory)) { tag in
Button {
viewModel.toggle(tag)
} label: {
HStack(alignment: .top) {
Image("checkmark.circle.\(viewModel.isSelected(tag: tag) ? "tick" : "untick")")
.resizable()
.scaledToFit()
.frame(width: 16)
.padding(.trailing, 3)
VStack(alignment: .leading, spacing: 6) {
Text(tag.text)
.font(STFont.regular14.font)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())

if tag.text == TimeType.range.rawValue {
Group {
if !$viewModel.selectedTimeRange.isEmpty {
Text(viewModel.selectedTimeRange.map {
$0.preciseTimeString
}.joined(separator: "\n"))
.underline()
.lineSpacing(4)
} else {
Text("눌러서 선택하기")
.underline()
}
}
.font(STFont.regular12.font)
.foregroundColor(STColor.darkGray)
.onTapGesture { isTimeRangeSheetOpen = true }
}
}
}
.padding(.horizontal, 20)
.padding(.vertical, 7)
}
.buttonStyle(.plain)
if selectedCategory == .department {
ForEach(viewModel.pinnedTagList) {
tag in FilterTagButton(tag: tag, isPinned: true, viewModel: viewModel, isTimeRangeSheetOpen: $isTimeRangeSheetOpen)
}
Divider()
}
peng-u-0807 marked this conversation as resolved.
Show resolved Hide resolved
ForEach(viewModel.filterTags(with: selectedCategory)) { tag in
FilterTagButton(tag: tag, isPinned: false, viewModel: viewModel, isTimeRangeSheetOpen: $isTimeRangeSheetOpen)
}
.frame(maxWidth: .infinity)
}
}
.id(selectedCategory) // rerender on change of category
Expand Down Expand Up @@ -133,6 +97,58 @@ struct FilterSheetContent: View {
}
}

struct FilterTagButton: View {
let tag: SearchTag
let isPinned: Bool
@ObservedObject var viewModel: FilterSheetViewModel
@Binding var isTimeRangeSheetOpen: Bool

var body: some View {
Button {
viewModel.toggle(tag)
} label: {
HStack(alignment: .top) {
Image("checkmark.circle.\(viewModel.isSelected(tag: tag) ? "tick" : "untick")")
.resizable()
.scaledToFit()
.frame(width: 16)
.padding(.trailing, 3)
VStack(alignment: .leading, spacing: 6) {
Text(tag.text)
.font(STFont.regular14.font)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())

if tag.text == TimeType.range.rawValue {
Group {
if !viewModel.selectedTimeRange.isEmpty {
Text(viewModel.selectedTimeRange.map {
$0.preciseTimeString
}.joined(separator: "\n"))
.underline()
.lineSpacing(4)
} else {
Text("눌러서 선택하기")
.underline()
}
}
.font(STFont.regular12.font)
.foregroundColor(STColor.darkGray)
.onTapGesture { isTimeRangeSheetOpen = true }
}
}
if isPinned {
Image("department.xmark")
.onTapGesture { viewModel.removePin(tag: tag) }
}
}
.padding(.horizontal, 20)
.padding(.vertical, 7)
}
.buttonStyle(.plain)
}
}

#if DEBUG
struct FilterSheetContent_Previews: PreviewProvider {
static var previews: some View {
Expand Down
Loading