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] 모아보기 기능 구현 #460

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
23 changes: 23 additions & 0 deletions Domain/Entities/AnnotationCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// AnnotationCollection.swift
// Reazy
//
// Created by 문인범 on 2/1/25.
//

import Foundation


struct AnnotationCollection: Identifiable, Equatable {
let id: String

let annotation: AnnotationCase
let commenText: String?
let contents: AttributedString
let pageIndex: Int

enum AnnotationCase: Equatable {
case highlight
case comment
}
}
Comment on lines +11 to +23
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

모아보기 뷰에서 사용되는 Entity 입니다

1 change: 1 addition & 0 deletions Presentation/Helper/NotificationCenter + Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ extension Notification.Name {
static let isFigureCaptured = Notification.Name("isFigureCaptured")
static let isCollectionCaptured = Notification.Name("isCollectionCaptured")
static let changeHomePaperInfo = Notification.Name("changeHomePaperInfo")
static let didSelectAnnotationCollection = Notification.Name("didSelectAnnotationCollection")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

PDFView로 Action을 전달하기 위한 Notification 입니다

}
9 changes: 7 additions & 2 deletions Presentation/MainPDF/ViewModel/MainPDFViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ extension MainPDFViewModel {

// 각 페이지의 모든 주석을 반복하며 밑줄과 코멘트 아이콘 지우기
for annotation in page.annotations {
if annotation.value(forAnnotationKey: .contents) != nil {
// if annotation.value(forAnnotationKey: .contents) != nil {
// page.removeAnnotation(annotation)
// }
if let a = annotation.contents, a.split(separator: "|")[0] != "UH" {
page.removeAnnotation(annotation)
}
}
Expand Down Expand Up @@ -206,6 +209,7 @@ extension MainPDFViewModel {
guard let page = selections.first?.pages.first else { return }

let highlightColor = color.uiColor
let id = UUID()

selections.forEach { selection in

Expand Down Expand Up @@ -236,7 +240,8 @@ extension MainPDFViewModel {
let highlight = PDFAnnotation(bounds: bounds, forType: .highlight, withProperties: nil)
highlight.endLineStyle = .none
highlight.color = highlightColor

highlight.contents = "UH|\(selection.string ?? "nil")|\(color.rawValue)|\(id)"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

모아보기에 필요한 정보들(하이라이트 or 코멘트 인지, 선택된 영역의 String, 사용된 color, id값)을
PDFAnnotation 프로퍼티에 저장합니다.


page.addAnnotation(highlight)
pdfDrawer.annotationHistory.append((action: .add(highlight), annotation: highlight, page: page))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// AnnotationCollectionCell.swift
// Reazy
//
// Created by 문인범 on 1/31/25.
//

import SwiftUI


struct AnnotationCollectionCell: View {
let annotation: AnnotationCollection

var body: some View {
VStack(alignment: .leading, spacing: 0) {
if annotation.annotation == .comment {
HStack(spacing: 0) {
Rectangle()
.foregroundStyle(.point4)
.frame(width: 1, height: 35)
.padding(.trailing, 8)

Text(annotation.commenText ?? "알 수 없음")
.font(.custom(ReazyFontType.pretendardMediumFont, size: 12))
.foregroundStyle(.point4)
.lineSpacing(5)
.lineLimit(2)
}
.padding(.bottom, 12)
}

Text(annotation.contents)
.lineSpacing(5)
.lineLimit(9)
}
.onTapGesture {
NotificationCenter.default.post(name: .didSelectAnnotationCollection, object: nil, userInfo: ["index": annotation.pageIndex])
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

NotificationCenter를 통해 PDFView에게 액션을 전달합니다.
해당 페이지로 이동

}
}
}



#Preview {
var attributedString = AttributedString(stringLiteral: "테스트 컨텐츠입니다. 테스트 컨텐츠입니다.테스트 컨텐츠입니다.테스트 컨텐츠입니다.테스트 컨텐츠입니다.")

let attributes: AttributeContainer = .init([
.backgroundColor: UIColor.red,
.font: UIFont.init(name: ReazyFontType.pretendardRegularFont, size: 12)!,
])

attributedString.setAttributes(attributes)

let highlight = AnnotationCollection(
id: "",
annotation: .highlight,
commenText: nil,
contents: attributedString,
pageIndex: 0
)

let comment = AnnotationCollection(
id: "",
annotation: .comment,
commenText: "테스트 코멘트 입니다.테스트 코멘트 입니다.",
contents: "테스트 컨텐츠입니다. 테스트 컨텐츠입니다.테스트 컨텐츠입니다.테스트 컨텐츠입니다.테스트 컨텐츠입니다.",
pageIndex: 0
)

return Group {
AnnotationCollectionCell(annotation: highlight)
Divider()
AnnotationCollectionCell(annotation: comment)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// AnnotationCollectionView.swift
// Reazy
//
// Created by 문인범 on 1/31/25.
//

import SwiftUI


struct AnnotationCollectionView: View {
@StateObject private var viewModel: AnnotationCollectionViewModel = .init()

var body: some View {
ScrollView {
VStack {
ForEach(viewModel.annotations) { annotation in
AnnotationCollectionCell(annotation: annotation)

divider
.padding(.vertical, 16)
}
}
.padding(.top, 20)
.padding(.horizontal, 24)
}
.onAppear {
viewModel.fetchData()
}
}


private var divider: some View {
Rectangle()
.foregroundStyle(.gray500)
.frame(height: 1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// AnnotationCollectionViewModel.swift
// Reazy
//
// Created by 문인범 on 1/31/25.
//

import Foundation
import UIKit


final class AnnotationCollectionViewModel: ObservableObject {
@Published public var annotations: [AnnotationCollection] = []


public func fetchData() {
guard let document = PDFSharedData.shared.document else { return }
let pageCount = document.pageCount

var resultText = ""
var currentId = ""
var currentContents = ""

for i in 0 ..< pageCount {
guard let page = document.page(at: i) else { continue }

page.annotations.forEach { annotation in
switch annotation.markupType {
case .highlight:
if let contents = annotation.contents {
if contents.split(separator: "|")[0] == "UH" || contents.split(separator: "|")[0] == "UC" {
let id = contents.split(separator: "|").last!

if id != currentId {
if !resultText.isEmpty {
extractAnnotation(
contents: currentContents,
body: resultText.replacingOccurrences(of: "\n", with: " "),
index: i
)
resultText = ""
}

currentId = String(id)
currentContents = contents
}

let text = annotation.contents!.split(separator: "|")[1]
if text == " " { return }

resultText += text
}
}

default:
break
}
}

if !resultText.isEmpty {
extractAnnotation(
contents: currentContents,
body: resultText.replacingOccurrences(of: "\n", with: " "),
index: i
)
resultText = ""
}
}
Comment on lines +24 to +68
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

현재 PDFDocument의 모든 PDFPage를 순회하여 필요한 PDFAnnotation들의 정보를 갖고와 필터링 한 후 Entity로 변환하여 저장합니다.

}



private func extractAnnotation(contents: String, body: String, index: Int) {
let splitedContents = contents.split(separator: "|")

let id = splitedContents.last!

switch splitedContents[0] == "UH" {
case true:
guard let color = HighlightColors(rawValue: String(splitedContents[2])) else { break }
var attributedString = AttributedString(body)
let container = AttributeContainer([
.backgroundColor: color.uiColor,
.font: UIFont(name: "Pretendard-Regular", size: 12)!
])

attributedString.setAttributes(container)

let annotation = AnnotationCollection(
id: String(id),
annotation: .highlight,
commenText: nil,
contents: attributedString,
pageIndex: index
)

self.annotations.append(annotation)
case false:
let commentText = splitedContents[2]

var attributedString = AttributedString(commentText)
let container = AttributeContainer([
.font: UIFont(name: "Pretendard-Medium", size: 12)!,
.foregroundColor: UIColor.point2,
])

attributedString.setAttributes(container)

let annotation = AnnotationCollection(
id: String(id),
annotation: .comment,
commenText: body,
contents: attributedString,
pageIndex: index
)

self.annotations.append(annotation)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,8 @@ extension CommentViewModel {
}

func drawUnderline(newComment: Comment) {
var textInputed = false

for index in newComment.pages {
guard let page = document?.page(at: index) else { continue }

Expand Down Expand Up @@ -514,7 +516,13 @@ extension CommentViewModel {

let underline = lineAnnotation(bounds: bounds, forType: .line, withProperties: nil)

underline.setValue(newComment.id.uuidString, forAnnotationKey: .contents)
if textInputed {
underline.setValue("UC| |" + newComment.id.uuidString, forAnnotationKey: .contents)
} else {
let text = "UC|\(newComment.selectedText)|\(newComment.text)|\(newComment.id.uuidString)"
underline.contents = text
textInputed = true
}
Comment on lines +519 to +525
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Comment에 사용되는 Underline annotation에 정보를 저장합니다.

page.addAnnotation(underline)
}
}
Expand Down
12 changes: 9 additions & 3 deletions Presentation/OriginalPaper/Menus/MenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ struct MenuView: View {
HStack(spacing: 0) {
TabButton(title: "목차", selectedTab: $selectedTab)
TabButton(title: "페이지", selectedTab: $selectedTab)
TabButton(title: "주석", selectedTab: $selectedTab)
}

if selectedTab == "목차" {
switch selectedTab {
case "목차":
IndexView()
} else {
case "페이지":
PageListView()
case "주석":
AnnotationCollectionView()
default:
EmptyView()
}
}
}
Expand All @@ -41,7 +47,7 @@ struct TabButton: View {
Text(title)
.reazyFont(selectedTab == title ? .body3 : .text5)
.foregroundStyle(selectedTab == title ? .primary1 : .gray600)
.frame(width: 126, height: 36)
.frame(width: 84, height: 36)

Rectangle()
.frame(height: 2)
Expand Down
13 changes: 12 additions & 1 deletion Presentation/OriginalPaper/OriginalViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,15 @@ extension OriginalViewController {
}
.store(in: &self.cancellable)
}

NotificationCenter.default.publisher(for: .didSelectAnnotationCollection)
.sink { [weak self] noti in
guard let index = noti.userInfo?["index"] as? Int,
let page = self?.mainPDFView.document?.page(at: index) else { return }

self?.mainPDFView.go(to: page)
}
.store(in: &self.cancellable)
}
}

Expand Down Expand Up @@ -452,7 +461,9 @@ extension OriginalViewController: UIGestureRecognizerDelegate {
commentViewModel.isMenuTapped = false

if viewModel.isCommentTapped, let buttonID = tappedAnnotation.contents {
viewModel.selectedComments = commentViewModel.comments.filter { $0.buttonId.uuidString == buttonID }
let a = buttonID.split(separator: "|")

viewModel.selectedComments = commentViewModel.comments.filter { $0.buttonId.uuidString == (a.count > 1 ? a.last! : a[0]) }
commentViewModel.setCommentPosition(selectedComments: viewModel.selectedComments, pdfView: mainPDFView)
}
viewModel.setHighlight(selectedComments: viewModel.selectedComments, isTapped: viewModel.isCommentTapped)
Expand Down
Loading