diff --git a/Mlem.xcodeproj/project.pbxproj b/Mlem.xcodeproj/project.pbxproj index 882d2f5a9..30971fcd5 100644 --- a/Mlem.xcodeproj/project.pbxproj +++ b/Mlem.xcodeproj/project.pbxproj @@ -420,6 +420,7 @@ CDF842682A49FB9000723DA0 /* Inbox View Logic.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF842672A49FB9000723DA0 /* Inbox View Logic.swift */; }; CDF8426B2A4A2AB600723DA0 /* Inbox Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8426A2A4A2AB600723DA0 /* Inbox Item.swift */; }; CDF8426F2A4A385A00723DA0 /* Inbox Item Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8426E2A4A385A00723DA0 /* Inbox Item Type.swift */; }; + CDF9EF332AB2845C003F885B /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF9EF322AB2845C003F885B /* Icons.swift */; }; E40E018C2AABF85500410B2C /* NavigationRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018B2AABF85500410B2C /* NavigationRoutes.swift */; }; E40E018E2AABFBDE00410B2C /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */; }; E40E01902AABFC9300410B2C /* AnyNavigationPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */; }; @@ -436,6 +437,7 @@ E4DDB4322A81819300B3A7E0 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DDB4312A81819300B3A7E0 /* Double.swift */; }; E4DDB4342A819C8000B3A7E0 /* QuickLookView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DDB4332A819C8000B3A7E0 /* QuickLookView.swift */; }; E4F0B5722AC2581800BC3E4A /* RoutableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F0B5712AC2581800BC3E4A /* RoutableTests.swift */; }; + E4F0B56F2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F0B56E2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -867,6 +869,7 @@ CDF842672A49FB9000723DA0 /* Inbox View Logic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox View Logic.swift"; sourceTree = ""; }; CDF8426A2A4A2AB600723DA0 /* Inbox Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox Item.swift"; sourceTree = ""; }; CDF8426E2A4A385A00723DA0 /* Inbox Item Type.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Inbox Item Type.swift"; sourceTree = ""; }; + CDF9EF322AB2845C003F885B /* Icons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icons.swift; sourceTree = ""; }; E40E018B2AABF85500410B2C /* NavigationRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRoutes.swift; sourceTree = ""; }; E40E018D2AABFBDE00410B2C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = ""; }; E40E018F2AABFC9300410B2C /* AnyNavigationPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyNavigationPath.swift; sourceTree = ""; }; @@ -883,6 +886,7 @@ E4DDB4312A81819300B3A7E0 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = ""; }; E4DDB4332A819C8000B3A7E0 /* QuickLookView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookView.swift; sourceTree = ""; }; E4F0B5712AC2581800BC3E4A /* RoutableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutableTests.swift; sourceTree = ""; }; + E4F0B56E2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationBackgroundInteraction.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1361,6 +1365,7 @@ 508845CE2A3641160088E483 /* JSONDecoder+Default.swift */, B1A26FE02A44AAB200B91A32 /* Navigation getter.swift */, B104A6DF2A59C19400B3E725 /* OperationQueue - Easy init.swift */, + E4F0B56E2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift */, 6386E0392A0455BC006B3C1D /* String - Contains Elements From Array.swift */, 630737882A1CD1E900039852 /* String.swift */, 5064D0402A6E63E000B22EE3 /* Task+Notifiable.swift */, @@ -1489,6 +1494,7 @@ 503422572AAB798600EFE88D /* AppFlow.swift */, 6386E02B2A03D1EC006B3C1D /* App State.swift */, 63DF71F02A02999C002AC14E /* App Constants.swift */, + CDF9EF322AB2845C003F885B /* Icons.swift */, B1B78D632A51D53900F72485 /* AppDelegate.swift */, 6363D5C827EE196A00E34822 /* Assets.xcassets */, 6363D5CA27EE196A00E34822 /* Preview Content */, @@ -2641,6 +2647,7 @@ 505240E72A88D36D00EA4558 /* SectionIndexTitles.swift in Sources */, 5064D0452A71549C00B22EE3 /* NotificationMessage.swift in Sources */, 63344C4D2A07ABEE001BC616 /* Community.swift in Sources */, + E4F0B56F2ABD00A000BC3E4A /* PresentationBackgroundInteraction.swift in Sources */, CDF842612A49EA3900723DA0 /* Mentions Tracker.swift in Sources */, 6D693A4C2A51B99E009E2D76 /* APICommentReport.swift in Sources */, 63344C672A08D4E3001BC616 /* AppearanceSettingsView.swift in Sources */, @@ -2690,6 +2697,7 @@ CD04D5DF2A361585008EF95B /* Empty Button Style.swift in Sources */, CD69F55B2A400D820028D4F7 /* App Theme.swift in Sources */, CDEBC3392A9ADE6C00518D9D /* APIClient+Post.swift in Sources */, + CDF9EF332AB2845C003F885B /* Icons.swift in Sources */, B14E93C22A45D3B300D6DA93 /* Community Link.swift in Sources */, 637218712A3A2AAD008C4816 /* GetSite.swift in Sources */, CD0BE42F2A65A73600314B24 /* Haptic Manager.swift in Sources */, diff --git a/Mlem/API/APIClient/APIClient+Post.swift b/Mlem/API/APIClient/APIClient+Post.swift index 28457eafb..105294b11 100644 --- a/Mlem/API/APIClient/APIClient+Post.swift +++ b/Mlem/API/APIClient/APIClient+Post.swift @@ -8,14 +8,15 @@ import Foundation extension APIClient { + // swiftlint:disable function_parameter_count func loadPosts( communityId: Int?, page: Int, sort: PostSortType?, type: FeedType, - limit: Int? = nil, - savedOnly: Bool? = nil, - communityName: String? = nil + limit: Int?, + savedOnly: Bool?, + communityName: String? ) async throws -> [APIPostView] { let request = try GetPostsRequest( session: session, @@ -30,6 +31,8 @@ extension APIClient { return try await perform(request: request).posts } + + // swiftlint:enable function_parameter_count func markPostAsRead(for postId: Int, read: Bool) async throws -> PostResponse { let request = try MarkPostReadRequest(session: session, postId: postId, read: read) @@ -60,13 +63,14 @@ extension APIClient { return try await perform(request: request) } + // swiftlint:disable function_parameter_count func editPost( postId: Int, name: String?, url: String?, body: String?, nsfw: Bool?, - languageId: Int? = nil + languageId: Int? ) async throws -> PostResponse { let request = try EditPostRequest( session: session, @@ -80,6 +84,8 @@ extension APIClient { return try await perform(request: request) } + + // swiftlint:enable function_parameter_count func ratePost(id: Int, score: ScoringOperation) async throws -> APIPostView { let request = try CreatePostLikeRequest(session: session, postId: id, score: score) diff --git a/Mlem/API/Models/ScoringOperation.swift b/Mlem/API/Models/ScoringOperation.swift index 5753f1c46..c14f652f9 100644 --- a/Mlem/API/Models/ScoringOperation.swift +++ b/Mlem/API/Models/ScoringOperation.swift @@ -13,3 +13,21 @@ enum ScoringOperation: Int, Decodable { case downvote = -1 case resetVote = 0 } + +extension ScoringOperation: AssociatedIcon { + var iconName: String { + switch self { + case .upvote: return Icons.upvoteSquare + case .downvote: return Icons.downvoteSquare + case .resetVote: return Icons.resetVoteSquare + } + } + + var iconNameFill: String { + switch self { + case .upvote: return Icons.upvoteSquareFill + case .downvote: return Icons.downvoteSquareFill + case .resetVote: return Icons.resetVoteSquareFill + } + } +} diff --git a/Mlem/API/Requests/Person/GetPersonDetails.swift b/Mlem/API/Requests/Person/GetPersonDetails.swift index a16f5654d..64072843f 100644 --- a/Mlem/API/Requests/Person/GetPersonDetails.swift +++ b/Mlem/API/Requests/Person/GetPersonDetails.swift @@ -49,7 +49,9 @@ struct GetPersonDetailsRequest: APIGetRequest { guard let host = instanceURL.host() else { throw GetPersonDetailsRequestError.unableToDetermineInstanceHost } - username = "\(username)@\(host)" + + // when logging into a locally running instance, we don't want to pass `user@localhost` + username = host == "localhost" ? username : "\(username)@\(host)" } queryItems.append(.init(name: "username", value: username)) diff --git a/Mlem/App Constants.swift b/Mlem/App Constants.swift index 232b81bec..60d5361ab 100644 --- a/Mlem/App Constants.swift +++ b/Mlem/App Constants.swift @@ -32,6 +32,7 @@ struct AppConstants { static let maxFeedPostHeight: CGFloat = 400 static let maxFeedPostHeightExpanded: CGFloat = 3000 + static let appIconSize: CGFloat = 60 static let thumbnailSize: CGFloat = 60 static let hugeAvatarSize: CGFloat = 120 static let largeAvatarSize: CGFloat = 32 @@ -40,6 +41,7 @@ struct AppConstants { static let largeAvatarSpacing: CGFloat = 10 static let postAndCommentSpacing: CGFloat = 10 // standard spacing for the app static let compactSpacing: CGFloat = 6 // standard spacing for compact things + static let appIconCornerRadius: CGFloat = 10 static let largeItemCornerRadius: CGFloat = 8 // posts, website previews, etc static let smallItemCornerRadius: CGFloat = 6 // settings items, compact thumbnails static let tinyItemCornerRadius: CGFloat = 4 // buttons @@ -53,107 +55,6 @@ struct AppConstants { static let editorOverscroll: CGFloat = 30 static let expandedPostOverscroll: CGFloat = 80 - // MARK: - SFSymbols - - // votes - static let generalVoteSymbolName: String = "arrow.up.arrow.down.square" - - static let plainUpvoteSymbolName: String = "arrow.up" - static let emptyUpvoteSymbolName: String = "arrow.up.square" - static let fullUpvoteSymbolName: String = "arrow.up.square.fill" - - static let plainDownvoteSymbolName: String = "arrow.down" - static let emptyDownvoteSymbolName: String = "arrow.down.square" - static let fullDownvoteSymbolName: String = "arrow.down.square.fill" - - static let emptyResetVoteSymbolName: String = "minus.square" - static let fullResetVoteSymbolName: String = "minus.square.fill" - static let scoringOpToVoteImage: [ScoringOperation?: String] = [.upvote: "arrow.up.square.fill", - .resetVote: "arrow.up.square", - .downvote: "arrow.down.square.fill"] - - // reply/send - static let emptyReplySymbolName: String = "arrowshape.turn.up.left" - static let fullReplySymbolName: String = "arrowshape.turn.up.left.fill" - static let sendSymbolName: String = "paperplane" - static let sendSymbolNameFill: String = "paperplane.fill" - - // save - static let emptySaveSymbolName: String = "bookmark" - static let fullSaveSymbolName: String = "bookmark.fill" - static let emptyUndoSaveSymbolName: String = "bookmark.slash" - static let fullUndoSaveSymbolName: String = "bookmark.slash.fill" - - // mark read - static let emptyMarkReadSymbolName: String = "envelope" - static let fullMarkReadSymbolName: String = "envelope.open.fill" - static let emptyMarkUnreadSymbolName: String = "envelope.open" - static let fullMarkUnreadSymbolName: String = "envelope.fill" - - // report/block - static let reportSymbolName: String = "exclamationmark.shield" - static let blockUserSymbolName: String = "person.fill.xmark" - - // post sizes - static let postSizeSettingsSymbolName: String = "rectangle.expand.vertical" - static let compactSymbolName: String = "rectangle.grid.1x2" - static let compactSymbolNameFill: String = "rectangle.grid.1x2.fill" - static let headlineSymbolName: String = "rectangle" - static let headlineSymbolNameFill: String = "rectangle.fill" - static let largeSymbolName: String = "text.below.photo" - static let largeSymbolNameFill: String = "text.below.photo.fill" - static let blurNsfwSymbolName: String = "eye.trianglebadge.exclamationmark" - - // feeds - static let federatedFeedSymbolName: String = "circle.hexagongrid.circle" // "arrow.left.arrow.right.circle" - static let federatedFeedSymbolNameFill: String = "circle.hexagongrid.circle.fill" // "arrow.left.arrow.right.circle.fill" - static let localFeedSymbolName: String = "house.circle" - static let localFeedSymbolNameFill: String = "house.circle.fill" - static let subscribedFeedSymbolName: String = "newspaper.circle" - static let subscribedFeedSymbolNameFill: String = "newspaper.circle.fill" - static let limitImageHeightInFeedSymbolName: String = "rectangle.compress.vertical" - - // sort types - static let activeSortSymbolName: String = "popcorn" // not married to this idea - static let activeSortSymbolNameFill: String = "popcorn.fill" - static let hotSortSymbolName: String = "flame" - static let hotSortSymbolNameFill: String = "flame.fill" - // we can workshop new/old--books is already used for documentation and there's an issue open saying that "new" needs a better symbol. I thought these two were funny together. - static let newSortSymbolName: String = "hare" - static let newSortSymbolNameFill: String = "hare.fill" - static let oldSortSymbolName: String = "tortoise" - static let oldSortSymbolNameFill: String = "tortoise.fill" - static let newCommentsSymbolName: String = "exclamationmark.bubble" - static let newCommentsSymbolNameFill: String = "exclamationmark.bubble.fill" - static let mostCommentsSymbolName: String = "bubble.left.and.bubble.right" - static let mostCommentsSymbolNameFill: String = "bubble.left.and.bubble.right.fill" - static let topSymbolName: String = "trophy" - static let topSymbolNameFill: String = "trophy.fill" - static let timeSymbolName: String = "calendar.day.timeline.leading" - static let timeSymbolNameFill: String = "calendar.day.timeline.leading.fill" - - // common operations - static let shareSymbolName: String = "square.and.arrow.up" - static let subscribeSymbolName: String = "plus.circle" - static let unsubscribeSymbolName: String = "multiply.circle" - static let blockSymbolName: String = "eye.slash" - static let unblockSymbolName: String = "eye" - static let filterSymbolName: String = "line.3.horizontal.decrease.circle" - static let filterSymbolNameFill: String = "line.3.horizontal.decrease.circle.fill" - - // misc - static let switchUserSymbolName: String = "person.crop.circle.badge.plus" - static let missingSymbolName: String = "questionmark.square.dashed" - static let connectionSymbolName: String = "antenna.radiowaves.left.and.right" - static let hapticSymbolName: String = "hand.tap" - static let transparencySymbolName: String = "square.on.square.intersection.dashed" - static let presentSymbolName: String = "circle.fill" - static let absentSymbolName: String = "circle" - static let iconSymbolName: String = "fleuron" - static let userSymbolName: String = "person.circle" - static let bannerSymbolName: String = "flag" - static let communitySymbolName: String = "building.2.crop.circle" - // MARK: - Other static let pictureEmoji: [String] = ["πŸŽ†", "πŸŽ‡", "🌠", "πŸŒ…", "πŸŒ†", "🌁", "πŸŒƒ", "πŸŒ„", "πŸŒ‰", "🌌", "πŸŒ‡", "πŸ–ΌοΈ", "πŸŽ‘", "🏞️", "πŸ—Ύ", "πŸ™οΈ"] diff --git a/Mlem/Assets.xcassets/Icons/Classic Lemmy by Eric Andrews.appiconset/Classic Lemmy.png b/Mlem/Assets.xcassets/Icons/Classic Lemmy by Eric Andrews.appiconset/Classic Lemmy.png index 3168e6366..8ce606adf 100644 Binary files a/Mlem/Assets.xcassets/Icons/Classic Lemmy by Eric Andrews.appiconset/Classic Lemmy.png and b/Mlem/Assets.xcassets/Icons/Classic Lemmy by Eric Andrews.appiconset/Classic Lemmy.png differ diff --git a/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/Contents.json b/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/Contents.json new file mode 100644 index 000000000..d167619eb --- /dev/null +++ b/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "stargazer.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/stargazer.png b/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/stargazer.png new file mode 100644 index 000000000..33b30c43e Binary files /dev/null and b/Mlem/Assets.xcassets/Icons/Stargazer By Sjmarf.appiconset/stargazer.png differ diff --git a/Mlem/ContentView.swift b/Mlem/ContentView.swift index 63061076e..380991a49 100644 --- a/Mlem/ContentView.swift +++ b/Mlem/ContentView.swift @@ -119,11 +119,15 @@ struct ContentView: View { NavigationStack { ResponseEditorView(concreteEditorModel: editing) } + .presentationDetents([.medium, .large], selection: .constant(.large)) + ._presentationBackgroundInteraction(enabledUpThrough: .medium) } .sheet(item: $editorTracker.editPost) { editing in NavigationStack { PostComposerView(editModel: editing) } + .presentationDetents([.medium, .large], selection: .constant(.large)) + ._presentationBackgroundInteraction(enabledUpThrough: .medium) } .environment(\.openURL, OpenURLAction(handler: didReceiveURL)) .environmentObject(editorTracker) diff --git a/Mlem/Custom Tab Bar/FancyTabBarLabel.swift b/Mlem/Custom Tab Bar/FancyTabBarLabel.swift index 38198165e..87f2193b1 100644 --- a/Mlem/Custom Tab Bar/FancyTabBarLabel.swift +++ b/Mlem/Custom Tab Bar/FancyTabBarLabel.swift @@ -14,11 +14,11 @@ struct FancyTabBarLabel: View { let activeSymbol: String let remoteSymbolUrl: URL? - static var feed: Self { .init(symbol: "scroll", activeSymbol: "scroll.fill", remoteSymbolUrl: nil) } - static var inbox: Self { .init(symbol: "mail.stack", activeSymbol: "mail.stack.fill", remoteSymbolUrl: nil) } - static var profile: Self { .init(symbol: "person.circle", activeSymbol: "person.circle.fill", remoteSymbolUrl: nil) } - static var search: Self { .init(symbol: "magnifyingglass", activeSymbol: "text.magnifyingglass", remoteSymbolUrl: nil) } - static var settings: Self { .init(symbol: "gear", activeSymbol: "gear", remoteSymbolUrl: nil) } + static var feed: Self { .init(symbol: Icons.feeds, activeSymbol: Icons.feedsFill, remoteSymbolUrl: nil) } + static var inbox: Self { .init(symbol: Icons.inbox, activeSymbol: Icons.inboxFill, remoteSymbolUrl: nil) } + static var profile: Self { .init(symbol: Icons.user, activeSymbol: Icons.userFill, remoteSymbolUrl: nil) } + static var search: Self { .init(symbol: Icons.search, activeSymbol: Icons.searchActive, remoteSymbolUrl: nil) } + static var settings: Self { .init(symbol: Icons.settings, activeSymbol: Icons.settings, remoteSymbolUrl: nil) } } @Environment(\.tabSelectionHashValue) private var selectedTagHashValue diff --git a/Mlem/Enums/FeedType.swift b/Mlem/Enums/FeedType.swift index b6f478e72..ef8e5dc67 100644 --- a/Mlem/Enums/FeedType.swift +++ b/Mlem/Enums/FeedType.swift @@ -26,17 +26,17 @@ enum FeedType: String, Encodable, SettingsOptions { extension FeedType: AssociatedIcon { var iconName: String { switch self { - case .all: return AppConstants.federatedFeedSymbolName - case .local: return AppConstants.localFeedSymbolName - case .subscribed: return AppConstants.subscribedFeedSymbolName + case .all: return Icons.federatedFeed + case .local: return Icons.localFeed + case .subscribed: return Icons.subscribedFeed } } var iconNameFill: String { switch self { - case .all: return AppConstants.federatedFeedSymbolName - case .local: return AppConstants.localFeedSymbolNameFill - case .subscribed: return AppConstants.subscribedFeedSymbolNameFill + case .all: return Icons.federatedFeed + case .local: return Icons.localFeedFill + case .subscribed: return Icons.subscribedFeedFill } } diff --git a/Mlem/Enums/Settings/PostSize.swift b/Mlem/Enums/Settings/PostSize.swift index 3e221045c..fef6d99be 100644 --- a/Mlem/Enums/Settings/PostSize.swift +++ b/Mlem/Enums/Settings/PostSize.swift @@ -22,17 +22,17 @@ extension PostSize: SettingsOptions { extension PostSize: AssociatedIcon { var iconName: String { switch self { - case .compact: return AppConstants.compactSymbolName - case .headline: return AppConstants.headlineSymbolName - case .large: return AppConstants.largeSymbolName + case .compact: return Icons.compactPost + case .headline: return Icons.headlinePost + case .large: return Icons.largePost } } var iconNameFill: String { switch self { - case .compact: return AppConstants.compactSymbolNameFill - case .headline: return AppConstants.headlineSymbolNameFill - case .large: return AppConstants.largeSymbolNameFill + case .compact: return Icons.compactPostFill + case .headline: return Icons.headlinePostFill + case .large: return Icons.largePostFill } } } diff --git a/Mlem/Enums/Settings/PostSortType.swift b/Mlem/Enums/Settings/PostSortType.swift index f93906c1d..3935bb840 100644 --- a/Mlem/Enums/Settings/PostSortType.swift +++ b/Mlem/Enums/Settings/PostSortType.swift @@ -84,25 +84,25 @@ extension PostSortType: SettingsOptions { extension PostSortType: AssociatedIcon { var iconName: String { switch self { - case .active: return AppConstants.activeSortSymbolName - case .hot: return AppConstants.hotSortSymbolName - case .new: return AppConstants.newSortSymbolName - case .old: return AppConstants.oldSortSymbolName - case .newComments: return AppConstants.newCommentsSymbolName - case .mostComments: return AppConstants.mostCommentsSymbolName - default: return AppConstants.timeSymbolName + case .active: return Icons.activeSort + case .hot: return Icons.hotSort + case .new: return Icons.newSort + case .old: return Icons.oldSort + case .newComments: return Icons.newCommentsSort + case .mostComments: return Icons.mostCommentsSort + default: return Icons.timeSort } } var iconNameFill: String { switch self { - case .active: return AppConstants.activeSortSymbolNameFill - case .hot: return AppConstants.hotSortSymbolNameFill - case .new: return AppConstants.newSortSymbolNameFill - case .old: return AppConstants.oldSortSymbolNameFill - case .newComments: return AppConstants.newCommentsSymbolNameFill - case .mostComments: return AppConstants.mostCommentsSymbolNameFill - default: return AppConstants.timeSymbolNameFill + case .active: return Icons.activeSortFill + case .hot: return Icons.hotSortFill + case .new: return Icons.newSortFill + case .old: return Icons.oldSortFill + case .newComments: return Icons.newCommentsSortFill + case .mostComments: return Icons.mostCommentsSortFill + default: return Icons.timeSortFill } } } diff --git a/Mlem/Extensions/AlternativeIconCell.swift b/Mlem/Extensions/AlternativeIconCell.swift index 27b15fab3..a3f36b377 100644 --- a/Mlem/Extensions/AlternativeIconCell.swift +++ b/Mlem/Extensions/AlternativeIconCell.swift @@ -21,9 +21,13 @@ struct AlternativeIconCell: View { getImage() .resizable() .scaledToFit() - .frame(width: 60, height: 60) + .frame(width: AppConstants.appIconSize, height: AppConstants.appIconSize) .foregroundColor(Color.white) - .cornerRadius(10.0) + .cornerRadius(AppConstants.appIconCornerRadius) + .overlay { + RoundedRectangle(cornerRadius: AppConstants.appIconCornerRadius) + .stroke(Color(.secondarySystemBackground), lineWidth: 1) + } VStack(alignment: .leading) { Text(icon.name) if let author = icon.author { @@ -34,7 +38,7 @@ struct AlternativeIconCell: View { } Spacer() if icon.selected { - Image(systemName: "checkmark") + Image(systemName: Icons.success) } } }.accessibilityElement(children: .combine) @@ -47,7 +51,7 @@ struct AlternativeIconCell: View { .flatMap { UIImage(named: $0) } .map { Image(uiImage: $0) - } ?? Image(systemName: "questionmark.folder") + } ?? Image(systemName: Icons.noFile) } return Image(uiImage: UIImage(named: id) ?? UIImage(imageLiteralResourceName: id)) } diff --git a/Mlem/Extensions/NSFW Overlay.swift b/Mlem/Extensions/NSFW Overlay.swift index 33f35777c..4b005bfa2 100644 --- a/Mlem/Extensions/NSFW Overlay.swift +++ b/Mlem/Extensions/NSFW Overlay.swift @@ -34,7 +34,7 @@ struct NSFWOverlay: ViewModifier { var nsfwOverlay: some View { if showNsfwFilter { VStack { - Image(systemName: "exclamationmark.triangle") + Image(systemName: Icons.warning) .font(.largeTitle) Text("NSFW") .fontWeight(.black) @@ -50,7 +50,7 @@ struct NSFWOverlay: ViewModifier { .background(.thinMaterial) .cornerRadius(AppConstants.largeItemCornerRadius) } else if isNsfw, shouldBlurNsfw { - Image(systemName: "eye.slash") + Image(systemName: Icons.hide) .foregroundColor(.white) .padding(4) .background(.thinMaterial) diff --git a/Mlem/Extensions/PresentationBackgroundInteraction.swift b/Mlem/Extensions/PresentationBackgroundInteraction.swift new file mode 100644 index 000000000..66bc5e125 --- /dev/null +++ b/Mlem/Extensions/PresentationBackgroundInteraction.swift @@ -0,0 +1,20 @@ +// +// PresentationBackgroundInteraction.swift +// Mlem +// +// Created by Bosco Ho on 2023-09-21. +// + +import SwiftUI + +extension View { + + /// No-op prior to iOS 16.4. + func _presentationBackgroundInteraction(enabledUpThrough detent: PresentationDetent) -> some View { + if #available(iOS 16.4, *) { + return self.presentationBackgroundInteraction(.enabled(upThrough: detent)) + } else { + return self + } + } +} diff --git a/Mlem/Extensions/Swipey Actions.swift b/Mlem/Extensions/Swipey Actions.swift index 68daefece..fc8b72af5 100644 --- a/Mlem/Extensions/Swipey Actions.swift +++ b/Mlem/Extensions/Swipey Actions.swift @@ -172,13 +172,13 @@ struct SwipeyView: ViewModifier { dragBackground .overlay { HStack(spacing: 0) { - Image(systemName: leadingSwipeSymbol ?? "exclamationmark.triangle") + Image(systemName: leadingSwipeSymbol ?? Icons.warning) .font(.title) .frame(width: 20, height: 20) .foregroundColor(.white) .padding(.horizontal, 20) Spacer() - Image(systemName: trailingSwipeSymbol ?? "exclamationmark.triangle") + Image(systemName: trailingSwipeSymbol ?? Icons.warning) .font(.title) .frame(width: 20, height: 20) .foregroundColor(.white) diff --git a/Mlem/Icons.swift b/Mlem/Icons.swift new file mode 100644 index 000000000..8f033bb42 --- /dev/null +++ b/Mlem/Icons.swift @@ -0,0 +1,175 @@ +// +// Icon.swift +// Mlem +// +// Created by Eric Andrews on 2023-09-13. +// + +import Foundation +import SwiftUI + +/// SFSymbol names for icons +struct Icons { + // votes + static let votes: String = "arrow.up.arrow.down.square" + static let upvote: String = "arrow.up" + static let upvoteSquare: String = "arrow.up.square" + static let upvoteSquareFill: String = "arrow.up.square.fill" + static let downvote: String = "arrow.down" + static let downvoteSquare: String = "arrow.down.square" + static let downvoteSquareFill: String = "arrow.down.square.fill" + static let resetVoteSquare: String = "minus.square" + static let resetVoteSquareFill: String = "minus.square.fill" + + // reply/send + static let reply: String = "arrowshape.turn.up.left" + static let replyFill: String = "arrowshape.turn.up.left.fill" + static let send: String = "paperplane" + static let sendFill: String = "paperplane.fill" + + // save + static let save: String = "bookmark" + static let saveFill: String = "bookmark.fill" + static let unsave: String = "bookmark.slash" + static let unsaveFill: String = "bookmark.slash.fill" + + // mark read + static let markRead: String = "envelope" + static let markReadFill: String = "envelope.open.fill" + static let markUnread: String = "envelope.open" + static let markUnreadFill: String = "envelope.fill" + + // moderation + static let moderation: String = "shield" + static let moderationFill: String = "shield.fill" + static let moderationReport: String = "exclamationmark.shield" + + // misc post + static let replies: String = "bubble.right" + static let textPost: String = "text.book.closed" + static let titleOnlyPost: String = "character.bubble" + static let pinned: String = "pin.fill" + static let websiteIcon: String = "globe" + + // post sizes + static let postSizeSetting: String = "rectangle.expand.vertical" + static let compactPost: String = "rectangle.grid.1x2" + static let compactPostFill: String = "rectangle.grid.1x2.fill" + static let headlinePost: String = "rectangle" + static let headlinePostFill: String = "rectangle.fill" + static let largePost: String = "text.below.photo" + static let largePostFill: String = "text.below.photo.fill" + + // feeds + static let federatedFeed: String = "circle.hexagongrid.circle" + static let federatedFeedFill: String = "circle.hexagongrid.circle.fill" + static let localFeed: String = "house.circle" + static let localFeedFill: String = "house.circle.fill" + static let subscribedFeed: String = "newspaper.circle" + static let subscribedFeedFill: String = "newspaper.circle.fill" + + // sort types + static let activeSort: String = "popcorn" + static let activeSortFill: String = "popcorn.fill" + static let hotSort: String = "flame" + static let hotSortFill: String = "flame.fill" + static let newSort: String = "hare" + static let newSortFill: String = "hare.fill" + static let oldSort: String = "tortoise" + static let oldSortFill: String = "tortoise.fill" + static let newCommentsSort: String = "exclamationmark.bubble" + static let newCommentsSortFill: String = "exclamationmark.bubble.fill" + static let mostCommentsSort: String = "bubble.left.and.bubble.right" + static let mostCommentsSortFill: String = "bubble.left.and.bubble.right.fill" + static let topSortMenu: String = "text.line.first.and.arrowtriangle.forward" + static let topSort: String = "trophy" + static let topSortFill: String = "trophy.fill" + static let timeSort: String = "calendar.day.timeline.leading" + static let timeSortFill: String = "calendar.day.timeline.leading.fill" + + // user flairs + static let developerFlair: String = "hammer.fill" + static let adminFlair: String = "crown.fill" + static let botFlair: String = "terminal.fill" + static let opFlair: String = "person.fill" + + // entities/general Lemmy concepts + static let instance: String = "server.rack" + static let user: String = "person.crop.circle" + static let userFill: String = "person.crop.circle.fill" + static let userBlock: String = "person.fill.xmark" + static let community: String = "building.2.crop.circle" + static let communityFill: String = "building.2.crop.circle.fill" + + // tabs + static let feeds: String = "scroll" + static let feedsFill: String = "scroll.fill" + static let inbox: String = "mail.stack" + static let inboxFill: String = "mail.stack.fill" + static let search: String = "magnifyingglass" + static let searchActive: String = "text.magnifyingglass" + static let settings: String = "gear" + + // information/status + static let success: String = "checkmark" + static let successCircle: String = "checkmark.circle" + static let successSquareFill: String = "checkmark.square.fill" + static let failure: String = "xmark" + static let present: String = "circle.fill" // that's present as in "here," not as in "gift" + static let absent: String = "circle" + static let warning: String = "exclamationmark.triangle" + static let hide: String = "eye.slash" + static let show: String = "eye" + static let blurNsfw: String = "eye.trianglebadge.exclamationmark" + static let endOfFeed: String = "figure.climbing" + static let noContent: String = "binoculars" + static let noPosts: String = "text.bubble" + static let time: String = "clock" + static let favorite: String = "star" + static let favoriteFill: String = "star.fill" + + // common operations + static let share: String = "square.and.arrow.up" + static let subscribe: String = "plus.circle" + static let unsubscribe: String = "multiply.circle" + static let filter: String = "line.3.horizontal.decrease.circle" + static let filterFill: String = "line.3.horizontal.decrease.circle.fill" + static let menu: String = "ellipsis" + static let importSymbol: String = "square.and.arrow.down" // Just "import" can't be used :( + + // settings + static let upvoteOnSave: String = "arrow.up.heart" + static let readIndicatorSetting: String = "book" + static let readIndicatorBarSetting: String = "rectangle.leftthird.inset.filled" + static let profileTabSettings: String = "person.text.rectangle" + static let nicknameField: String = "rectangle.and.pencil.and.ellipsis" + static let label: String = "tag" + static let unreadBadge: String = "envelope.badge" + static let showAvatar: String = "person.fill.questionmark" + static let widgetWizard: String = "wand.and.stars" + static let thumbnail: String = "photo" + static let author: String = "signature" + static let websiteAddress: String = "link" + static let leftRight: String = "arrow.left.arrow.right" + static let developerMode: String = "wrench.adjustable.fill" + static let limitImageHeightSetting: String = "rectangle.compress.vertical" + + // misc + static let switchUser: String = "person.crop.circle.badge.plus" + static let missing: String = "questionmark.square.dashed" + static let connection: String = "antenna.radiowaves.left.and.right" + static let haptics: String = "hand.tap" + static let transparency: String = "square.on.square.intersection.dashed" + static let icon: String = "fleuron" + static let banner: String = "flag" + static let noWifi: String = "wifi.slash" + static let easterEgg: String = "gift.fill" + static let jumpButton: String = "chevron.down" + static let jumpButtonCircle: String = "chevron.down.circle" + static let browser: String = "safari" + static let emptySquare: String = "square" + static let dropdown: String = "chevron.down" + static let noFile: String = "questionmark.folder" + static let delete: String = "trash" + static let forward: String = "chevron.right" +} diff --git a/Mlem/Models/Trackers/Post Tracker.swift b/Mlem/Models/Trackers/Post Tracker.swift index cf5f16a42..cd9493c3a 100644 --- a/Mlem/Models/Trackers/Post Tracker.swift +++ b/Mlem/Models/Trackers/Post Tracker.swift @@ -33,6 +33,8 @@ class PostTracker: ObservableObject { private(set) var isLoading: Bool = false // accessible but not published because it causes lots of bad view redraws private(set) var page: Int = 1 + private var hasReachedEnd: Bool = false + // prefetching private let prefetcher = ImagePrefetcher( pipeline: ImagePipeline.shared, @@ -75,9 +77,14 @@ class PostTracker: ObservableObject { type: type, limit: internetSpeed.pageSize ) - await add(newPosts, filtering: filtering) - page += 1 - } while !newPosts.isEmpty && numItems > items.count + AppConstants.infiniteLoadThresholdOffset + + if newPosts.isEmpty { + hasReachedEnd = true + } else { + await add(newPosts, filtering: filtering) + page += 1 + } + } while !hasReachedEnd && numItems > items.count + AppConstants.infiniteLoadThresholdOffset // so although the API kindly returns `400`/"not_logged_in" for expired // sessions _without_ 2FA enabled, currently once you enable 2FA on an account @@ -122,7 +129,8 @@ class PostTracker: ObservableObject { communityId: communityId, page: page, sort: sort, - type: feedType + type: feedType, + limit: internetSpeed.pageSize ) await reset(with: newPosts, filteredWith: filtering) @@ -161,6 +169,7 @@ class PostTracker: ObservableObject { with newItems: [PostModel] = .init(), filteredWith filter: @escaping (_: PostModel) -> Bool = { _ in true } ) { + hasReachedEnd = false page = newItems.isEmpty ? 1 : 2 ids = .init(minimumCapacity: 1000) items = dedupedItems(from: newItems.filter(filter)) @@ -170,7 +179,7 @@ class PostTracker: ObservableObject { /// NOTE: this is equivalent to the old shouldLoadContentPreciselyAfter @MainActor func shouldLoadContentAfter(after item: PostModel) -> Bool { - guard !isLoading else { return false } + guard !isLoading, !hasReachedEnd else { return false } let thresholdIndex = max(0, items.index(items.endIndex, offsetBy: AppConstants.infiniteLoadThresholdOffset)) if thresholdIndex >= 0, @@ -441,6 +450,8 @@ class PostTracker: ObservableObject { break } } + + prefetcher.startPrefetching(with: imageRequests) } /// Filters a list of PostModels to only those PostModels not present in ids. Updates ids. diff --git a/Mlem/Notifications/NotificationDisplayer.swift b/Mlem/Notifications/NotificationDisplayer.swift index 00dd893b1..08ef57b05 100644 --- a/Mlem/Notifications/NotificationDisplayer.swift +++ b/Mlem/Notifications/NotificationDisplayer.swift @@ -265,13 +265,13 @@ private struct Toast: View { var icon: some View { switch style { case .success: - Image(systemName: "checkmark") + Image(systemName: Icons.success) .foregroundColor(.green) case .error: - Image(systemName: "xmark") + Image(systemName: Icons.failure) .foregroundColor(.red) case .noInternet: - Image(systemName: "wifi.slash") + Image(systemName: Icons.noWifi) .foregroundColor(.red) case .loader: ProgressView() @@ -281,7 +281,7 @@ private struct Toast: View { .resizable() .frame(width: 30, height: 30) // limit the size of the asset since it's _huge_ } else { - Image(systemName: "gift.fill") + Image(systemName: Icons.easterEgg) .foregroundColor(.pink) } } diff --git a/Mlem/Repositories/PostRepository.swift b/Mlem/Repositories/PostRepository.swift index 6f9dd5d73..d6ae89752 100644 --- a/Mlem/Repositories/PostRepository.swift +++ b/Mlem/Repositories/PostRepository.swift @@ -16,7 +16,7 @@ class PostRepository { page: Int, sort: PostSortType?, type: FeedType, - limit: Int? = nil, + limit: Int, savedOnly: Bool? = nil, communityName: String? = nil ) async throws -> [PostModel] { @@ -77,20 +77,23 @@ class PostRepository { /// - url: new post url /// - body: new post body /// - nsfw: new post nsfw status + /// - languageId: the language id for the post if available /// - Returns: PostModel with the new state of the post func editPost( postId: Int, name: String?, url: String?, body: String?, - nsfw: Bool? + nsfw: Bool?, + languageId: Int? = nil ) async throws -> PostModel { let response = try await apiClient.editPost( postId: postId, name: name, url: url, body: body, - nsfw: nsfw + nsfw: nsfw, + languageId: languageId ) return PostModel(from: response.postView) } diff --git a/Mlem/Views/Shared/Accounts/Accounts Page.swift b/Mlem/Views/Shared/Accounts/Accounts Page.swift index 9d39a3ba3..93a0bb96f 100644 --- a/Mlem/Views/Shared/Accounts/Accounts Page.swift +++ b/Mlem/Views/Shared/Accounts/Accounts Page.swift @@ -67,7 +67,7 @@ struct AccountsPage: View { Button { isShowingInstanceAdditionSheet = true } label: { - Label("Add Account", systemImage: AppConstants.switchUserSymbolName) + Label("Add Account", systemImage: Icons.switchUser) } .accessibilityLabel("Add a new account.") diff --git a/Mlem/Views/Shared/Accounts/Add Account View.swift b/Mlem/Views/Shared/Accounts/Add Account View.swift index 419f861f0..126348af6 100644 --- a/Mlem/Views/Shared/Accounts/Add Account View.swift +++ b/Mlem/Views/Shared/Accounts/Add Account View.swift @@ -266,7 +266,7 @@ struct AddSavedInstanceView: View { Text("Logging In") case .success: Spacer() - Image(systemName: "checkmark.circle") + Image(systemName: Icons.successCircle) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 120) diff --git a/Mlem/Views/Shared/Accounts/Components/Instance Summary.swift b/Mlem/Views/Shared/Accounts/Components/Instance Summary.swift index 8087f3cb8..f96736635 100644 --- a/Mlem/Views/Shared/Accounts/Components/Instance Summary.swift +++ b/Mlem/Views/Shared/Accounts/Components/Instance Summary.swift @@ -22,8 +22,8 @@ struct InstanceSummary: View { @State var isPresentingRedirectAlert: Bool = false var isLoading: Bool { siteData == nil && !fetchFailed } - var downvotesSymbolName: String { instance.downvotes ? AppConstants.presentSymbolName : AppConstants.absentSymbolName } - var federatedSymbolName: String { instance.federated ? AppConstants.presentSymbolName : AppConstants.absentSymbolName } + var downvotesSymbolName: String { instance.downvotes ? Icons.present : Icons.absent } + var federatedSymbolName: String { instance.federated ? Icons.present : Icons.absent } var signupURL: URL? { let signupString = "\(instance.url.description)/signup" if let ret = URL(string: signupString) { @@ -132,7 +132,7 @@ struct InstanceSummary: View { shouldExpand: false, fixedSize: CGSize(width: 80, height: 80) ) { - AnyView(Image(systemName: "server.rack") + AnyView(Image(systemName: Icons.instance) .resizable() .frame(width: AppConstants.largeAvatarSize, height: AppConstants.largeAvatarSize)) } diff --git a/Mlem/Views/Shared/Accounts/Instance Picker View.swift b/Mlem/Views/Shared/Accounts/Instance Picker View.swift index 8e25896d0..7ec024ee3 100644 --- a/Mlem/Views/Shared/Accounts/Instance Picker View.swift +++ b/Mlem/Views/Shared/Accounts/Instance Picker View.swift @@ -15,38 +15,62 @@ struct InstancePickerView: View { @Binding var selectedInstance: InstanceMetadata? - @State var instances: [InstanceMetadata]? + @State private var query: String = "" + @State private var instances: [InstanceMetadata]? /// Instances currently accepting new users - var filteredInstances: [InstanceMetadata]? { + var filteredInstances: ArraySlice? { instances? - .filter { instance in - instance.newUsers - } + .filter { query.isEmpty || $0.name.lowercased().hasPrefix(query.lowercased()) } + .prefix(30) // limit to a maximum of 30 } let onboarding: Bool var body: some View { ScrollView { - LazyVStack(spacing: 0) { + VStack(spacing: .zero) { if onboarding { Text(pickInstance) .frame(maxWidth: .infinity) .padding() } + HStack { + Image(systemName: "magnifyingglass") + .foregroundColor(.secondary) + TextField("Search", text: $query, prompt: Text("Looking for a specific instance?")) + .autocorrectionDisabled() + .textInputAutocapitalization(.never) + .textFieldStyle(.roundedBorder) + } + .padding(.horizontal) + .padding(.bottom, 16) + if let filteredInstances { - ForEach(filteredInstances) { instance in - VStack(spacing: 0) { - Divider() - - InstanceSummary( - instance: instance, - onboarding: true, - selectedInstance: $selectedInstance - ) - .padding(.horizontal) + if filteredInstances.isEmpty { + VStack(spacing: 16) { + Spacer() + Text("There are no results for \(query)") + Button { + query = "" + } label: { + Text("Clear") + } + Spacer() + } + } else { + ForEach(filteredInstances) { instance in + VStack(spacing: .zero) { + Divider() + + InstanceSummary( + instance: instance, + onboarding: true, + selectedInstance: $selectedInstance + ) + .padding(.horizontal) + } } } } else { @@ -54,9 +78,15 @@ struct InstancePickerView: View { } } } + .scrollDismissesKeyboard(.immediately) .navigationTitle("Instances") .task { instances = await loadInstances() + // remote source is already sorted by user count but that may change... + .sorted(by: { $0.users > $1.users }) + // restrict the list to instances who are currently ccepting new users + // also filter on the presence of `nsfw` in the version string + .filter { $0.newUsers && !$0.version.contains("nsfw") } } } } diff --git a/Mlem/Views/Shared/Cached Image.swift b/Mlem/Views/Shared/Cached Image.swift index be2447928..dd7789cd8 100644 --- a/Mlem/Views/Shared/Cached Image.swift +++ b/Mlem/Views/Shared/Cached Image.swift @@ -159,7 +159,7 @@ struct CachedImage: View { } static func imageNotFoundDefault() -> AnyView { - AnyView(Image(systemName: "questionmark.square.dashed") + AnyView(Image(systemName: Icons.missing) .resizable() .scaledToFit() .frame(maxWidth: AppConstants.thumbnailSize, maxHeight: AppConstants.thumbnailSize) diff --git a/Mlem/Views/Shared/Comments/Comment Item Logic.swift b/Mlem/Views/Shared/Comments/Comment Item Logic.swift index 701016582..b4983623e 100644 --- a/Mlem/Views/Shared/Comments/Comment Item Logic.swift +++ b/Mlem/Views/Shared/Comments/Comment Item Logic.swift @@ -232,7 +232,7 @@ extension CommentItem { // report ret.append(MenuFunction.standardMenuFunction( text: "Report", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: "Really report?", enabled: true ) { @@ -245,7 +245,7 @@ extension CommentItem { // block ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { diff --git a/Mlem/Views/Shared/Comments/Comment Item.swift b/Mlem/Views/Shared/Comments/Comment Item.swift index d77daebcc..292efbea1 100644 --- a/Mlem/Views/Shared/Comments/Comment Item.swift +++ b/Mlem/Views/Shared/Comments/Comment Item.swift @@ -187,11 +187,6 @@ struct CommentItem: View { .onTapGesture { toggleCollapsed() } - .contextMenu { - ForEach(genMenuFunctions()) { item in - MenuButton(menuFunction: item, confirmDestructive: confirmDestructive) - } - } .destructiveConfirmation( isPresentingConfirmDestructive: $isPresentingConfirmDestructive, confirmationMenuFunction: confirmationMenuFunction @@ -202,6 +197,11 @@ struct CommentItem: View { trailing: enableSwipeActions ? [saveSwipeAction, replySwipeAction, expandCollapseCommentAction] : [] ) .border(width: borderWidth, edges: [.leading], color: threadingColors[depth % threadingColors.count]) + .contextMenu { + ForEach(genMenuFunctions()) { item in + MenuButton(menuFunction: item, confirmDestructive: confirmDestructive) + } + } } // swiftlint:enable function_body_length } diff --git a/Mlem/Views/Shared/Comments/Components/CommentBodyView.swift b/Mlem/Views/Shared/Comments/Components/CommentBodyView.swift index 99a6aaeac..ffc6593e3 100644 --- a/Mlem/Views/Shared/Comments/Components/CommentBodyView.swift +++ b/Mlem/Views/Shared/Comments/Components/CommentBodyView.swift @@ -113,17 +113,17 @@ struct CommentBodyView: View { // votes if showCommentDownvotesSeparately { HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: myVote == .upvote ? AppConstants.fullUpvoteSymbolName : AppConstants.emptyUpvoteSymbolName) + Image(systemName: myVote == .upvote ? Icons.upvoteSquareFill : Icons.upvoteSquare) Text(String(commentView.counts.upvotes)) } HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: myVote == .downvote ? AppConstants.fullDownvoteSymbolName : AppConstants.emptyDownvoteSymbolName) + Image(systemName: myVote == .downvote ? Icons.downvoteSquareFill : Icons.downvoteSquare) Text(String(commentView.counts.downvotes)) } } else { HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: AppConstants.scoringOpToVoteImage[myVote]!) + Image(systemName: myVote == .resetVote ? Icons.upvoteSquare : myVote.iconName) Text(String(commentView.counts.score)) } .foregroundColor(.secondary) diff --git a/Mlem/Views/Shared/Components/Components/DownvoteButtonView.swift b/Mlem/Views/Shared/Components/Components/DownvoteButtonView.swift index a7395a6af..26366619e 100644 --- a/Mlem/Views/Shared/Components/Components/DownvoteButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/DownvoteButtonView.swift @@ -17,7 +17,7 @@ struct DownvoteButtonView: View { await downvote() } } label: { - Image(systemName: AppConstants.plainDownvoteSymbolName) + Image(systemName: Icons.downvote) .resizable() .scaledToFit() .frame(width: AppConstants.barIconSize, height: AppConstants.barIconSize) diff --git a/Mlem/Views/Shared/Components/Components/DownvoteCounterView.swift b/Mlem/Views/Shared/Components/Components/DownvoteCounterView.swift index 55dec088f..2386664da 100644 --- a/Mlem/Views/Shared/Components/Components/DownvoteCounterView.swift +++ b/Mlem/Views/Shared/Components/Components/DownvoteCounterView.swift @@ -22,7 +22,7 @@ struct DownvoteCounterView: View { } } label: { HStack(spacing: 8) { - Image(systemName: AppConstants.plainDownvoteSymbolName) + Image(systemName: Icons.downvote) Text(String(score)) .monospacedDigit() } diff --git a/Mlem/Views/Shared/Components/Components/Ellipsis Menu.swift b/Mlem/Views/Shared/Components/Components/Ellipsis Menu.swift index bb956640e..37843ac22 100644 --- a/Mlem/Views/Shared/Components/Components/Ellipsis Menu.swift +++ b/Mlem/Views/Shared/Components/Components/Ellipsis Menu.swift @@ -25,7 +25,7 @@ struct EllipsisMenu: View { MenuButton(menuFunction: menuFunction, confirmDestructive: confirmDestructive) } } label: { - Image(systemName: "ellipsis") + Image(systemName: Icons.menu) .frame(width: size, height: size) .foregroundColor(.primary) .background(RoundedRectangle(cornerRadius: AppConstants.tinyItemCornerRadius) diff --git a/Mlem/Views/Shared/Components/Components/InfoStackView.swift b/Mlem/Views/Shared/Components/Components/InfoStackView.swift index 0ba8295fd..8ff52db88 100644 --- a/Mlem/Views/Shared/Components/Components/InfoStackView.swift +++ b/Mlem/Views/Shared/Components/Components/InfoStackView.swift @@ -61,7 +61,7 @@ struct InfoStackView: View { @ViewBuilder func netVotesView(votes: DetailedVotes) -> some View { HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: AppConstants.scoringOpToVoteImage[votes.myVote]!) + Image(systemName: votes.myVote == .resetVote ? Icons.upvoteSquare : votes.myVote.iconNameFill) Text(String(votes.score)) } .accessibilityAddTraits(.isStaticText) @@ -72,7 +72,7 @@ struct InfoStackView: View { @ViewBuilder func upvotesView(votes: DetailedVotes) -> some View { HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: votes.myVote == .upvote ? AppConstants.fullUpvoteSymbolName : AppConstants.emptyUpvoteSymbolName) + Image(systemName: votes.myVote == .upvote ? Icons.upvoteSquareFill : Icons.upvoteSquare) Text(String(votes.upvotes)) } .accessibilityAddTraits(.isStaticText) @@ -84,8 +84,8 @@ struct InfoStackView: View { func downvotesView(votes: DetailedVotes) -> some View { HStack(spacing: AppConstants.iconToTextSpacing) { Image(systemName: votes.myVote == .downvote - ? AppConstants.fullDownvoteSymbolName - : AppConstants.emptyDownvoteSymbolName) + ? Icons.downvoteSquareFill + : Icons.downvoteSquare) Text(String(votes.downvotes)) } .accessibilityAddTraits(.isStaticText) @@ -95,7 +95,7 @@ struct InfoStackView: View { @ViewBuilder func savedView(isSaved: Bool) -> some View { - Image(systemName: isSaved ? AppConstants.fullSaveSymbolName : AppConstants.emptySaveSymbolName) + Image(systemName: isSaved ? Icons.saveFill : Icons.save) .accessibilityAddTraits(.isStaticText) .accessibilityElement(children: .ignore) .accessibilityLabel(isSaved ? "saved" : "") @@ -104,7 +104,7 @@ struct InfoStackView: View { @ViewBuilder func repliesView(numReplies: Int) -> some View { HStack(spacing: AppConstants.iconToTextSpacing) { - Image(systemName: "bubble.right") + Image(systemName: Icons.replies) Text(numReplies.description) } .accessibilityAddTraits(.isStaticText) diff --git a/Mlem/Views/Shared/Components/Components/JumpButtonView.swift b/Mlem/Views/Shared/Components/Components/JumpButtonView.swift index 597a36985..27e38df9c 100644 --- a/Mlem/Views/Shared/Components/Components/JumpButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/JumpButtonView.swift @@ -5,8 +5,8 @@ // Created by Sjmarf on 11/08/2023. // -import SwiftUI import Dependencies +import SwiftUI struct JumpButtonView: View { @State private var pressed: Bool = false @@ -18,7 +18,7 @@ struct JumpButtonView: View { var body: some View { Button {} label: { - Image(systemName: "chevron.down") + Image(systemName: Icons.jumpButton) .fontWeight(.semibold) .foregroundStyle(.secondary) .aspectRatio(contentMode: .fit) @@ -30,7 +30,7 @@ struct JumpButtonView: View { .clipShape(Circle()) ) .padding(10) - .scaleEffect(self.pressed ? 1.2 : 1.0) + .scaleEffect(pressed ? 1.2 : 1.0) .onTapGesture { hapticManager.play(haptic: .gentleInfo, priority: .high) onShortPress() @@ -42,7 +42,7 @@ struct JumpButtonView: View { }, onPressingChanged: { pressing in withAnimation(.interactiveSpring()) { - self.pressed = pressing + pressed = pressing } } ) diff --git a/Mlem/Views/Shared/Components/Components/ReplyButtonView.swift b/Mlem/Views/Shared/Components/Components/ReplyButtonView.swift index f742e3777..5093d98f6 100644 --- a/Mlem/Views/Shared/Components/Components/ReplyButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/ReplyButtonView.swift @@ -17,7 +17,7 @@ struct ReplyButtonView: View { init(accessibilityContext: String, reply: (() -> Void)?) { self.reply = reply - self.replyIcon = reply == nil ? "quote.bubble.left" : AppConstants.emptyReplySymbolName + self.replyIcon = reply == nil ? "quote.bubble.left" : Icons.reply self.replyButtonText = "Reply to \(accessibilityContext)" } diff --git a/Mlem/Views/Shared/Components/Components/SaveButtonView.swift b/Mlem/Views/Shared/Components/Components/SaveButtonView.swift index 0ac4907eb..4001584a4 100644 --- a/Mlem/Views/Shared/Components/Components/SaveButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/SaveButtonView.swift @@ -26,7 +26,7 @@ struct SaveButtonView: View { Button { save() } label: { - Image(systemName: isSaved ? "bookmark.fill" : "bookmark") + Image(systemName: isSaved ? Icons.saveFill : Icons.save) .resizable() .scaledToFit() .frame(width: AppConstants.barIconSize, height: AppConstants.barIconSize) diff --git a/Mlem/Views/Shared/Components/Components/ShareButtonView.swift b/Mlem/Views/Shared/Components/Components/ShareButtonView.swift index 7dabc8eb5..da81df1aa 100644 --- a/Mlem/Views/Shared/Components/Components/ShareButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/ShareButtonView.swift @@ -27,7 +27,7 @@ struct ShareButtonView: View { } var label: some View { - Image(systemName: AppConstants.shareSymbolName) + Image(systemName: Icons.share) .resizable() .scaledToFit() .frame(width: AppConstants.barIconSize, height: AppConstants.barIconSize) diff --git a/Mlem/Views/Shared/Components/Components/UpvoteButtonView.swift b/Mlem/Views/Shared/Components/Components/UpvoteButtonView.swift index ce728350a..93e6bd816 100644 --- a/Mlem/Views/Shared/Components/Components/UpvoteButtonView.swift +++ b/Mlem/Views/Shared/Components/Components/UpvoteButtonView.swift @@ -17,7 +17,7 @@ struct UpvoteButtonView: View { await upvote() } } label: { - Image(systemName: AppConstants.plainUpvoteSymbolName) + Image(systemName: Icons.upvote) .resizable() .scaledToFit() .frame(width: AppConstants.barIconSize, height: AppConstants.barIconSize) diff --git a/Mlem/Views/Shared/Components/Components/UpvoteCounterView.swift b/Mlem/Views/Shared/Components/Components/UpvoteCounterView.swift index c26546079..bb6b640b3 100644 --- a/Mlem/Views/Shared/Components/Components/UpvoteCounterView.swift +++ b/Mlem/Views/Shared/Components/Components/UpvoteCounterView.swift @@ -22,7 +22,7 @@ struct UpvoteCounterView: View { } } label: { HStack(spacing: 8) { - Image(systemName: AppConstants.plainUpvoteSymbolName) + Image(systemName: Icons.upvote) Text(String(score)) .monospacedDigit() } diff --git a/Mlem/Views/Shared/Components/Components/Website Indicator View.swift b/Mlem/Views/Shared/Components/Components/Website Indicator View.swift index 34841595a..b09ec2417 100644 --- a/Mlem/Views/Shared/Components/Components/Website Indicator View.swift +++ b/Mlem/Views/Shared/Components/Components/Website Indicator View.swift @@ -15,7 +15,7 @@ struct WebsiteIndicatorView: View { .clipShape(Circle()) .frame(maxWidth: .infinity, maxHeight: .infinity) .overlay { - Image(systemName: "safari") + Image(systemName: Icons.browser) .resizable() .scaledToFit() .foregroundColor(.white) diff --git a/Mlem/Views/Shared/Components/End Of Feed View.swift b/Mlem/Views/Shared/Components/End Of Feed View.swift index 14480f5e5..300abebe5 100644 --- a/Mlem/Views/Shared/Components/End Of Feed View.swift +++ b/Mlem/Views/Shared/Components/End Of Feed View.swift @@ -17,7 +17,7 @@ struct EndOfFeedView: View { LoadingView(whatIsLoading: .posts) } else { HStack { - Image(systemName: "figure.climbing") + Image(systemName: Icons.endOfFeed) Text("I think I've found the bottom!") } diff --git a/Mlem/Views/Shared/Components/ReadCheck.swift b/Mlem/Views/Shared/Components/ReadCheck.swift index 13a4a06ca..7ba76ca25 100644 --- a/Mlem/Views/Shared/Components/ReadCheck.swift +++ b/Mlem/Views/Shared/Components/ReadCheck.swift @@ -10,7 +10,7 @@ import SwiftUI struct ReadCheck: View { var body: some View { - Image(systemName: "checkmark") + Image(systemName: Icons.success) .resizable() .scaledToFit() .frame(width: 10, height: 10) diff --git a/Mlem/Views/Shared/Components/Thumbnail Image View.swift b/Mlem/Views/Shared/Components/Thumbnail Image View.swift index df4646648..bff5ebc58 100644 --- a/Mlem/Views/Shared/Components/Thumbnail Image View.swift +++ b/Mlem/Views/Shared/Components/Thumbnail Image View.swift @@ -58,9 +58,9 @@ struct ThumbnailImageView: View { .frame(width: size.width, height: size.height, alignment: .topLeading) } case .text: - Image(systemName: "text.book.closed") + Image(systemName: Icons.textPost) case .titleOnly: - Image(systemName: "character.bubble") + Image(systemName: Icons.titleOnlyPost) } } .foregroundColor(.secondary) diff --git a/Mlem/Views/Shared/Composer/PostComposerView.swift b/Mlem/Views/Shared/Composer/PostComposerView.swift index 363f70a04..1ae1f647f 100644 --- a/Mlem/Views/Shared/Composer/PostComposerView.swift +++ b/Mlem/Views/Shared/Composer/PostComposerView.swift @@ -22,6 +22,10 @@ struct PostComposerView: View { @State var postBody: String @State var isNSFW: Bool + private var hasPostContent: Bool { + !postTitle.isEmpty || !postURL.isEmpty || !postBody.isEmpty + } + init(editModel: PostEditorModel) { self.postTracker = editModel.postTracker self.editModel = editModel @@ -67,6 +71,7 @@ struct PostComposerView: View { dismiss() } + .interactiveDismissDisabled(hasPostContent) } } diff --git a/Mlem/Views/Shared/Composer/PostDetailEditorView.swift b/Mlem/Views/Shared/Composer/PostDetailEditorView.swift index 5fa06607b..ae6de5ac8 100644 --- a/Mlem/Views/Shared/Composer/PostDetailEditorView.swift +++ b/Mlem/Views/Shared/Composer/PostDetailEditorView.swift @@ -213,7 +213,7 @@ struct PostDetailEditorView: View { await submitPost() } } label: { - Image(systemName: "paperplane") + Image(systemName: Icons.send) }.disabled(isSubmitting || !isReadyToPost) } } diff --git a/Mlem/Views/Shared/Composer/ResponseEditorView.swift b/Mlem/Views/Shared/Composer/ResponseEditorView.swift index e5e2217ae..0aebea587 100644 --- a/Mlem/Views/Shared/Composer/ResponseEditorView.swift +++ b/Mlem/Views/Shared/Composer/ResponseEditorView.swift @@ -93,13 +93,14 @@ struct ResponseEditorView: View { await submit() } } label: { - Image(systemName: "paperplane") + Image(systemName: Icons.send) }.disabled(isSubmitting || !isReadyToReply) } } .navigationBarColor() .navigationTitle(editorModel.modalName) .navigationBarTitleDisplayMode(.inline) + .interactiveDismissDisabled(isReadyToReply) } @MainActor diff --git a/Mlem/Views/Shared/Links/AvatarView.swift b/Mlem/Views/Shared/Links/AvatarView.swift index eca915d2d..884c7b915 100644 --- a/Mlem/Views/Shared/Links/AvatarView.swift +++ b/Mlem/Views/Shared/Links/AvatarView.swift @@ -78,43 +78,19 @@ struct AvatarView: View { switch type { case .community: return AnyView( - ZStack { - VStack { - Spacer() - Image(systemName: "building.2.fill") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: avatarSize * 0.66) - .foregroundStyle(.white) - } - .scaledToFit() - .mask( - Circle() - .frame(width: avatarSize * 0.83, height: avatarSize * 0.83) - ) - } - .frame(maxWidth: .infinity) - .background(.gray) + Image(systemName: Icons.communityFill) + .resizable() + .scaledToFill() + .background(.white) + .foregroundStyle(Color.gray.gradient) ) case .user: return AnyView( - ZStack { - VStack { - Spacer() - Image(systemName: "person.fill") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: avatarSize * 0.75) - .foregroundStyle(.white) - } - .scaledToFit() - .mask( - Circle() - .frame(width: avatarSize * 0.83, height: avatarSize * 0.83) - ) - } - .frame(maxWidth: .infinity) - .background(.gray) + Image(systemName: Icons.userFill) + .resizable() + .scaledToFill() + .background(.white) + .foregroundStyle(Color.gray.gradient) ) } } diff --git a/Mlem/Views/Shared/Links/User/UserLabelView.swift b/Mlem/Views/Shared/Links/User/UserLabelView.swift index 64736b35b..02524bf39 100644 --- a/Mlem/Views/Shared/Links/User/UserLabelView.swift +++ b/Mlem/Views/Shared/Links/User/UserLabelView.swift @@ -62,11 +62,11 @@ struct UserLabelView: View { static let mlemOfficial = "vlemmy.net/u/MlemOfficial" static let flairMlemOfficial = UserLinkViewFlair(color: Color.purple, image: Image("mlem")) - static let flairDeveloper = UserLinkViewFlair(color: Color.purple, image: Image(systemName: "hammer.fill")) - static let flairMod = UserLinkViewFlair(color: Color.green, image: Image(systemName: "shield.fill")) - static let flairBot = UserLinkViewFlair(color: Color.indigo, image: Image(systemName: "server.rack")) - static let flairOP = UserLinkViewFlair(color: Color.orange, image: Image(systemName: "person.fill")) - static let flairAdmin = UserLinkViewFlair(color: Color.red, image: Image(systemName: "crown.fill")) + static let flairDeveloper = UserLinkViewFlair(color: Color.purple, image: Image(systemName: Icons.developerFlair)) + static let flairMod = UserLinkViewFlair(color: Color.green, image: Image(systemName: Icons.moderationFill)) + static let flairBot = UserLinkViewFlair(color: Color.indigo, image: Image(systemName: Icons.botFlair)) + static let flairOP = UserLinkViewFlair(color: Color.orange, image: Image(systemName: Icons.opFlair)) + static let flairAdmin = UserLinkViewFlair(color: Color.red, image: Image(systemName: Icons.adminFlair)) static let flairRegular = UserLinkViewFlair(color: Color.gray) var body: some View { diff --git a/Mlem/Views/Shared/Markdown View.swift b/Mlem/Views/Shared/Markdown View.swift index 1e0e4dfce..ca988e52c 100644 --- a/Mlem/Views/Shared/Markdown View.swift +++ b/Mlem/Views/Shared/Markdown View.swift @@ -126,7 +126,7 @@ extension Theme { .markdownMargin(top: .em(0.25)) } .taskListMarker { configuration in - Image(systemName: configuration.isCompleted ? "checkmark.square.fill" : "square") + Image(systemName: configuration.isCompleted ? Icons.successSquareFill : Icons.emptySquare) .symbolRenderingMode(.hierarchical) .foregroundStyle(Color.checkbox, Color.checkboxBackground) .imageScale(.small) diff --git a/Mlem/Views/Shared/Posts/Components/Stickied Tag.swift b/Mlem/Views/Shared/Posts/Components/Stickied Tag.swift index 7a7a0b071..4f23f569e 100644 --- a/Mlem/Views/Shared/Posts/Components/Stickied Tag.swift +++ b/Mlem/Views/Shared/Posts/Components/Stickied Tag.swift @@ -23,7 +23,7 @@ struct StickiedTag: View { var body: some View { HStack { - Image(systemName: "pin.fill") + Image(systemName: Icons.pinned) .foregroundColor(calculateColor()) .accessibilityLabel(tagType == .local ? "Post stickied to your local instance" : "Post stickied to your current community") } diff --git a/Mlem/Views/Shared/Posts/Expanded Post.swift b/Mlem/Views/Shared/Posts/Expanded Post.swift index 2526082a9..b828c95ef 100644 --- a/Mlem/Views/Shared/Posts/Expanded Post.swift +++ b/Mlem/Views/Shared/Posts/Expanded Post.swift @@ -258,7 +258,7 @@ struct ExpandedPost: View { } else { VStack(spacing: 2) { VStack(spacing: 5) { - Image(systemName: "binoculars") + Image(systemName: Icons.noContent) Text("No comments to be found") } Text("Why not post the first one?") diff --git a/Mlem/Views/Shared/Posts/ExpandedPostLogic.swift b/Mlem/Views/Shared/Posts/ExpandedPostLogic.swift index 0ea9d4704..8fad29a38 100644 --- a/Mlem/Views/Shared/Posts/ExpandedPostLogic.swift +++ b/Mlem/Views/Shared/Posts/ExpandedPostLogic.swift @@ -184,7 +184,7 @@ extension ExpandedPost { // report ret.append(MenuFunction.standardMenuFunction( text: "Report Post", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: AppConstants.reportPostPrompt, enabled: true ) { @@ -194,7 +194,7 @@ extension ExpandedPost { // block user ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { diff --git a/Mlem/Views/Shared/Posts/Feed Post.swift b/Mlem/Views/Shared/Posts/Feed Post.swift index 1ac3d200f..89d1e6a5e 100644 --- a/Mlem/Views/Shared/Posts/Feed Post.swift +++ b/Mlem/Views/Shared/Posts/Feed Post.swift @@ -382,7 +382,7 @@ struct FeedPost: View { // report ret.append(MenuFunction.standardMenuFunction( text: "Report Post", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: AppConstants.reportPostPrompt, enabled: true ) { @@ -392,7 +392,7 @@ struct FeedPost: View { // block user ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { @@ -404,7 +404,7 @@ struct FeedPost: View { // block community ret.append(MenuFunction.standardMenuFunction( text: "Block Community", - imageName: AppConstants.blockSymbolName, + imageName: Icons.hide, destructiveActionPrompt: nil, enabled: true ) { @@ -427,8 +427,8 @@ extension FeedPost { var upvoteSwipeAction: SwipeAction { let (emptySymbolName, fullSymbolName) = post.votes.myVote == .upvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyUpvoteSymbolName, AppConstants.fullUpvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.upvoteSquare, Icons.upvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .upvoteColor, @@ -440,8 +440,8 @@ extension FeedPost { guard siteInformation.enableDownvotes else { return nil } let (emptySymbolName, fullSymbolName) = post.votes.myVote == .downvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyDownvoteSymbolName, AppConstants.fullDownvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.downvoteSquare, Icons.downvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .downvoteColor, @@ -451,8 +451,8 @@ extension FeedPost { var saveSwipeAction: SwipeAction { let (emptySymbolName, fullSymbolName) = post.saved - ? (AppConstants.emptyUndoSaveSymbolName, AppConstants.fullUndoSaveSymbolName) - : (AppConstants.emptySaveSymbolName, AppConstants.fullSaveSymbolName) + ? (Icons.unsave, Icons.unsaveFill) + : (Icons.save, Icons.saveFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .saveColor, diff --git a/Mlem/Views/Shared/Posts/Post Sizes/Large Post.swift b/Mlem/Views/Shared/Posts/Post Sizes/Large Post.swift index 663eca7d5..d01b07d58 100644 --- a/Mlem/Views/Shared/Posts/Post Sizes/Large Post.swift +++ b/Mlem/Views/Shared/Posts/Post Sizes/Large Post.swift @@ -126,7 +126,7 @@ struct LargePost: View { @ViewBuilder private var minimizedIcon: some View { - Image(systemName: "rectangle.expand.vertical") + Image(systemName: Icons.postSizeSetting) .resizable() .aspectRatio(contentMode: .fit) .padding(6) diff --git a/Mlem/Views/Shared/TimestampView.swift b/Mlem/Views/Shared/TimestampView.swift index cd7a5e7b1..f0db95fe9 100644 --- a/Mlem/Views/Shared/TimestampView.swift +++ b/Mlem/Views/Shared/TimestampView.swift @@ -13,7 +13,7 @@ struct TimestampView: View { var body: some View { HStack(spacing: spacing) { - Image(systemName: "clock") + Image(systemName: Icons.time) Text(getTimeIntervalFromNow(date: date)) } .foregroundColor(.secondary) diff --git a/Mlem/Views/Shared/TokenRefreshView.swift b/Mlem/Views/Shared/TokenRefreshView.swift index 50a2534be..a3c8eb0e5 100644 --- a/Mlem/Views/Shared/TokenRefreshView.swift +++ b/Mlem/Views/Shared/TokenRefreshView.swift @@ -82,7 +82,7 @@ struct TokenRefreshView: View { .controlSize(.large) .frame(height: 60) case .success: - Image(systemName: "checkmark.circle") + Image(systemName: Icons.successCircle) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 60) diff --git a/Mlem/Views/Shared/Website Icon Complex.swift b/Mlem/Views/Shared/Website Icon Complex.swift index bd99e2f35..eb38f3543 100644 --- a/Mlem/Views/Shared/Website Icon Complex.swift +++ b/Mlem/Views/Shared/Website Icon Complex.swift @@ -63,7 +63,7 @@ struct WebsiteIconComplex: View { var imageHeight: CGFloat { horizontalSizeClass == .regular ? 400 : screenWidth * 0.66 } var body: some View { - VStack(spacing: 0) { + LazyVStack(spacing: 0) { if shouldShowWebsitePreviews, let thumbnailURL = post.thumbnailImageUrl { CachedImage( url: thumbnailURL, @@ -85,7 +85,7 @@ struct WebsiteIconComplex: View { url: faviconURL, shouldExpand: false, fixedSize: CGSize(width: AppConstants.smallAvatarSize, height: AppConstants.smallAvatarSize), - imageNotFound: { AnyView(Image(systemName: "globe")) } + imageNotFound: { AnyView(Image(systemName: Icons.websiteIcon)) } ) } diff --git a/Mlem/Views/Tabs/Feeds/Community List/Community List View.swift b/Mlem/Views/Tabs/Feeds/Community List/Community List View.swift index d04398d7f..287542bcb 100644 --- a/Mlem/Views/Tabs/Feeds/Community List/Community List View.swift +++ b/Mlem/Views/Tabs/Feeds/Community List/Community List View.swift @@ -17,15 +17,14 @@ struct CommunitySection: Identifiable { } struct CommunityListView: View { - @StateObject private var model: CommunityListModel = .init() @Binding var selectedCommunity: CommunityLinkWithContext? - + init(selectedCommunity: Binding) { self._selectedCommunity = selectedCommunity } - + // MARK: - Body var body: some View { @@ -34,7 +33,7 @@ struct CommunityListView: View { List(selection: $selectedCommunity) { HomepageFeedRowView( feedType: .subscribed, - iconName: AppConstants.subscribedFeedSymbolNameFill, + iconName: Icons.subscribedFeedFill, iconColor: .red, description: "Subscribed communities from all servers", navigationContext: .sidebar @@ -42,38 +41,38 @@ struct CommunityListView: View { .id("top") // For "scroll to top" sidebar item HomepageFeedRowView( feedType: .local, - iconName: AppConstants.localFeedSymbolNameFill, + iconName: Icons.localFeedFill, iconColor: .green, description: "Local communities from your server", navigationContext: .sidebar ) HomepageFeedRowView( feedType: .all, - iconName: AppConstants.federatedFeedSymbolNameFill, + iconName: Icons.federatedFeedFill, iconColor: .blue, description: "All communities that federate with your server", navigationContext: .sidebar ) - ForEach(model.visibleSections) { section in - Section(header: headerView(for: section)) { - ForEach(model.communities(for: section)) { community in - CommuntiyFeedRowView( - community: community, - subscribed: model.isSubscribed(to: community), - communitySubscriptionChanged: model.updateSubscriptionStatus, - navigationContext: .sidebar - ) - } + ForEach(model.visibleSections) { section in + Section(header: headerView(for: section)) { + ForEach(model.communities(for: section)) { community in + CommuntiyFeedRowView( + community: community, + subscribed: model.isSubscribed(to: community), + communitySubscriptionChanged: model.updateSubscriptionStatus, + navigationContext: .sidebar + ) } } } + } .fancyTabScrollCompatible() .navigationTitle("Communities") .navigationBarColor() .listStyle(PlainListStyle()) .scrollIndicators(.hidden) - + SectionIndexTitles(proxy: scrollProxy, communitySections: model.allSections()) } } diff --git a/Mlem/Views/Tabs/Feeds/Community List/Components/CommunityListRowViews.swift b/Mlem/Views/Tabs/Feeds/Community List/Components/CommunityListRowViews.swift index 5f1b76f53..8717a4064 100644 --- a/Mlem/Views/Tabs/Feeds/Community List/Components/CommunityListRowViews.swift +++ b/Mlem/Views/Tabs/Feeds/Community List/Components/CommunityListRowViews.swift @@ -12,7 +12,7 @@ struct FavoriteStarButtonStyle: ButtonStyle { let isFavorited: Bool func makeBody(configuration: Configuration) -> some View { - Image(systemName: isFavorited ? "star.fill" : "star") + Image(systemName: isFavorited ? Icons.favoriteFill : Icons.favorite) .foregroundColor(.blue) .opacity(isFavorited ? 1.0 : 0.2) .accessibilityRepresentation { configuration.label } @@ -20,7 +20,6 @@ struct FavoriteStarButtonStyle: ButtonStyle { } struct CommuntiyFeedRowView: View { - @Dependency(\.favoriteCommunitiesTracker) var favoriteCommunitiesTracker @Dependency(\.hapticManager) var hapticManager @Dependency(\.notifier) var notifier diff --git a/Mlem/Views/Tabs/Feeds/Components/PostSortMenu.swift b/Mlem/Views/Tabs/Feeds/Components/PostSortMenu.swift index b77e2cf17..2addb68f8 100644 --- a/Mlem/Views/Tabs/Feeds/Components/PostSortMenu.swift +++ b/Mlem/Views/Tabs/Feeds/Components/PostSortMenu.swift @@ -33,7 +33,7 @@ struct PostSortMenu: View { } } label: { - Label("Top…", systemImage: "text.line.first.and.arrowtriangle.forward") + Label("Top…", systemImage: Icons.topSortMenu) } } label: { if shortLabel { diff --git a/Mlem/Views/Tabs/Feeds/Components/Sidebar Header Avatar.swift b/Mlem/Views/Tabs/Feeds/Components/Sidebar Header Avatar.swift index ff023a128..6f45485ca 100644 --- a/Mlem/Views/Tabs/Feeds/Components/Sidebar Header Avatar.swift +++ b/Mlem/Views/Tabs/Feeds/Components/Sidebar Header Avatar.swift @@ -37,11 +37,10 @@ struct CommunitySidebarHeaderAvatar: View { VStack(alignment: .center) { Spacer() .frame(height: 20) - Image(systemName: "person.fill") + Image(systemName: Icons.user) .font(.system(size: AppConstants.hugeAvatarSize)) // SF Symbols are apparently font .foregroundColor(.secondary) } } - } } diff --git a/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift b/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift index 9c97a51f2..2908f5dfc 100644 --- a/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift +++ b/Mlem/Views/Tabs/Feeds/Components/Sidebar View.swift @@ -113,7 +113,7 @@ struct CommunitySidebarView: View { @ViewBuilder func errorView(errorDetails: String) -> some View { VStack(spacing: 10) { - Image(systemName: "exclamationmark.bubble") + Image(systemName: Icons.warning) .font(.title) Text("Community details loading failed!") diff --git a/Mlem/Views/Tabs/Feeds/Feed View Logic.swift b/Mlem/Views/Tabs/Feeds/Feed View Logic.swift index c644ee725..03615f274 100644 --- a/Mlem/Views/Tabs/Feeds/Feed View Logic.swift +++ b/Mlem/Views/Tabs/Feeds/Feed View Logic.swift @@ -107,7 +107,7 @@ extension FeedView { let isSelected = postSortType == type return MenuFunction.standardMenuFunction( text: type.description, - imageName: isSelected ? AppConstants.timeSymbolNameFill : AppConstants.timeSymbolName, + imageName: isSelected ? Icons.timeSortFill : Icons.timeSort, destructiveActionPrompt: nil, enabled: !isSelected ) { @@ -122,7 +122,7 @@ extension FeedView { let blurNsfwText = shouldBlurNsfw ? "Unblur NSFW" : "Blur NSFW" ret.append(MenuFunction.standardMenuFunction( text: blurNsfwText, - imageName: AppConstants.blurNsfwSymbolName, + imageName: Icons.blurNsfw, destructiveActionPrompt: nil, enabled: true ) { @@ -148,7 +148,7 @@ extension FeedView { // new post ret.append(MenuFunction.standardMenuFunction( text: "New Post", - imageName: AppConstants.sendSymbolNameFill, + imageName: Icons.sendFill, destructiveActionPrompt: nil, enabled: true ) { @@ -162,8 +162,8 @@ extension FeedView { if let communityDetails { let isSubscribed: Bool = communityDetails.communityView.subscribed.rawValue == "Subscribed" let (subscribeText, subscribeSymbol, subscribePrompt) = isSubscribed - ? ("Unsubscribe", AppConstants.unsubscribeSymbolName, "Really unsubscribe from \(community.name)?") - : ("Subscribe", AppConstants.subscribeSymbolName, nil) + ? ("Unsubscribe", Icons.unsubscribe, "Really unsubscribe from \(community.name)?") + : ("Subscribe", Icons.subscribe, nil) ret.append(MenuFunction.standardMenuFunction( text: subscribeText, imageName: subscribeSymbol, @@ -210,8 +210,8 @@ extension FeedView { if let communityDetails { // block let (blockText, blockSymbol, blockPrompt) = communityDetails.communityView.blocked - ? ("Unblock", AppConstants.unblockSymbolName, nil) - : ("Block", AppConstants.blockSymbolName, "Really block \(community.name)?") + ? ("Unblock", Icons.show, nil) + : ("Block", Icons.hide, "Really block \(community.name)?") ret.append(MenuFunction.standardMenuFunction( text: blockText, imageName: blockSymbol, diff --git a/Mlem/Views/Tabs/Feeds/Feed View.swift b/Mlem/Views/Tabs/Feeds/Feed View.swift index 806121ccd..9707b8f83 100644 --- a/Mlem/Views/Tabs/Feeds/Feed View.swift +++ b/Mlem/Views/Tabs/Feeds/Feed View.swift @@ -150,7 +150,7 @@ struct FeedView: View { LoadingView(whatIsLoading: .posts) } else { VStack(alignment: .center, spacing: 5) { - Image(systemName: "text.bubble") + Image(systemName: Icons.noPosts) Text("No posts to be found") } .padding() @@ -213,7 +213,7 @@ struct FeedView: View { MenuButton(menuFunction: menuFunction, confirmDestructive: confirmDestructive) } } label: { - Label("Post Size", systemImage: AppConstants.postSizeSettingsSymbolName) + Label("Post Size", systemImage: Icons.postSizeSetting) } } label: { Label("More", systemImage: "ellipsis") @@ -238,7 +238,7 @@ struct FeedView: View { MenuButton(menuFunction: menuFunction, confirmDestructive: nil) // no destructive sorts } } label: { - Label("Top...", systemImage: AppConstants.topSymbolName) + Label("Top...", systemImage: Icons.topSort) } } label: { Label( @@ -269,7 +269,7 @@ struct FeedView: View { HStack(alignment: .center, spacing: 0) { Text(feedType.label) .font(.headline) - Image(systemName: "chevron.down") + Image(systemName: Icons.dropdown) .scaleEffect(0.7) } .foregroundColor(.primary) diff --git a/Mlem/Views/Tabs/Inbox/Feed/All Items Feed View.swift b/Mlem/Views/Tabs/Inbox/Feed/All Items Feed View.swift index 5c591b741..b8f799822 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/All Items Feed View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/All Items Feed View.swift @@ -27,7 +27,7 @@ extension InboxView { @ViewBuilder func noItemsView() -> some View { VStack(alignment: .center, spacing: 5) { - Image(systemName: "text.bubble") + Image(systemName: Icons.noPosts) Text("No items to be found") } diff --git a/Mlem/Views/Tabs/Inbox/Feed/InteractionSwipeAndMenuHelpers.swift b/Mlem/Views/Tabs/Inbox/Feed/InteractionSwipeAndMenuHelpers.swift index 450c057c6..f2a12d722 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/InteractionSwipeAndMenuHelpers.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/InteractionSwipeAndMenuHelpers.swift @@ -12,8 +12,8 @@ extension InboxView { func upvoteCommentReplySwipeAction(commentReply: APICommentReplyView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = commentReply.myVote == .upvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyUpvoteSymbolName, AppConstants.fullUpvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.upvoteSquare, Icons.upvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .upvoteColor @@ -24,8 +24,8 @@ extension InboxView { func downvoteCommentReplySwipeAction(commentReply: APICommentReplyView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = commentReply.myVote == .downvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyDownvoteSymbolName, AppConstants.fullDownvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.downvoteSquare, Icons.downvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .downvoteColor @@ -36,8 +36,8 @@ extension InboxView { func toggleCommentReplyReadSwipeAction(commentReply: APICommentReplyView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = commentReply.commentReply.read ? - (AppConstants.emptyMarkUnreadSymbolName, AppConstants.fullMarkUnreadSymbolName) : - (AppConstants.emptyMarkReadSymbolName, AppConstants.fullMarkReadSymbolName) + (Icons.markUnread, Icons.markUnreadFill) : + (Icons.markRead, Icons.markReadFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .purple @@ -48,7 +48,7 @@ extension InboxView { func replyToCommentReplySwipeAction(commentReply: APICommentReplyView) -> SwipeAction? { SwipeAction( - symbol: .init(emptyName: AppConstants.emptyReplySymbolName, fillName: AppConstants.fullReplySymbolName), + symbol: .init(emptyName: Icons.reply, fillName: Icons.replyFill), color: .accentColor ) { replyToCommentReply(commentReply: commentReply) @@ -106,7 +106,7 @@ extension InboxView { // report ret.append(MenuFunction.standardMenuFunction( text: "Report Comment", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: nil, enabled: true ) { @@ -116,7 +116,7 @@ extension InboxView { // block ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { @@ -134,8 +134,8 @@ extension InboxView { func upvoteMentionSwipeAction(mentionView: APIPersonMentionView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = mentionView.myVote == .upvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyUpvoteSymbolName, AppConstants.fullUpvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.upvoteSquare, Icons.upvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .upvoteColor @@ -146,8 +146,8 @@ extension InboxView { func downvoteMentionSwipeAction(mentionView: APIPersonMentionView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = mentionView.myVote == .downvote ? - (AppConstants.emptyResetVoteSymbolName, AppConstants.fullResetVoteSymbolName) : - (AppConstants.emptyDownvoteSymbolName, AppConstants.fullDownvoteSymbolName) + (Icons.resetVoteSquare, Icons.resetVoteSquareFill) : + (Icons.downvoteSquare, Icons.downvoteSquareFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .downvoteColor @@ -158,8 +158,8 @@ extension InboxView { func toggleMentionReadSwipeAction(mentionView: APIPersonMentionView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = mentionView.personMention.read ? - (AppConstants.emptyMarkUnreadSymbolName, AppConstants.fullMarkUnreadSymbolName) : - (AppConstants.emptyMarkReadSymbolName, AppConstants.fullMarkReadSymbolName) + (Icons.markUnread, Icons.markUnreadFill) : + (Icons.markRead, Icons.markReadFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .purple @@ -170,7 +170,7 @@ extension InboxView { func replyToMentionSwipeAction(mentionView: APIPersonMentionView) -> SwipeAction? { SwipeAction( - symbol: .init(emptyName: AppConstants.emptyReplySymbolName, fillName: AppConstants.fullReplySymbolName), + symbol: .init(emptyName: Icons.reply, fillName: Icons.replyFill), color: .accentColor ) { replyToMention(mention: mentionView) @@ -228,7 +228,7 @@ extension InboxView { // report ret.append(MenuFunction.standardMenuFunction( text: "Report Comment", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: nil, enabled: true ) { @@ -238,7 +238,7 @@ extension InboxView { // block ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { @@ -256,8 +256,8 @@ extension InboxView { func toggleMessageReadSwipeAction(message: APIPrivateMessageView) -> SwipeAction { let (emptySymbolName, fullSymbolName) = message.privateMessage.read ? - (AppConstants.emptyMarkUnreadSymbolName, AppConstants.fullMarkUnreadSymbolName) : - (AppConstants.emptyMarkReadSymbolName, AppConstants.fullMarkReadSymbolName) + (Icons.markUnread, Icons.markUnreadFill) : + (Icons.markRead, Icons.markReadFill) return SwipeAction( symbol: .init(emptyName: emptySymbolName, fillName: fullSymbolName), color: .purple @@ -268,7 +268,7 @@ extension InboxView { func replyToMessageSwipeAction(message: APIPrivateMessageView) -> SwipeAction { SwipeAction( - symbol: .init(emptyName: AppConstants.emptyReplySymbolName, fillName: AppConstants.fullReplySymbolName), + symbol: .init(emptyName: Icons.reply, fillName: Icons.replyFill), color: .accentColor ) { replyToMessage(message: message) @@ -304,7 +304,7 @@ extension InboxView { // report ret.append(MenuFunction.standardMenuFunction( text: "Report Message", - imageName: AppConstants.reportSymbolName, + imageName: Icons.moderationReport, destructiveActionPrompt: nil, enabled: true ) { @@ -314,7 +314,7 @@ extension InboxView { // block ret.append(MenuFunction.standardMenuFunction( text: "Block User", - imageName: AppConstants.blockUserSymbolName, + imageName: Icons.userBlock, destructiveActionPrompt: AppConstants.blockUserPrompt, enabled: true ) { diff --git a/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Mention View.swift b/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Mention View.swift index dc0a8cb0c..176712775 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Mention View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Mention View.swift @@ -25,13 +25,13 @@ struct InboxMentionView: View { switch mention.myVote { case .upvote: - self.voteIconName = AppConstants.plainUpvoteSymbolName + self.voteIconName = Icons.upvote self.voteColor = .upvoteColor case .downvote: - self.voteIconName = AppConstants.plainDownvoteSymbolName + self.voteIconName = Icons.downvote self.voteColor = .downvoteColor default: - self.voteIconName = AppConstants.plainUpvoteSymbolName + self.voteIconName = Icons.upvote self.voteColor = .secondary } } diff --git a/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Reply View.swift b/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Reply View.swift index 56a50d19d..d1bbe1f5d 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Reply View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/Item Types/Inbox Reply View.swift @@ -27,13 +27,13 @@ struct InboxReplyView: View { switch reply.myVote { case .upvote: - self.voteIconName = AppConstants.plainUpvoteSymbolName + self.voteIconName = Icons.upvote self.voteColor = .upvoteColor case .downvote: - self.voteIconName = AppConstants.plainDownvoteSymbolName + self.voteIconName = Icons.downvote self.voteColor = .downvoteColor default: - self.voteIconName = AppConstants.plainUpvoteSymbolName + self.voteIconName = Icons.upvote self.voteColor = .secondary } } diff --git a/Mlem/Views/Tabs/Inbox/Feed/Mentions Feed View.swift b/Mlem/Views/Tabs/Inbox/Feed/Mentions Feed View.swift index bab253911..aa09e84ac 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/Mentions Feed View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/Mentions Feed View.swift @@ -32,7 +32,7 @@ extension InboxView { @ViewBuilder func noMentionsView() -> some View { VStack(alignment: .center, spacing: 5) { - Image(systemName: "text.bubble") + Image(systemName: Icons.noPosts) Text("No mentions to be found") } diff --git a/Mlem/Views/Tabs/Inbox/Feed/Messages Feed View.swift b/Mlem/Views/Tabs/Inbox/Feed/Messages Feed View.swift index 8438c1f4a..a474a78b5 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/Messages Feed View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/Messages Feed View.swift @@ -32,7 +32,7 @@ extension InboxView { @ViewBuilder func noMessagesView() -> some View { VStack(alignment: .center, spacing: 5) { - Image(systemName: "text.bubble") + Image(systemName: Icons.noPosts) Text("No messages to be found") } diff --git a/Mlem/Views/Tabs/Inbox/Feed/Replies Feed View.swift b/Mlem/Views/Tabs/Inbox/Feed/Replies Feed View.swift index 6f2ac5a3a..8d7c91363 100644 --- a/Mlem/Views/Tabs/Inbox/Feed/Replies Feed View.swift +++ b/Mlem/Views/Tabs/Inbox/Feed/Replies Feed View.swift @@ -36,7 +36,7 @@ extension InboxView { @ViewBuilder func noRepliesView() -> some View { VStack(alignment: .center, spacing: 5) { - Image(systemName: "text.bubble") + Image(systemName: Icons.noPosts) Text("No replies to be found") } diff --git a/Mlem/Views/Tabs/Inbox/Inbox View Logic.swift b/Mlem/Views/Tabs/Inbox/Inbox View Logic.swift index f97ab1f9e..841fbb95d 100644 --- a/Mlem/Views/Tabs/Inbox/Inbox View Logic.swift +++ b/Mlem/Views/Tabs/Inbox/Inbox View Logic.swift @@ -349,8 +349,8 @@ extension InboxView { var ret: [MenuFunction] = .init() let (filterReadText, filterReadSymbol) = shouldFilterRead - ? ("Show All", AppConstants.filterSymbolNameFill) - : ("Show Only Unread", AppConstants.filterSymbolName) + ? ("Show All", Icons.filterFill) + : ("Show Only Unread", Icons.filter) ret.append(MenuFunction.standardMenuFunction( text: filterReadText, diff --git a/Mlem/Views/Tabs/Inbox/Inbox View.swift b/Mlem/Views/Tabs/Inbox/Inbox View.swift index 0adae107f..13ffd2690 100644 --- a/Mlem/Views/Tabs/Inbox/Inbox View.swift +++ b/Mlem/Views/Tabs/Inbox/Inbox View.swift @@ -161,7 +161,7 @@ struct InboxView: View { @ViewBuilder func errorView() -> some View { VStack(spacing: 10) { - Image(systemName: "exclamationmark.bubble") + Image(systemName: Icons.noPosts) .font(.title) Text("Inbox loading failed!") diff --git a/Mlem/Views/Tabs/Profile/User View.swift b/Mlem/Views/Tabs/Profile/User View.swift index 9417fa337..05c68addf 100644 --- a/Mlem/Views/Tabs/Profile/User View.swift +++ b/Mlem/Views/Tabs/Profile/User View.swift @@ -79,7 +79,7 @@ struct UserView: View { private var moderatorButton: some View { if let user = userDetails, !moderatedCommunities.isEmpty { NavigationLink(.userModeratorLink(.init(user: user, moderatedCommunities: moderatedCommunities))) { - Image(systemName: "shield") + Image(systemName: Icons.moderation) } } } @@ -90,7 +90,7 @@ struct UserView: View { Button { isPresentingAccountSwitcher = true } label: { - Image(systemName: AppConstants.switchUserSymbolName) + Image(systemName: Icons.switchUser) } } } diff --git a/Mlem/Views/Tabs/Settings/Components/Settings Item.swift b/Mlem/Views/Tabs/Settings/Components/Settings Item.swift index 2bfe828b0..3041609de 100644 --- a/Mlem/Views/Tabs/Settings/Components/Settings Item.swift +++ b/Mlem/Views/Tabs/Settings/Components/Settings Item.swift @@ -73,14 +73,26 @@ struct SelectableSettingsItem: View { struct SquircleLabelStyle: LabelStyle { var color: Color - var fontSize: CGFloat = 17 + var fontSize: CGFloat = 14 + let ios16FontSize: CGFloat = 17 + + /** + This computed property handles the fact that on iOS 17, the font size of 17 makes the icons too large. The code will not compile if fontSize itself is either absent or computed, so we define it above, compute an adjusted value here, and use the computed value. + */ + var adjustedFontSize: CGFloat { + if #available(iOS 17.0, *) { + return fontSize + } else { + return ios16FontSize + } + } func makeBody(configuration: Configuration) -> some View { Label { configuration.title } icon: { configuration.icon - .font(.system(size: fontSize)) + .font(.system(size: adjustedFontSize)) .foregroundColor(.white) .frame(width: AppConstants.settingsIconSize, height: AppConstants.settingsIconSize) .background(color) @@ -113,7 +125,7 @@ struct SettingsPickerButton: View { label Spacer() if isOn { - Image(systemName: "checkmark") + Image(systemName: Icons.success) .foregroundStyle(.blue) .transition(.opacity) } diff --git a/Mlem/Views/Tabs/Settings/Components/Views/About/ContributorsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/About/ContributorsView.swift index 0c068d97b..2ac2bbd9e 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/About/ContributorsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/About/ContributorsView.swift @@ -23,7 +23,7 @@ struct DeveloperView: View { .foregroundColor(.secondary) } Spacer() - Image(systemName: "chevron.right") + Image(systemName: Icons.forward) } } .tint(color) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/About/LicensesView.swift b/Mlem/Views/Tabs/Settings/Components/Views/About/LicensesView.swift index 9d1d33074..589a90c4d 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/About/LicensesView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/About/LicensesView.swift @@ -22,7 +22,7 @@ struct LicensesView: View { .font(.subheadline) } Spacer() - Image(systemName: "chevron.right") + Image(systemName: Icons.forward) } } } diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Accessibility/AccessibilitySettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Accessibility/AccessibilitySettingsView.swift index aa2982afd..8fccbd3e2 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Accessibility/AccessibilitySettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Accessibility/AccessibilitySettingsView.swift @@ -20,7 +20,7 @@ struct AccessibilitySettingsView: View { List { Section { SelectableSettingsItem( - settingIconSystemName: "book", + settingIconSystemName: Icons.readIndicatorSetting, settingName: "Read Post Indicator", currentValue: $readMarkStyle, options: ReadMarkStyle.allCases @@ -32,7 +32,7 @@ struct AccessibilitySettingsView: View { Text("Bar Thickness") } icon: { if showSettingsIcons { - Image(systemName: "rectangle.leftthird.inset.filled") + Image(systemName: Icons.readIndicatorBarSetting) .foregroundColor(.pink) .opacity(readMarkStyle == .bar ? 1 : 0.4) } @@ -71,7 +71,7 @@ struct AccessibilitySettingsView: View { Section { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.transparencySymbolName, + settingPictureSystemName: Icons.transparency, settingName: "Translucent Insets", isTicked: $hasTranslucentInsets ) @@ -81,7 +81,7 @@ struct AccessibilitySettingsView: View { Section { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.iconSymbolName, + settingPictureSystemName: Icons.icon, settingName: "Show Settings Icons", isTicked: $showSettingsIcons ) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Advanced/AdvancedSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Advanced/AdvancedSettingsView.swift index 95a692459..037ec44ab 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Advanced/AdvancedSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Advanced/AdvancedSettingsView.swift @@ -17,7 +17,7 @@ struct AdvancedSettingsView: View { List { Section { SwitchableSettingsItem( - settingPictureSystemName: "wrench.adjustable.fill", + settingPictureSystemName: Icons.developerMode, settingName: "Developer Mode", isTicked: $developerMode ) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/AppearanceSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/AppearanceSettingsView.swift index 9bd166de1..27619eac2 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/AppearanceSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/AppearanceSettingsView.swift @@ -26,7 +26,7 @@ struct AppearanceSettingsView: View { } } #if !os(macOS) && !targetEnvironment(macCatalyst) - NavigationLink(value: SettingsRoute.appearancePage(.appIcon)) { + NavigationLink(value: SettingsRoute.appearancePage(.appIcon)) { Label { Text("App Icon") } icon: { @@ -35,6 +35,10 @@ struct AppearanceSettingsView: View { .scaledToFit() .frame(width: AppConstants.settingsIconSize, height: AppConstants.settingsIconSize) .cornerRadius(AppConstants.smallItemCornerRadius) + .overlay { + RoundedRectangle(cornerRadius: AppConstants.smallItemCornerRadius) + .stroke(Color(.secondarySystemBackground), lineWidth: 1) + } } } #endif diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Comment/CommentSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Comment/CommentSettingsView.swift index 734fe38ea..b5b3fa53a 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Comment/CommentSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Comment/CommentSettingsView.swift @@ -44,7 +44,7 @@ struct CommentSettingsView: View { Form { Section { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.compactSymbolName, + settingPictureSystemName: Icons.compactPost, settingName: "Compact Comments", isTicked: $compactComments ) @@ -54,7 +54,7 @@ struct CommentSettingsView: View { Text("Customize Widgets") } icon: { if showSettingsIcons { - Image(systemName: "wand.and.stars") + Image(systemName: Icons.widgetWizard) .foregroundColor(.pink) } } @@ -65,32 +65,32 @@ struct CommentSettingsView: View { Section("Interactions and Info") { SwitchableSettingsItem( - settingPictureSystemName: "server.rack", + settingPictureSystemName: Icons.instance, settingName: "Show User Server Instance", isTicked: $shouldShowUserServerInComment ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.emptyUpvoteSymbolName, + settingPictureSystemName: Icons.upvoteSquare, settingName: "Show Score In Info", isTicked: $shouldShowScoreInCommentBar ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.generalVoteSymbolName, + settingPictureSystemName: Icons.votes, settingName: "Show Downvotes Separately", isTicked: $showCommentDownvotesSeparately ) SwitchableSettingsItem( - settingPictureSystemName: "clock", + settingPictureSystemName: Icons.time, settingName: "Show Time Posted In Info", isTicked: $shouldShowTimeInCommentBar ) SwitchableSettingsItem( - settingPictureSystemName: "bookmark", + settingPictureSystemName: Icons.save, settingName: "Show Saved Status In Info", isTicked: $shouldShowSavedInCommentBar ) SwitchableSettingsItem( - settingPictureSystemName: "bubble.right", + settingPictureSystemName: Icons.replies, settingName: "Show Replies In Info", isTicked: $shouldShowRepliesInCommentBar ) @@ -98,12 +98,12 @@ struct CommentSettingsView: View { Section { SwitchableSettingsItem( - settingPictureSystemName: "chevron.down.circle", + settingPictureSystemName: Icons.jumpButtonCircle, settingName: "Show Jump Button", isTicked: $showCommentJumpButton ) SelectableSettingsItem( - settingIconSystemName: "arrow.left.arrow.right", + settingIconSystemName: Icons.leftRight, settingName: "Side", currentValue: $commentJumpButtonSide, options: JumpButtonLocation.allCases diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Community/CommunitySettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Community/CommunitySettingsView.swift index a424a640e..0a59e4b4a 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Community/CommunitySettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Community/CommunitySettingsView.swift @@ -12,12 +12,12 @@ struct CommunitySettingsView: View { var body: some View { Form { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.communitySymbolName, + settingPictureSystemName: Icons.community, settingName: "Show Avatars", isTicked: $shouldShowCommunityIcons ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.bannerSymbolName, + settingPictureSystemName: Icons.banner, settingName: "Show Banners", isTicked: $shouldShowCommunityHeaders ) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift index f50db834e..2bc354a06 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Post/PostSettingsView.swift @@ -43,7 +43,7 @@ struct PostSettingsView: View { Form { Section { SelectableSettingsItem( - settingIconSystemName: "rectangle.compress.vertical", + settingIconSystemName: Icons.postSizeSetting, settingName: "Post Size", currentValue: $postSize, options: PostSize.allCases @@ -54,7 +54,7 @@ struct PostSettingsView: View { Text("Customize Widgets") } icon: { if showSettingsIcons { - Image(systemName: "wand.and.stars") + Image(systemName: Icons.widgetWizard) .foregroundColor(.pink) } } @@ -66,36 +66,36 @@ struct PostSettingsView: View { Section("Body") { SwitchableSettingsItem( - settingPictureSystemName: "photo", + settingPictureSystemName: Icons.thumbnail, settingName: "Thumbnails On Right", isTicked: $shouldShowThumbnailsOnRight ) SwitchableSettingsItem( - settingPictureSystemName: "server.rack", + settingPictureSystemName: Icons.instance, settingName: "Show User Server Instance", isTicked: $shouldShowUserServerInPost ) SwitchableSettingsItem( - settingPictureSystemName: "server.rack", + settingPictureSystemName: Icons.instance, settingName: "Show Community Server Instance", isTicked: $shouldShowCommunityServerInPost ) SwitchableSettingsItem( - settingPictureSystemName: "signature", + settingPictureSystemName: Icons.author, settingName: "Show Post Creator", isTicked: $shouldShowPostCreator ) SwitchableSettingsItem( - settingPictureSystemName: "photo", + settingPictureSystemName: Icons.thumbnail, settingName: "Show Post Thumbnails", isTicked: $shouldShowPostThumbnails ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.limitImageHeightInFeedSymbolName, + settingPictureSystemName: Icons.limitImageHeightSetting, settingName: "Limit Image Height In Feed", isTicked: $limitImageHeightInFeed ) @@ -103,27 +103,27 @@ struct PostSettingsView: View { Section("Interactions and Info") { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.emptyUpvoteSymbolName, + settingPictureSystemName: Icons.upvoteSquare, settingName: "Show Score In Info", isTicked: $shouldShowScoreInPostBar ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.generalVoteSymbolName, + settingPictureSystemName: Icons.votes, settingName: "Show Downvotes Separately", isTicked: $showDownvotesSeparately ) SwitchableSettingsItem( - settingPictureSystemName: "clock", + settingPictureSystemName: Icons.time, settingName: "Show Time Posted In Info", isTicked: $shouldShowTimeInPostBar ) SwitchableSettingsItem( - settingPictureSystemName: "bookmark", + settingPictureSystemName: Icons.save, settingName: "Show Saved Status In Info", isTicked: $shouldShowSavedInPostBar ) SwitchableSettingsItem( - settingPictureSystemName: "bubble.right", + settingPictureSystemName: Icons.replies, settingName: "Show Replies In Info", isTicked: $shouldShowRepliesInPostBar ) @@ -159,18 +159,18 @@ struct PostSettingsView: View { .padding(.horizontal) SwitchableSettingsItem( - settingPictureSystemName: "link", + settingPictureSystemName: Icons.websiteAddress, settingName: "Show Website Address", isTicked: $shouldShowWebsiteHost ) SwitchableSettingsItem( - settingPictureSystemName: "globe", + settingPictureSystemName: Icons.websiteIcon, settingName: "Show Website Icon", isTicked: $shouldShowWebsiteIcon ) .disabled(!shouldShowWebsiteHost) SwitchableSettingsItem( - settingPictureSystemName: "photo.circle.fill", + settingPictureSystemName: Icons.thumbnail, settingName: "Show Website Preview", isTicked: $shouldShowWebsitePreviews ) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Shared/LayoutWidgetView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Shared/LayoutWidgetView.swift index e5773731c..274502f10 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Shared/LayoutWidgetView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/Shared/LayoutWidgetView.swift @@ -24,25 +24,25 @@ struct LayoutWidgetView: View { HStack(spacing: 12) { switch widget.type { case .upvote: - icon(AppConstants.plainUpvoteSymbolName) + icon(Icons.upvote) case .downvote: - icon(AppConstants.plainDownvoteSymbolName) + icon(Icons.downvote) case .save: - icon(AppConstants.emptySaveSymbolName) + icon(Icons.save) case .reply: - icon(AppConstants.emptyReplySymbolName) + icon(Icons.reply) case .share: - icon(AppConstants.shareSymbolName) + icon(Icons.share) case .upvoteCounter: - icon(AppConstants.plainUpvoteSymbolName) + icon(Icons.upvote) Text("9") case .downvoteCounter: - icon(AppConstants.plainDownvoteSymbolName) + icon(Icons.downvote) Text("2") case .scoreCounter: - icon(AppConstants.plainUpvoteSymbolName) + icon(Icons.upvote) Text("7") - icon(AppConstants.plainDownvoteSymbolName) + icon(Icons.downvote) default: EmptyView() } diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/TabBar/TabBarSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/TabBar/TabBarSettingsView.swift index 87a9cccdd..00c9dc72d 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/TabBar/TabBarSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/TabBar/TabBarSettingsView.swift @@ -24,7 +24,7 @@ struct TabBarSettingsView: View { // present once guest mode is fully implemented Section { SelectableSettingsItem( - settingIconSystemName: "person.text.rectangle", + settingIconSystemName: Icons.profileTabSettings, settingName: "Profile Tab Label", currentValue: $profileTabLabel, options: ProfileTabLabel.allCases @@ -54,7 +54,7 @@ struct TabBarSettingsView: View { appState.setActiveAccount(newAccount) } } icon: { - Image(systemName: "rectangle.and.pencil.and.ellipsis") + Image(systemName: Icons.nicknameField) .foregroundColor(.pink) } } @@ -62,19 +62,19 @@ struct TabBarSettingsView: View { Section { SwitchableSettingsItem( - settingPictureSystemName: "tag", + settingPictureSystemName: Icons.label, settingName: "Show Tab Labels", isTicked: $showTabNames ) SwitchableSettingsItem( - settingPictureSystemName: "envelope.badge", + settingPictureSystemName: Icons.unreadBadge, settingName: "Show Unread Count", isTicked: $showInboxUnreadBadge ) SwitchableSettingsItem( - settingPictureSystemName: "person.fill.questionmark", + settingPictureSystemName: Icons.showAvatar, settingName: "Show User Avatar", // if `.anonymous` is selected the toggle here should always be false isTicked: profileTabLabel == .anonymous ? .constant(false) : $showUserAvatar diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/User/UserSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/User/UserSettingsView.swift index c3d4d6919..22c42de5b 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Appearance/User/UserSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Appearance/User/UserSettingsView.swift @@ -14,12 +14,12 @@ struct UserSettingsView: View { var body: some View { Form { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.userSymbolName, + settingPictureSystemName: Icons.user, settingName: "Show Avatars", isTicked: $shouldShowUserAvatars ) SwitchableSettingsItem( - settingPictureSystemName: AppConstants.bannerSymbolName, + settingPictureSystemName: Icons.banner, settingName: "Show Banners", isTicked: $shouldShowUserHeaders ) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/Filters/FiltersSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/Filters/FiltersSettingsView.swift index 0ed3775f7..3061b1f47 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/Filters/FiltersSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/Filters/FiltersSettingsView.swift @@ -59,7 +59,7 @@ struct FiltersSettingsView: View { Text("Import Filters") } icon: { if showSettingsIcons { - Image(systemName: "square.and.arrow.down") + Image(systemName: Icons.importSymbol) } } } @@ -111,7 +111,7 @@ struct FiltersSettingsView: View { Text("Delete All Filters") } icon: { if showSettingsIcons { - Image(systemName: "trash") + Image(systemName: Icons.delete) } } .foregroundColor(.red) diff --git a/Mlem/Views/Tabs/Settings/Components/Views/General/GeneralSettingsView.swift b/Mlem/Views/Tabs/Settings/Components/Views/General/GeneralSettingsView.swift index 59546ceb7..64d257a3d 100644 --- a/Mlem/Views/Tabs/Settings/Components/Views/General/GeneralSettingsView.swift +++ b/Mlem/Views/Tabs/Settings/Components/Views/General/GeneralSettingsView.swift @@ -32,14 +32,14 @@ struct GeneralSettingsView: View { List { Section { SelectableSettingsItem( - settingIconSystemName: AppConstants.hapticSymbolName, + settingIconSystemName: Icons.haptics, settingName: "Haptic Level", currentValue: $hapticLevel, options: HapticPriority.allCases ) SwitchableSettingsItem( - settingPictureSystemName: "arrow.up.heart", + settingPictureSystemName: Icons.upvoteOnSave, settingName: "Upvote On Save", isTicked: $upvoteOnSave ) @@ -51,7 +51,7 @@ struct GeneralSettingsView: View { Section { SwitchableSettingsItem( - settingPictureSystemName: AppConstants.blurNsfwSymbolName, + settingPictureSystemName: Icons.blurNsfw, settingName: "Blur NSFW Content", isTicked: $shouldBlurNsfw ) @@ -94,7 +94,7 @@ struct GeneralSettingsView: View { Section { SelectableSettingsItem( - settingIconSystemName: AppConstants.connectionSymbolName, + settingIconSystemName: Icons.connection, settingName: "Internet Speed", currentValue: $internetSpeed, options: InternetSpeed.allCases @@ -113,7 +113,7 @@ struct GeneralSettingsView: View { Text("Delete Community Favorites") } icon: { if showSettingsIcons { - Image(systemName: "trash") + Image(systemName: Icons.delete) } } .foregroundColor(.red)