Skip to content

Commit

Permalink
Display folders and allow navigating them
Browse files Browse the repository at this point in the history
  • Loading branch information
Ceylo committed Dec 27, 2024
1 parent ce985a2 commit a7ed81f
Show file tree
Hide file tree
Showing 12 changed files with 630 additions and 531 deletions.
14 changes: 11 additions & 3 deletions FAKit/Sources/FAKit/FAUserGalleryLike.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,32 @@ import FAPages

/// The representation for a gallery-like page (gallery, scraps, favorites)
public struct FAUserGalleryLike: Sendable, Equatable {
public typealias FolderGroup = FAFolderGroup
public typealias Folder = FAFolder

public let displayAuthor: String
public let previews: [FASubmissionPreview]
public let nextPageUrl: URL?
public let folderGroups: [FolderGroup]

public init(
displayAuthor: String,
previews: [FASubmissionPreview],
nextPageUrl: URL?
nextPageUrl: URL?,
folderGroups: [FolderGroup]
) {
self.displayAuthor = displayAuthor
self.previews = previews
self.nextPageUrl = nextPageUrl
self.folderGroups = folderGroups
}

public func appending(_ gallery: Self) -> Self {
.init(
displayAuthor: displayAuthor,
previews: previews + gallery.previews,
nextPageUrl: gallery.nextPageUrl
nextPageUrl: gallery.nextPageUrl,
folderGroups: folderGroups
)
}
}
Expand All @@ -40,7 +47,8 @@ extension FAUserGalleryLike {
previews: page.previews
.compactMap { $0 }
.map { FASubmissionPreview($0) },
nextPageUrl: page.nextPageUrl
nextPageUrl: page.nextPageUrl,
folderGroups: page.folderGroups
)
}
}
Expand Down
67 changes: 49 additions & 18 deletions FAKit/Sources/FAPages/FAUserGalleryLikePage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,35 @@
import Foundation
@preconcurrency import SwiftSoup

public enum FAFolderItem: Sendable, Equatable {
case section(title: String)
case folder(title: String, url: URL)
public struct FAFolderGroup: Sendable, Hashable, Identifiable {
public let title: String?
public let folders: [FAFolder]
public let id = UUID()

public init(title: String?, folders: [FAFolder]) {
self.title = title
self.folders = folders
}
}

public struct FAFolder: Sendable, Hashable, Identifiable {
public let title: String
public let url: URL
public let isActive: Bool
public let id = UUID()

public init(title: String, url: URL, isActive: Bool) {
self.title = title
self.url = url
self.isActive = isActive
}
}

public struct FAUserGalleryLikePage: Sendable {
public let previews: [FASubmissionsPage.Submission?]
public let displayAuthor: String
public let nextPageUrl: URL?
public let folderItems: [FAFolderItem]
public let folderGroups: [FAFolderGroup]
}

extension FAUserGalleryLikePage {
Expand Down Expand Up @@ -55,16 +74,16 @@ extension FAUserGalleryLikePage {
self.nextPageUrl = nil
}

let folderItemsQuery = "div#page-galleryscraps div#columnpage div.sidebar div.folder-list div.user-folders"
let folderItemsQuery = "div#page-galleryscraps div#columnpage div div.folder-list div.user-folders"
let userItems = try siteContentNode.select(folderItemsQuery)
self.folderItems = try Self.parseFolderItems(from: userItems, currentUrl: url)
self.folderGroups = try Self.parseFolderGroups(from: userItems, currentUrl: url)
} catch {
logger.error("\(#file, privacy: .public) - \(error, privacy: .public)")
return nil
}
}

static func parseFolderItems(from node: SwiftSoup.Elements, currentUrl: URL) throws -> [FAFolderItem] {
static func parseFolderGroups(from node: SwiftSoup.Elements, currentUrl: URL) throws -> [FAFolderGroup] {
enum Error: Swift.Error {
case unexpectedStructure
}
Expand All @@ -77,33 +96,45 @@ extension FAUserGalleryLikePage {
}
}

func parseFolder(in liNode: SwiftSoup.Element) throws -> FAFolderItem {
let title = try liNode.text()
func parseFolder(in liNode: SwiftSoup.Element) throws -> FAFolder {
var title = try liNode.text()
if title.starts(with: "❯❯ ") {
title = String(title.dropFirst(3))
}
let isActive = liNode.hasClass("active")
let url: URL
if let linkNode = try? liNode.select("a").first() {
let urlStr = try linkNode.attr("href")
url = try URL(string: FAURLs.homeUrl.absoluteString + urlStr).unwrap()
} else {
url = currentUrl
}
return .folder(title: title, url: url)
return .init(title: title, url: url, isActive: isActive)
}

var folderItems = [FAFolderItem]()
var folderGroups = [FAFolderGroup]()
var unfinishedFolderGroupTitle: String?

for child in node[0].children() {
if child.tagName() == "div" && child.hasClass("container-item-top") {
let title = try child.text()
folderItems.append(.section(title: title))
} else if child.tagName() == "div" && child.hasClass("default-folders") {
folderItems.append(contentsOf: try child.select("li").map(parseFolder))
} else if child.tagName() == "ul" {
folderItems.append(contentsOf: try child.select("li").map(parseFolder))
unfinishedFolderGroupTitle = try child.text()
} else if (child.tagName() == "div" && child.hasClass("default-folders")) || child.tagName() == "ul" {
// HTML structure is modified on mobile and first item is missing
if unfinishedFolderGroupTitle == nil && folderGroups.isEmpty {
unfinishedFolderGroupTitle = "Gallery Folders"
}
let folders = try child.select("li").map(parseFolder)
folderGroups.append(.init(
title: unfinishedFolderGroupTitle.take(),
folders: folders
))
} else {
let html = (try? child.html()) ?? ""
logger.error("Unhandled tag \(child.tagName(), privacy: .public) in \(html, privacy: .public)")
throw Error.unexpectedStructure
}
}
return folderItems

return folderGroups
}
}
10 changes: 5 additions & 5 deletions FAKit/Tests/FAPagesTests/FAJournalsPageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ struct FAJournalsPageTests {
#expect(page.journals.count == 25)
#expect(page.journals.prefix(5) == [
.init(id: 10954574, title: "I'll resume posting art!",
datetime: "Sep 14, 2024 03:17 AM", naturalDatetime: "a month ago",
datetime: "Sep 14, 2024 04:17 AM", naturalDatetime: "3 months ago",
url: URL(string: "https://www.furaffinity.net/journal/10954574/")!),
.init(id: 10893232, title: "fullbody commissions (CLOSED)",
datetime: "Jun 23, 2024 07:14 PM", naturalDatetime: "3 months ago",
datetime: "Jun 23, 2024 08:14 PM", naturalDatetime: "6 months ago",
url: URL(string: "https://www.furaffinity.net/journal/10893232/")!),
.init(id: 10877414, title: "Pride themed group YCH closed!!",
datetime: "Jun 1, 2024 03:03 AM", naturalDatetime: "4 months ago",
datetime: "Jun 1, 2024 04:03 AM", naturalDatetime: "7 months ago",
url: URL(string: "https://www.furaffinity.net/journal/10877414/")!),
.init(id: 10815815, title: "Change on commissions!",
datetime: "Mar 2, 2024 03:13 AM", naturalDatetime: "7 months ago",
datetime: "Mar 2, 2024 04:13 AM", naturalDatetime: "10 months ago",
url: URL(string: "https://www.furaffinity.net/journal/10815815/")!),
.init(id: 10691323, title: "Follow me on BlueSky! (and other places)",
datetime: "Sep 20, 2023 02:50 AM", naturalDatetime: "a year ago",
datetime: "Sep 20, 2023 03:50 AM", naturalDatetime: "a year ago",
url: URL(string: "https://www.furaffinity.net/journal/10691323/")!),
])
}
Expand Down
Loading

0 comments on commit a7ed81f

Please sign in to comment.