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

Fix/#152 인스타그램 이미지 조회 문제 수정 #155

Merged
merged 17 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,14 @@ import SwiftSoup

extension SwiftSoupClient: DependencyKey {
public static let liveValue: Self = {
let provider = SwiftSoupProvider()

return Self(
parseOGTitleAndImage: { url, completion in
guard let html = try? String(contentsOf: url),
let document = try? SwiftSoup.parse(html) else {
await completion()
return (nil, nil)
}

let title = try? document.select("meta[property=og:title]").first()?.attr("content")
let imageURL = try? document.select("meta[property=og:image]").first()?.attr("content")

guard title != nil || imageURL != nil else {
var request = URLRequest(url: url)
request.setValue(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
forHTTPHeaderField: "User-Agent"
)

guard let data = try? await URLSession.shared.data(for: request).0,
let html = String(data: data, encoding: .utf8),
let document = try? SwiftSoup.parse(html) else {
return (nil, nil)
}

let title = try? document.select("meta[property=og:title]").first()?.attr("content")
let imageURL = try? document.select("meta[property=og:image]").first()?.attr("content")

await completion()

return (title, imageURL)
}

await completion()

return (title, imageURL)
parseOGTitle: { url in
try await provider.parseOGTitle(url)
},
parseOGImageURL: { url in
try await provider.parseOGImageURL(url)
}
)
}()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import DependenciesMacros

@DependencyClient
public struct SwiftSoupClient {
public var parseOGTitleAndImage: @Sendable (
_ url: URL,
_ completion: @Sendable () async -> Void
) async -> (String?, String?) = { _, _ in (nil , nil) }
public var parseOGTitle: @Sendable (
_ url: URL
) async throws -> String? = { _ in nil }

public var parseOGImageURL: @Sendable (
_ url: URL
) async throws -> String? = { _ in nil }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// SwiftSoupProvider.swift
// CoreKit
//
// Created by 김도형 on 11/17/24.
//

import SwiftUI
import SwiftSoup

final class SwiftSoupProvider {
func parseOGTitle(_ url: URL) async throws -> String? {
try await parseOGMeta(url: url, type: "og:title")
}

func parseOGImageURL(_ url: URL) async throws -> String? {
try await parseOGMeta(url: url, type: "og:image")
}

func parseOGMeta(url: URL, type: String) async throws -> String? {
let html = try String(contentsOf: url)
let document = try SwiftSoup.parse(html)

if let metaData = try document.select("meta[property=\(type)]").first()?.attr("content") {
return metaData
} else {
var request = URLRequest(url: url)
request.setValue(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
forHTTPHeaderField: "User-Agent"
)

let (data, _) = try await URLSession.shared.data(for: request)
guard let html = String(data: data, encoding: .utf8) else {
return nil
}
let document = try SwiftSoup.parse(html)
let metaData = try document.select("meta[property=\(type)]").first()?.attr("content")

return metaData
}
}
}
Comment on lines +11 to +43
Copy link
Contributor

Choose a reason for hiding this comment

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

provider 분리하니까 훨낫네요 뷰에 저 코드들 있던것으로 기억하는데 잘 뺀듯

Copy link
Contributor Author

Choose a reason for hiding this comment

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

provider 분리하니까 훨낫네요 뷰에 저 코드들 있던것으로 기억하는데 잘 뺀듯

감사합니다

16 changes: 8 additions & 8 deletions Projects/DSKit/Sources/Components/PokitLinkCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ public struct PokitLinkCard<Item: PokitLinkCardItem>: View {
private let link: Item
private let action: () -> Void
private let kebabAction: (() -> Void)?
private let fetchMetaData: (() -> Void)?

public init(
link: Item,
action: @escaping () -> Void,
kebabAction: (() -> Void)? = nil
kebabAction: (() -> Void)? = nil,
fetchMetaData: (() -> Void)? = nil
) {
self.link = link
self.action = action
self.kebabAction = kebabAction
self.fetchMetaData = fetchMetaData
}

public var body: some View {
Expand Down Expand Up @@ -110,18 +113,15 @@ public struct PokitLinkCard<Item: PokitLinkCardItem>: View {

@MainActor
private func thumbleNail(url: URL) -> some View {
var request = URLRequest(url: url)
request.setValue(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
forHTTPHeaderField: "User-Agent"
)

return LazyImage(request: .init(urlRequest: request)) { phase in
LazyImage(url: url) { phase in
Group {
if let image = phase.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
} else if phase.error != nil {
placeholder
.onAppear { fetchMetaData?() }
} else {
placeholder
}
Expand Down
2 changes: 2 additions & 0 deletions Projects/DSKit/Sources/Components/PokitLinkPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public struct PokitLinkPreview: View {
if let image = phase.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
} else {
PokitSpinner()
.foregroundStyle(.pokit(.icon(.brand)))
Expand All @@ -56,6 +57,7 @@ public struct PokitLinkPreview: View {
Spacer()
}
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.clipped()
.background {
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(.pokit(.bg(.base)))
Expand Down
2 changes: 1 addition & 1 deletion Projects/Domain/Sources/Base/BaseContentItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct BaseContentItem: Identifiable, Equatable, PokitLinkCardItem, Sorta
public let categoryName: String
public let categoryId: Int
public let title: String
public let thumbNail: String
public var thumbNail: String
public let data: String
public let domain: String
public let createdAt: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import Foundation

import ComposableArchitecture
import FeatureContentCard
import Domain
import CoreKit
import DSKit
Expand Down Expand Up @@ -51,16 +52,7 @@ public struct CategoryDetailFeature {
}
return identifiedArray
}
var contents: IdentifiedArrayOf<BaseContentItem>? {
guard let contentList = domain.contentList.data else {
return nil
}
var identifiedArray = IdentifiedArrayOf<BaseContentItem>()
contentList.forEach { content in
identifiedArray.append(content)
}
return identifiedArray
}
var contents: IdentifiedArrayOf<ContentCardFeature.State> = []
Comment on lines -54 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

�굿굿!

var kebobSelectedType: PokitDeleteBottomSheet.SheetType?
var selectedContentItem: BaseContentItem?
var shareSheetItem: BaseContentItem? = nil
Expand All @@ -73,6 +65,7 @@ public struct CategoryDetailFeature {
var hasNext: Bool {
domain.contentList.hasNext
}
var isLoading: Bool = true

public init(category: BaseCategoryItem) {
self.domain = .init(categpry: category)
Expand All @@ -86,6 +79,7 @@ public struct CategoryDetailFeature {
case async(AsyncAction)
case scope(ScopeAction)
case delegate(DelegateAction)
case contents(IdentifiedActionOf<ContentCardFeature>)

@CasePathable
public enum View: BindableAction, Equatable {
Expand Down Expand Up @@ -121,10 +115,11 @@ public struct CategoryDetailFeature {
case 클립보드_감지
}

public enum ScopeAction: Equatable {
public enum ScopeAction {
case categoryBottomSheet(PokitBottomSheet.Delegate)
case categoryDeleteBottomSheet(PokitDeleteBottomSheet.Delegate)
case filterBottomSheet(CategoryFilterSheet.Delegate)
case contents(IdentifiedActionOf<ContentCardFeature>)
}

public enum DelegateAction: Equatable {
Expand Down Expand Up @@ -163,13 +158,19 @@ public struct CategoryDetailFeature {
/// - Delegate
case .delegate(let delegateAction):
return handleDelegateAction(delegateAction, state: &state)

case .contents(let contentsAction):
return .send(.scope(.contents(contentsAction)))
}
}

/// - Reducer body
public var body: some ReducerOf<Self> {
BindingReducer(action: \.view)
Reduce(self.core)
.forEach(\.contents, action: \.contents) {
ContentCardFeature()
}
}
}
//MARK: - FeatureAction Effect
Expand All @@ -191,7 +192,7 @@ private extension CategoryDetailFeature {
case .카테고리_선택했을때(let item):
state.domain.category = item
return .run { send in
await send(.inner(.pagenation_초기화))
await send(.inner(.pagenation_초기화), animation: .pokitDissolve)
await send(.async(.카테고리_내_컨텐츠_목록_조회_API))
await send(.inner(.카테고리_선택_시트_활성화(false)))
}
Expand Down Expand Up @@ -248,10 +249,17 @@ private extension CategoryDetailFeature {

case .카테고리_내_컨텐츠_목록_조회_API_반영(let contentList):
state.domain.contentList = contentList

var identifiedArray = IdentifiedArrayOf<ContentCardFeature.State>()
contentList.data?.forEach { identifiedArray.append(.init(content: $0)) }
state.contents = identifiedArray

state.isLoading = false
return .none

case let .컨텐츠_삭제_API_반영(id):
state.domain.contentList.data?.removeAll { $0.id == id }
state.contents.removeAll { $0.content.id == id }
state.domain.category.contentCount -= 1
state.selectedContentItem = nil
state.isPokitDeleteSheetPresented = false
Expand All @@ -264,11 +272,15 @@ private extension CategoryDetailFeature {

state.domain.contentList = contentList
state.domain.contentList.data = list + newList
newList.forEach { state.contents.append(.init(content: $0)) }

return .none

case .pagenation_초기화:
state.domain.pageable.page = 0
state.domain.contentList.data = nil
state.isLoading = true
state.contents.removeAll()
return .none
}
}
Expand Down Expand Up @@ -459,6 +471,15 @@ private extension CategoryDetailFeature {
.send(.async(.카테고리_내_컨텐츠_목록_조회_API))
)
}

case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))):
return .send(.delegate(.contentItemTapped(content)))
case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))):
state.kebobSelectedType = .링크삭제
state.selectedContentItem = content
return .send(.inner(.카테고리_시트_활성화(true)))
case .contents:
return .none
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import SwiftUI

import ComposableArchitecture
import FeatureContentCard
import Domain
import DSKit
import Util
Expand Down Expand Up @@ -136,8 +137,8 @@ private extension CategoryDetailView {

var contentScrollView: some View {
Group {
if let contents = store.contents {
if contents.isEmpty {
if !store.isLoading {
if store.contents.isEmpty {
VStack {
PokitCaution(
image: .empty,
Expand All @@ -151,17 +152,17 @@ private extension CategoryDetailView {
} else {
ScrollView(showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(contents) { content in
let isFirst = content == contents.first
let isLast = content == contents.last
ForEachStore(
store.scope(state: \.contents, action: \.contents)
) { store in
let isFirst = store.state.id == self.store.contents.first?.id
let isLast = store.state.id == self.store.contents.last?.id

PokitLinkCard(
link: content,
action: { send(.컨텐츠_항목_눌렀을때(content)) },
kebabAction: { send(.카테고리_케밥_버튼_눌렀을때(.링크삭제, selectedItem: content)) }
ContentCardView(
store: store,
isFirst: isFirst,
isLast: isLast
)
.divider(isFirst: isFirst, isLast: isLast)
.pokitScrollTransition(.opacity)
}

if store.hasNext {
Expand Down
Loading
Loading