Skip to content

Commit ba5c037

Browse files
JPKribsLePips
andauthored
[iOS] Admin Dashboard - Parental Ratings (#1353)
* Labels and Max Parental Rating * Parental Ratings * UnratedItem.displayTitle * Linting Fixes * Localizations, LearnMore, & cleaner grouping * Strings.swift * Review changes + Age Groups change * cleanup, use SeparatorVStack in LearnMoreButton * fix colors --------- Co-authored-by: Ethan Pippin <[email protected]>
1 parent 8f05169 commit ba5c037

File tree

10 files changed

+399
-41
lines changed

10 files changed

+399
-41
lines changed

Shared/Components/SeparatorVStack.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ import SwiftUI
1010

1111
// https://movingparts.io/variadic-views-in-swiftui
1212

13-
/// An `HStack` that inserts an optional `separator` between views.
13+
/// A `VStack` that inserts an optional `separator` between views.
1414
///
1515
/// - Note: Default spacing is removed. The separator view is responsible
1616
/// for spacing.
1717
struct SeparatorVStack<Content: View, Separator: View>: View {
1818

19-
private var content: () -> Content
20-
private var separator: () -> Separator
19+
private let alignment: HorizontalAlignment
20+
private let content: () -> Content
21+
private let separator: () -> Separator
2122

2223
var body: some View {
23-
_VariadicView.Tree(SeparatorVStackLayout(separator: separator)) {
24+
_VariadicView.Tree(SeparatorVStackLayout(alignment: alignment, separator: separator)) {
2425
content()
2526
}
2627
}
@@ -29,10 +30,12 @@ struct SeparatorVStack<Content: View, Separator: View>: View {
2930
extension SeparatorVStack {
3031

3132
init(
33+
alignment: HorizontalAlignment = .center,
3234
@ViewBuilder separator: @escaping () -> Separator,
3335
@ViewBuilder content: @escaping () -> Content
3436
) {
3537
self.init(
38+
alignment: alignment,
3639
content: content,
3740
separator: separator
3841
)
@@ -43,14 +46,15 @@ extension SeparatorVStack {
4346

4447
struct SeparatorVStackLayout: _VariadicView_UnaryViewRoot {
4548

46-
var separator: () -> Separator
49+
let alignment: HorizontalAlignment
50+
let separator: () -> Separator
4751

4852
@ViewBuilder
4953
func body(children: _VariadicView.Children) -> some View {
5054

5155
let last = children.last?.id
5256

53-
localHStack {
57+
VStack(alignment: alignment, spacing: 0) {
5458
ForEach(children) { child in
5559
child
5660

@@ -60,12 +64,5 @@ extension SeparatorVStack {
6064
}
6165
}
6266
}
63-
64-
@ViewBuilder
65-
private func localHStack(@ViewBuilder content: @escaping () -> some View) -> some View {
66-
VStack(spacing: 0) {
67-
content()
68-
}
69-
}
7067
}
7168
}

Shared/Coordinators/AdminDashboardCoordinator.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ final class AdminDashboardCoordinator: NavigationCoordinatable {
6060
@Route(.modal)
6161
var userPermissions = makeUserPermissions
6262
@Route(.modal)
63+
var userParentalRatings = makeUserParentalRatings
64+
@Route(.modal)
6365
var resetUserPassword = makeResetUserPassword
6466
@Route(.modal)
6567
var addServerUser = makeAddServerUser
@@ -160,6 +162,12 @@ final class AdminDashboardCoordinator: NavigationCoordinatable {
160162
}
161163
}
162164

165+
func makeUserParentalRatings(viewModel: ServerUserAdminViewModel) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
166+
NavigationViewCoordinator {
167+
ServerUserParentalRatingView(viewModel: viewModel)
168+
}
169+
}
170+
163171
func makeResetUserPassword(userID: String) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
164172
NavigationViewCoordinator {
165173
ResetUserPasswordView(userID: userID, requiresCurrentPassword: false)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Swiftfin is subject to the terms of the Mozilla Public
3+
// License, v2.0. If a copy of the MPL was not distributed with this
4+
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
5+
//
6+
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
7+
//
8+
9+
import Foundation
10+
import JellyfinAPI
11+
12+
extension UnratedItem: Displayable {
13+
14+
var displayTitle: String {
15+
switch self {
16+
case .movie:
17+
L10n.movies
18+
case .trailer:
19+
L10n.trailers
20+
case .series:
21+
L10n.tvShows
22+
case .music:
23+
L10n.music
24+
case .book:
25+
L10n.books
26+
case .liveTvChannel:
27+
L10n.liveTVChannels
28+
case .liveTvProgram:
29+
L10n.liveTVPrograms
30+
case .channelContent:
31+
L10n.channels
32+
case .other:
33+
L10n.other
34+
}
35+
}
36+
}

Shared/Strings/Strings.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ internal enum L10n {
2222
internal static let access = L10n.tr("Localizable", "access", fallback: "Access")
2323
/// Accessibility
2424
internal static let accessibility = L10n.tr("Localizable", "accessibility", fallback: "Accessibility")
25+
/// Access schedule
26+
internal static let accessSchedule = L10n.tr("Localizable", "accessSchedule", fallback: "Access schedule")
27+
/// Create an access schedule to limit access to certain hours.
28+
internal static let accessScheduleDescription = L10n.tr("Localizable", "accessScheduleDescription", fallback: "Create an access schedule to limit access to certain hours.")
2529
/// Active
2630
internal static let active = L10n.tr("Localizable", "active", fallback: "Active")
2731
/// Active Devices
@@ -50,6 +54,10 @@ internal enum L10n {
5054
internal static let administrator = L10n.tr("Localizable", "administrator", fallback: "Administrator")
5155
/// Advanced
5256
internal static let advanced = L10n.tr("Localizable", "advanced", fallback: "Advanced")
57+
/// Age %@
58+
internal static func agesGroup(_ p1: Any) -> String {
59+
return L10n.tr("Localizable", "agesGroup", String(describing: p1), fallback: "Age %@")
60+
}
5361
/// Aired
5462
internal static let aired = L10n.tr("Localizable", "aired", fallback: "Aired")
5563
/// Air Time
@@ -60,6 +68,8 @@ internal enum L10n {
6068
}
6169
/// Album Artist
6270
internal static let albumArtist = L10n.tr("Localizable", "albumArtist", fallback: "Album Artist")
71+
/// All Audiences
72+
internal static let allAudiences = L10n.tr("Localizable", "allAudiences", fallback: "All Audiences")
6373
/// View all past and present devices that have connected.
6474
internal static let allDevicesDescription = L10n.tr("Localizable", "allDevicesDescription", fallback: "View all past and present devices that have connected.")
6575
/// All Genres
@@ -68,6 +78,10 @@ internal enum L10n {
6878
internal static let allMedia = L10n.tr("Localizable", "allMedia", fallback: "All Media")
6979
/// Allow collection management
7080
internal static let allowCollectionManagement = L10n.tr("Localizable", "allowCollectionManagement", fallback: "Allow collection management")
81+
/// Allowed tags
82+
internal static let allowedTags = L10n.tr("Localizable", "allowedTags", fallback: "Allowed tags")
83+
/// Only show media to this user with at least one of the specified tags.
84+
internal static let allowedTagsDescription = L10n.tr("Localizable", "allowedTagsDescription", fallback: "Only show media to this user with at least one of the specified tags.")
7185
/// Allow media item deletion
7286
internal static let allowItemDeletion = L10n.tr("Localizable", "allowItemDeletion", fallback: "Allow media item deletion")
7387
/// Allow media item editing
@@ -198,8 +212,18 @@ internal enum L10n {
198212
internal static let bitrateTestDisclaimer = L10n.tr("Localizable", "bitrateTestDisclaimer", fallback: "Longer tests are more accurate but may result in a delayed playback.")
199213
/// bps
200214
internal static let bitsPerSecond = L10n.tr("Localizable", "bitsPerSecond", fallback: "bps")
215+
/// Blocked tags
216+
internal static let blockedTags = L10n.tr("Localizable", "blockedTags", fallback: "Blocked tags")
217+
/// Hide media with at least one of the specified tags.
218+
internal static let blockedTagsDescription = L10n.tr("Localizable", "blockedTagsDescription", fallback: "Hide media with at least one of the specified tags.")
219+
/// Block unrated items
220+
internal static let blockUnratedItems = L10n.tr("Localizable", "blockUnratedItems", fallback: "Block unrated items")
221+
/// Block items from this user with no or unrecognized rating information.
222+
internal static let blockUnratedItemsDescription = L10n.tr("Localizable", "blockUnratedItemsDescription", fallback: "Block items from this user with no or unrecognized rating information.")
201223
/// Blue
202224
internal static let blue = L10n.tr("Localizable", "blue", fallback: "Blue")
225+
/// Books
226+
internal static let books = L10n.tr("Localizable", "books", fallback: "Books")
203227
/// Bugs and Features
204228
internal static let bugsAndFeatures = L10n.tr("Localizable", "bugsAndFeatures", fallback: "Bugs and Features")
205229
/// Buttons
@@ -736,6 +760,10 @@ internal enum L10n {
736760
internal static let liveTV = L10n.tr("Localizable", "liveTV", fallback: "Live TV")
737761
/// Live TV access
738762
internal static let liveTvAccess = L10n.tr("Localizable", "liveTvAccess", fallback: "Live TV access")
763+
/// Live TV Channels
764+
internal static let liveTVChannels = L10n.tr("Localizable", "liveTVChannels", fallback: "Live TV Channels")
765+
/// Live TV Programs
766+
internal static let liveTVPrograms = L10n.tr("Localizable", "liveTVPrograms", fallback: "Live TV Programs")
739767
/// Live TV recording management
740768
internal static let liveTvRecordingManagement = L10n.tr("Localizable", "liveTvRecordingManagement", fallback: "Live TV recording management")
741769
/// Loading
@@ -782,6 +810,10 @@ internal enum L10n {
782810
internal static let maximumSessions = L10n.tr("Localizable", "maximumSessions", fallback: "Maximum sessions")
783811
/// Maximum sessions policy
784812
internal static let maximumSessionsPolicy = L10n.tr("Localizable", "maximumSessionsPolicy", fallback: "Maximum sessions policy")
813+
/// Maximum parental rating
814+
internal static let maxParentalRating = L10n.tr("Localizable", "maxParentalRating", fallback: "Maximum parental rating")
815+
/// Content with a higher rating will be hidden from this user.
816+
internal static let maxParentalRatingDescription = L10n.tr("Localizable", "maxParentalRatingDescription", fallback: "Content with a higher rating will be hidden from this user.")
785817
/// This setting may result in media failing to start playback.
786818
internal static let mayResultInPlaybackFailure = L10n.tr("Localizable", "mayResultInPlaybackFailure", fallback: "This setting may result in media failing to start playback.")
787819
/// Media
@@ -818,6 +850,8 @@ internal enum L10n {
818850
internal static func multipleUsers(_ p1: Int) -> String {
819851
return L10n.tr("Localizable", "multipleUsers", p1, fallback: "%d users")
820852
}
853+
/// Music
854+
internal static let music = L10n.tr("Localizable", "music", fallback: "Music")
821855
/// MVC
822856
internal static let mvc = L10n.tr("Localizable", "mvc", fallback: "MVC")
823857
/// Name
@@ -926,6 +960,8 @@ internal enum L10n {
926960
internal static func pageOfWithNumbers(_ p1: Any, _ p2: Any) -> String {
927961
return L10n.tr("Localizable", "pageOfWithNumbers", String(describing: p1), String(describing: p2), fallback: "Page %1$@ of %2$@")
928962
}
963+
/// Parental controls
964+
internal static let parentalControls = L10n.tr("Localizable", "parentalControls", fallback: "Parental controls")
929965
/// Parental Rating
930966
internal static let parentalRating = L10n.tr("Localizable", "parentalRating", fallback: "Parental Rating")
931967
/// Password
@@ -1394,6 +1430,8 @@ internal enum L10n {
13941430
internal static let title = L10n.tr("Localizable", "title", fallback: "Title")
13951431
/// Too Many Redirects
13961432
internal static let tooManyRedirects = L10n.tr("Localizable", "tooManyRedirects", fallback: "Too Many Redirects")
1433+
/// Trailers
1434+
internal static let trailers = L10n.tr("Localizable", "trailers", fallback: "Trailers")
13971435
/// Trailing Value
13981436
internal static let trailingValue = L10n.tr("Localizable", "trailingValue", fallback: "Trailing Value")
13991437
/// Transcode

Swiftfin.xcodeproj/project.pbxproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
4E204E592C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */; };
3535
4E2182E52CAF67F50094806B /* PlayMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2182E42CAF67EF0094806B /* PlayMethod.swift */; };
3636
4E2182E62CAF67F50094806B /* PlayMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2182E42CAF67EF0094806B /* PlayMethod.swift */; };
37+
4E2470082D078DD7009139D8 /* ServerUserParentalRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2470062D078DD7009139D8 /* ServerUserParentalRatingView.swift */; };
3738
4E24ECFB2D076F6200A473A9 /* ListRowCheckbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E24ECFA2D076F2B00A473A9 /* ListRowCheckbox.swift */; };
3839
4E24ECFC2D076F6200A473A9 /* ListRowCheckbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E24ECFA2D076F2B00A473A9 /* ListRowCheckbox.swift */; };
3940
4E2AC4BE2C6C48D200DD600D /* CustomDeviceProfileAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2AC4BD2C6C48D200DD600D /* CustomDeviceProfileAction.swift */; };
@@ -100,6 +101,8 @@
100101
4E5E48E52AB59806003F1B48 /* CustomizeViewsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */; };
101102
4E63B9FA2C8A5BEF00C25378 /* AdminDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9F42C8A5BEF00C25378 /* AdminDashboardView.swift */; };
102103
4E63B9FC2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */; };
104+
4E656C302D0798AA00F993F3 /* ParentalRating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E656C2F2D0798A900F993F3 /* ParentalRating.swift */; };
105+
4E656C312D0798AA00F993F3 /* ParentalRating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E656C2F2D0798A900F993F3 /* ParentalRating.swift */; };
103106
4E6619FC2CEFE2BE00025C99 /* ItemEditorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6619FB2CEFE2B500025C99 /* ItemEditorViewModel.swift */; };
104107
4E6619FD2CEFE2BE00025C99 /* ItemEditorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6619FB2CEFE2B500025C99 /* ItemEditorViewModel.swift */; };
105108
4E661A012CEFE39D00025C99 /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E661A002CEFE39900025C99 /* EditMetadataView.swift */; };
@@ -1181,6 +1184,7 @@
11811184
4E182C9E2C94A1E000FBEFD5 /* ServerTaskRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTaskRow.swift; sourceTree = "<group>"; };
11821185
4E204E582C574FD9004D22A2 /* CustomizeSettingsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeSettingsCoordinator.swift; sourceTree = "<group>"; };
11831186
4E2182E42CAF67EF0094806B /* PlayMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayMethod.swift; sourceTree = "<group>"; };
1187+
4E2470062D078DD7009139D8 /* ServerUserParentalRatingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerUserParentalRatingView.swift; sourceTree = "<group>"; };
11841188
4E24ECFA2D076F2B00A473A9 /* ListRowCheckbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRowCheckbox.swift; sourceTree = "<group>"; };
11851189
4E2AC4BD2C6C48D200DD600D /* CustomDeviceProfileAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDeviceProfileAction.swift; sourceTree = "<group>"; };
11861190
4E2AC4C12C6C491200DD600D /* AudoCodec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudoCodec.swift; sourceTree = "<group>"; };
@@ -1228,6 +1232,7 @@
12281232
4E5E48E42AB59806003F1B48 /* CustomizeViewsSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeViewsSettings.swift; sourceTree = "<group>"; };
12291233
4E63B9F42C8A5BEF00C25378 /* AdminDashboardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdminDashboardView.swift; sourceTree = "<group>"; };
12301234
4E63B9FB2C8A5C3E00C25378 /* ActiveSessionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActiveSessionsViewModel.swift; sourceTree = "<group>"; };
1235+
4E656C2F2D0798A900F993F3 /* ParentalRating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentalRating.swift; sourceTree = "<group>"; };
12311236
4E6619FB2CEFE2B500025C99 /* ItemEditorViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEditorViewModel.swift; sourceTree = "<group>"; };
12321237
4E661A002CEFE39900025C99 /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
12331238
4E661A042CEFE46300025C99 /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = "<group>"; };
@@ -2142,6 +2147,14 @@
21422147
path = Components;
21432148
sourceTree = "<group>";
21442149
};
2150+
4E2470072D078DD7009139D8 /* ServerUserParentalRatingView */ = {
2151+
isa = PBXGroup;
2152+
children = (
2153+
4E2470062D078DD7009139D8 /* ServerUserParentalRatingView.swift */,
2154+
);
2155+
path = ServerUserParentalRatingView;
2156+
sourceTree = "<group>";
2157+
};
21452158
4E2AC4C02C6C48EB00DD600D /* MediaComponents */ = {
21462159
isa = PBXGroup;
21472160
children = (
@@ -2299,6 +2312,7 @@
22992312
4E35CE622CBED3FF00DBD886 /* ServerLogsView */,
23002313
4E182C9A2C94991800FBEFD5 /* ServerTasksView */,
23012314
4EC2B1A72CC9725400D866BE /* ServerUserDetailsView */,
2315+
4E2470072D078DD7009139D8 /* ServerUserParentalRatingView */,
23022316
4E537A822D03D0FA00659A1A /* ServerUserDeviceAccessView */,
23032317
4E537A8C2D04410E00659A1A /* ServerUserLiveTVAccessView */,
23042318
4EF3D80A2CF7D6670081AD20 /* ServerUserAccessView */,
@@ -4234,6 +4248,7 @@
42344248
E122A9122788EAAD0060FA63 /* MediaStream.swift */,
42354249
4E661A2D2CEFE77700025C99 /* MetadataField.swift */,
42364250
E1AD105E26D9ADDD003E4A08 /* NameGuidPair.swift */,
4251+
4E656C2F2D0798A900F993F3 /* ParentalRating.swift */,
42374252
4EFE0C7C2D0156A500D4834D /* PersonKind.swift */,
42384253
E1ED7FDA2CAA4B6D00ACB6E3 /* PlayerStateInfo.swift */,
42394254
4E2182E42CAF67EF0094806B /* PlayMethod.swift */,
@@ -5162,6 +5177,7 @@
51625177
E1356E0429A731EB00382563 /* SeparatorHStack.swift in Sources */,
51635178
E1575E69293E77B5001665B1 /* ItemSortBy.swift in Sources */,
51645179
E1B490482967E2E500D3EDCE /* CoreStore.swift in Sources */,
5180+
4E656C312D0798AA00F993F3 /* ParentalRating.swift in Sources */,
51655181
E1DC9845296DECB600982F06 /* ProgressIndicator.swift in Sources */,
51665182
E1C925F928875647002A7A66 /* LatestInLibraryView.swift in Sources */,
51675183
E11B1B6D2718CD68006DA3E8 /* JellyfinAPIError.swift in Sources */,
@@ -5384,6 +5400,7 @@
53845400
6220D0B426D5ED8000B8E046 /* LibraryCoordinator.swift in Sources */,
53855401
E17AC96D2954E9CA003D2BC2 /* DownloadListView.swift in Sources */,
53865402
4E8B34EA2AB91B6E0018F305 /* ItemFilter.swift in Sources */,
5403+
4E2470082D078DD7009139D8 /* ServerUserParentalRatingView.swift in Sources */,
53875404
E1A1528828FD229500600579 /* ChevronButton.swift in Sources */,
53885405
E1CB75732C80E71800217C76 /* DirectPlayProfile.swift in Sources */,
53895406
E1B490472967E2E500D3EDCE /* CoreStore.swift in Sources */,
@@ -5546,6 +5563,7 @@
55465563
4E2AC4C52C6C492700DD600D /* MediaContainer.swift in Sources */,
55475564
4E2AC4CB2C6C494E00DD600D /* VideoCodec.swift in Sources */,
55485565
E1EA09692BED78BB004CDE76 /* UserAccessPolicy.swift in Sources */,
5566+
4E656C302D0798AA00F993F3 /* ParentalRating.swift in Sources */,
55495567
E18E0204288749200022598C /* RowDivider.swift in Sources */,
55505568
E18E01DA288747230022598C /* iPadOSEpisodeContentView.swift in Sources */,
55515569
E1CB75752C80EAFA00217C76 /* ArrayBuilder.swift in Sources */,

Swiftfin/Components/LearnMoreButton.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ struct LearnMoreButton: View {
4242
private var learnMoreView: some View {
4343
NavigationView {
4444
ScrollView {
45-
VStack(alignment: .leading, spacing: 16) {
45+
SeparatorVStack(alignment: .leading) {
46+
Divider()
47+
} content: {
4648
ForEach(items) { content in
4749
VStack(alignment: .leading, spacing: 8) {
4850
Text(content.title)
@@ -53,16 +55,17 @@ struct LearnMoreButton: View {
5355
.font(.subheadline)
5456
.foregroundStyle(.secondary)
5557
}
56-
Divider()
58+
.padding(.vertical, 16)
5759
}
5860
}
59-
.edgePadding()
61+
.edgePadding(.horizontal)
6062
}
6163
.navigationTitle(title)
6264
.navigationBarTitleDisplayMode(.inline)
6365
.navigationBarCloseButton {
6466
isPresented = false
6567
}
6668
}
69+
.foregroundStyle(Color.primary, Color.secondary)
6770
}
6871
}

0 commit comments

Comments
 (0)