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

Automatic collapse child comments #841

Merged
merged 32 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5c36133
[Feature] Added automatic collapse child comments, including settings…
schmeat Jan 3, 2024
25a70ba
- Updated icon for collapse comment settings
schmeat Jan 3, 2024
71052be
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 3, 2024
e74dd8a
- Added isCollapsed flag to parentCollapsed to make only parent comme…
schmeat Jan 3, 2024
155bc1d
- Updated logic for calculating when to hide comments automatically
schmeat Jan 3, 2024
e24c713
- Code clean up for auto collapse comments
schmeat Jan 3, 2024
b839613
- Updated text and location of Collapse comments in settings
schmeat Jan 3, 2024
67e60e9
- Switch back enum to struct
schmeat Jan 4, 2024
24980b0
- switch back enum to struct
schmeat Jan 4, 2024
0d513de
- switching enum to struct, third time's the charm?
schmeat Jan 4, 2024
ea36093
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 4, 2024
c3a0ce0
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 4, 2024
d5be159
- Rough logic and UI for collapse comments automatically with a bar b…
schmeat Jan 5, 2024
4212d58
- Updated appearance of the collapsed child comment text box
schmeat Jan 5, 2024
467929a
- Iterating through depth 1 comments to also uncollapse for comment r…
schmeat Jan 5, 2024
5aa5391
- added padding top and bottom to CollapsedCommentReplies
schmeat Jan 6, 2024
44ca122
- restricted height and changed colour to .accent for CollapsedCommen…
schmeat Jan 6, 2024
05e83b2
- refactored uncollapse comment logic into method
schmeat Jan 6, 2024
7f0b9e9
- Fixed logic for top level comment collapsing/uncollapsing
schmeat Jan 7, 2024
ccb8279
Update Mlem/Views/Shared/Comments/Components/CollapsedCommentReplies.…
schmeat Jan 7, 2024
722694f
- set isCommentReplyHidden to false when disappearing view so that it…
schmeat Jan 8, 2024
b11eeba
Update Mlem/Views/Shared/Comments/Comment Item.swift
schmeat Jan 8, 2024
32f55ef
- Check different child count to prevent child comment button from sh…
schmeat Jan 8, 2024
72d6cd9
- formatting
schmeat Jan 8, 2024
6317985
- Added page context to collapsing child comments, bypassing a lot of…
schmeat Jan 10, 2024
3830ba0
- formatting
schmeat Jan 10, 2024
347be9b
- re-added ondisappear for coming back from profile
schmeat Jan 11, 2024
f005efa
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 14, 2024
67dd861
- added check before reloading comments on page (thanks sjmarf)
schmeat Jan 14, 2024
270f90c
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 16, 2024
a4085d6
Merge branch 'dev' into feature/collapse-child-comments
schmeat Jan 17, 2024
ac1e036
Merge branch 'dev' of https://github.com/schmeat/mlem into feature/co…
schmeat Jan 29, 2024
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
4 changes: 4 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
6DEB0FFB2A4F87BF007CAB99 /* User Moderator View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB0FFA2A4F87BF007CAB99 /* User Moderator View.swift */; };
6DFF50432A48DED3001E648D /* Inbox View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFF50422A48DED3001E648D /* Inbox View.swift */; };
6DFF50452A48E373001E648D /* GetPrivateMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFF50442A48E373001E648D /* GetPrivateMessages.swift */; };
6FB4A4DE2B47860B00A7CD82 /* CollapsedCommentReplies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB4A4DD2B47860B00A7CD82 /* CollapsedCommentReplies.swift */; };
88B165B82A8643F4007C9115 /* View+NavigationBarColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88B165B72A8643F4007C9115 /* View+NavigationBarColor.swift */; };
AD1B0D352A5F63F60006F554 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD1B0D342A5F63F60006F554 /* AboutView.swift */; };
AD1B0D372A5F7A260006F554 /* Licenses.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD1B0D362A5F7A260006F554 /* Licenses.swift */; };
Expand Down Expand Up @@ -837,6 +838,7 @@
6DEB0FFA2A4F87BF007CAB99 /* User Moderator View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "User Moderator View.swift"; sourceTree = "<group>"; };
6DFF50422A48DED3001E648D /* Inbox View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox View.swift"; sourceTree = "<group>"; };
6DFF50442A48E373001E648D /* GetPrivateMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPrivateMessages.swift; sourceTree = "<group>"; };
6FB4A4DD2B47860B00A7CD82 /* CollapsedCommentReplies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsedCommentReplies.swift; sourceTree = "<group>"; };
88B165B72A8643F4007C9115 /* View+NavigationBarColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+NavigationBarColor.swift"; sourceTree = "<group>"; };
AD1B0D342A5F63F60006F554 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
AD1B0D362A5F7A260006F554 /* Licenses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Licenses.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2039,6 +2041,7 @@
children = (
CDE6A80C2A45EAB30062D161 /* Embedded Post.swift */,
CD391F992A537EF900E213B5 /* CommentBodyView.swift */,
6FB4A4DD2B47860B00A7CD82 /* CollapsedCommentReplies.swift */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -3451,6 +3454,7 @@
6D15D74C2A44DC240061B5CB /* Date+RelativeTime.swift in Sources */,
CDA217E62A63016A00BDA173 /* ReportMessage.swift in Sources */,
CD9DD8832A622A6C0044EA8E /* ReportCommentReply.swift in Sources */,
6FB4A4DE2B47860B00A7CD82 /* CollapsedCommentReplies.swift in Sources */,
CD3FBCE12A4A836000B2063F /* AllItemsFeedView.swift in Sources */,
6D91D4582A4159D8006B8F9A /* CommunityListRowViews.swift in Sources */,
63F0C7B92A0533C700A18C5D /* Add Account View.swift in Sources */,
Expand Down
25 changes: 18 additions & 7 deletions Mlem/API/Internal/HierarchicalComment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ class HierarchicalComment: ObservableObject {
/// Indicates whether the *current* comment is collapsed.
@Published var isCollapsed: Bool = false

init(comment: APICommentView, children: [HierarchicalComment], parentCollapsed: Bool, collapsed: Bool) {
init(
comment: APICommentView,
children: [HierarchicalComment],
parentCollapsed: Bool,
collapsed: Bool,
shouldCollapseChildren: Bool = false
) {
let depth = max(0, comment.comment.path.split(separator: ".").count - 2)

self.commentView = comment
self.children = children
self.depth = max(0, commentView.comment.path.split(separator: ".").count - 2)
self.isParentCollapsed = parentCollapsed
self.isCollapsed = collapsed
self.depth = depth
self.isParentCollapsed = shouldCollapseChildren && depth >= 1 || parentCollapsed
self.isCollapsed = shouldCollapseChildren && depth == 1 || collapsed
self.links = comment.comment.content.parseLinks()
}
}
Expand Down Expand Up @@ -161,23 +169,26 @@ extension [APICommentView] {
}

let identifiedComments = Dictionary(uniqueKeysWithValues: allComments.lazy.map { ($0.id, $0) })

let collapseChildComments = UserDefaults.standard.bool(forKey: "collapseChildComments")

/// Recursively populates child comments by looking up IDs from `childrenById`
func populateChildren(_ comment: APICommentView) -> HierarchicalComment {
guard let childIds = childrenById[comment.id] else {
return .init(
comment: comment,
children: [],
parentCollapsed: false,
collapsed: false
collapsed: false,
shouldCollapseChildren: collapseChildComments
)
}

let commentWithChildren = HierarchicalComment(
comment: comment,
children: [],
parentCollapsed: false,
collapsed: false
collapsed: false,
shouldCollapseChildren: collapseChildComments
)
commentWithChildren.children = childIds
.compactMap { id -> HierarchicalComment? in
Expand Down
3 changes: 2 additions & 1 deletion Mlem/Icons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
import SwiftUI

/// SFSymbol names for icons
struct Icons {
enum Icons {
Sjmarf marked this conversation as resolved.
Show resolved Hide resolved
// votes
static let votes: String = "arrow.up.arrow.down.square"
static let upvote: String = "arrow.up"
Expand Down Expand Up @@ -187,6 +187,7 @@ struct Icons {
static let developerMode: String = "wrench.adjustable.fill"
static let limitImageHeightSetting: String = "rectangle.compress.vertical"
static let swipeUpGestureSetting: String = "arrow.up.to.line.alt"
static let collapseComments: String = "arrow.down.and.line.horizontal.and.arrow.up"

// misc
static let switchUser: String = "person.crop.circle.badge.plus"
Expand Down
31 changes: 30 additions & 1 deletion Mlem/Views/Shared/Comments/Comment Item Logic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,41 @@ extension CommentItem {
func toggleCollapsed() {
withAnimation(.showHideComment(!hierarchicalComment.isCollapsed)) {
// Perhaps we want an explict flag for this in the future?
if !showPostContext {
if collapseComments, !isCommentReplyHidden, pageContext == .posts {
toggleTopLevelCommentCollapse(isCollapsed: !hierarchicalComment.isCollapsed)
} else if !showPostContext {
commentTracker.setCollapsed(!hierarchicalComment.isCollapsed, comment: hierarchicalComment)
}
}
}

/// Uncollapses HierarchicalComment and children at depth level 1
func uncollapseComment() {
commentTracker.setCollapsed(false, comment: hierarchicalComment)

for comment in hierarchicalComment.children where comment.depth == 1 {
commentTracker.setCollapsed(false, comment: comment)
}
}

// Collapses the top level comment and retains the child comment collapse state
// If a user views all child comments, then collapses top level comment, the children will be uncollapsed along side top level
func toggleTopLevelCommentCollapse(isCollapsed: Bool) {
hierarchicalComment.isCollapsed = isCollapsed

if !isCollapsed {
isCommentReplyHidden = false
}
}

/// Collapses HierarchicalComment and children at depth level 1
func collapseComment() {
for comment in hierarchicalComment.children where comment.depth == 1 {
commentTracker.setCollapsed(true, comment: comment)
comment.isParentCollapsed = true
}
}

// MARK: helpers

// swiftlint:disable function_body_length
Expand Down
41 changes: 39 additions & 2 deletions Mlem/Views/Shared/Comments/Comment Item.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ struct CommentItem: View {
case standard, never
}

enum PageContext {
case posts, profile
}

@Dependency(\.apiClient) var apiClient
@Dependency(\.commentRepository) var commentRepository
@Dependency(\.errorHandler) var errorHandler
Expand All @@ -27,6 +31,7 @@ struct CommentItem: View {
@AppStorage("shouldShowSavedInCommentBar") var shouldShowSavedInCommentBar: Bool = false
@AppStorage("shouldShowRepliesInCommentBar") var shouldShowRepliesInCommentBar: Bool = true
@AppStorage("compactComments") var compactComments: Bool = false
@AppStorage("collapseChildComments") var collapseComments: Bool = false

// MARK: Temporary

Expand All @@ -35,6 +40,8 @@ struct CommentItem: View {
@State var dirtyScore: Int // = 0
@State var dirtySaved: Bool // = false
@State var dirty: Bool = false

@State var isCommentReplyHidden: Bool = false

@State var isComposingReport: Bool = false

Expand Down Expand Up @@ -64,6 +71,7 @@ struct CommentItem: View {
let showPostContext: Bool
let showCommentCreator: Bool
let enableSwipeActions: Bool
let pageContext: PageContext

// MARK: Destructive confirmation

Expand Down Expand Up @@ -100,14 +108,16 @@ struct CommentItem: View {
indentBehaviour: IndentBehaviour = .standard,
showPostContext: Bool,
showCommentCreator: Bool,
enableSwipeActions: Bool = true
enableSwipeActions: Bool = true,
pageContext: PageContext = .posts
) {
self.hierarchicalComment = hierarchicalComment
self.postContext = postContext
self.indentBehaviour = indentBehaviour
self.showPostContext = showPostContext
self.showCommentCreator = showCommentCreator
self.enableSwipeActions = enableSwipeActions
self.pageContext = pageContext

_dirtyVote = State(initialValue: hierarchicalComment.commentView.myVote ?? .resetVote)
_dirtyScore = State(initialValue: hierarchicalComment.commentView.counts.score)
Expand Down Expand Up @@ -184,11 +194,29 @@ struct CommentItem: View {
Spacer()
.frame(height: AppConstants.postAndCommentSpacing)
}

if collapseComments,
pageContext == .posts,
!hierarchicalComment.isCollapsed,
hierarchicalComment.depth == 0,
hierarchicalComment.children.count > 0,
!isCommentReplyHidden {
Divider()
HStack {
CollapsedCommentReplies(numberOfReplies: .constant(hierarchicalComment.commentView.counts.childCount))
.onTapGesture {
isCommentReplyHidden = true
uncollapseComment()
}
}
}
}
}
.contentShape(Rectangle()) // allow taps in blank space to register
.onTapGesture {
toggleCollapsed()
if pageContext == .posts {
toggleCollapsed()
}
}
.destructiveConfirmation(
isPresentingConfirmDestructive: $isPresentingConfirmDestructive,
Expand All @@ -205,6 +233,15 @@ struct CommentItem: View {
MenuButton(menuFunction: item, confirmDestructive: confirmDestructive)
}
}
.onChange(of: collapseComments) { newValue in
if pageContext == .posts {
if newValue == false {
uncollapseComment()
} else {
collapseComment()
}
}
}
}
// swiftlint:enable function_body_length
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// CollapsedCommentReplies.swift
// Mlem
//
// Created by Sumeet Gill on 2024-01-04.
//

import SwiftUI

struct CollapsedCommentReplies: View {
@Binding var numberOfReplies: Int
var lineWidth: CGFloat = 2

var body: some View {
HStack {
Rectangle()
.border(width: lineWidth, edges: [.leading], color: .accentColor)
.frame(width: lineWidth)
Image(systemName: Icons.replies)
Text("Show ^[\(numberOfReplies) Reply](inflect: true)")
.foregroundStyle(.blue)
.padding(.vertical, 10)
}
.frame(maxHeight: 50)
.padding(.leading, 10)
}
}

#Preview {
CollapsedCommentReplies(numberOfReplies: .constant(1))
}
6 changes: 5 additions & 1 deletion Mlem/Views/Shared/Posts/Expanded Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ struct ExpandedPost: View {
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) { toolbarMenu }
}
.task { await loadComments() }
.task {
if commentTracker.comments.isEmpty {
await loadComments()
}
}
.task { await postTracker.markRead(post: post) }
.refreshable { await refreshComments() }
.onChange(of: commentSortingType) { newSortingType in
Expand Down
8 changes: 4 additions & 4 deletions Mlem/Views/Tabs/Profile/UserFeedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Created by Sjmarf on 25/08/2023.
//

import SwiftUI
import Dependencies
import SwiftUI

struct UserFeedView: View {
@Dependency(\.siteInformation) var siteInformation
Expand All @@ -33,11 +33,10 @@ struct UserFeedView: View {
}

var isOwnProfile: Bool {
return siteInformation.myUserInfo?.localUserView.person.id == user.userId
siteInformation.myUserInfo?.localUserView.person.id == user.userId
Sjmarf marked this conversation as resolved.
Show resolved Hide resolved
}

var body: some View {

LazyVStack(spacing: 0) {
switch selectedTab {
case .communities:
Expand Down Expand Up @@ -117,7 +116,8 @@ struct UserFeedView: View {
postContext: nil,
indentBehaviour: .never,
showPostContext: true,
showCommentCreator: false
showCommentCreator: false,
pageContext: .profile
)

Divider()
Expand Down
20 changes: 10 additions & 10 deletions Mlem/Views/Tabs/Profile/UserView+Logic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ extension UserView {
func tryReloadUser() async {
Sjmarf marked this conversation as resolved.
Show resolved Hide resolved
do {
let authoredContent = try await personRepository.loadUserDetails(for: user.userId, limit: internetSpeed.pageSize)
self.user = UserModel(from: authoredContent)
self.communityTracker.replaceAll(with: user.moderatedCommunities ?? [])
user = UserModel(from: authoredContent)
communityTracker.replaceAll(with: user.moderatedCommunities ?? [])

var savedContentData: GetPersonDetailsResponse?
if isOwnProfile {
Expand Down Expand Up @@ -74,16 +74,16 @@ extension UserView {
privateCommentTracker.comments = newComments
privatePostTracker.reset(with: newPosts)

self.isLoadingContent = false
isLoadingContent = false

} catch {
errorHandler.handle(
.init(
title: "Couldn't load user info",
message: "There was an error while loading user information.\nTry again later.",
underlyingError: error
)
errorHandler.handle(
.init(
title: "Couldn't load user info",
message: "There was an error while loading user information.\nTry again later.",
underlyingError: error
)
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct CommentSettingsView: View {
@AppStorage("shouldShowUserServerInComment") var shouldShowUserServerInComment: Bool = false

@AppStorage("showCommentJumpButton") var showCommentJumpButton: Bool = true
@AppStorage("collapseChildComments") var collapseChildComments: Bool = false
@AppStorage("commentJumpButtonSide") var commentJumpButtonSide: JumpButtonLocation = .bottomTrailing

var body: some View {
Expand All @@ -61,6 +62,11 @@ struct CommentSettingsView: View {
settingName: "Compact Comments",
isTicked: $compactComments
)
SwitchableSettingsItem(
settingPictureSystemName: Icons.collapseComments,
settingName: "Collapse Comments",
isTicked: $collapseChildComments
)

NavigationLink(.commentSettings(.layoutWidget)) {
Label {
Expand Down
Loading