Skip to content

Commit

Permalink
Add Customizable Area to AccountOverview (#27)
Browse files Browse the repository at this point in the history
<!--

This source file is part of the Stanford Spezi open-source project

SPDX-FileCopyrightText: 2022 Stanford University and the project authors
(see CONTRIBUTORS.md)

SPDX-License-Identifier: MIT

-->

# Add Customizable Area to AccountOverview

## ♻️ Current situation & Problem
Like described in issue #25, some applications like the Spezi Template
application could display some additional information such as licensing
information or other settings at the bottom of the Account Overview.
Furthermore, some developers might want to do it the other way around
and link to account information within a settings page.

## 💡 Proposed Solution
Add footer components to AccountOverview, as suggested by @PSchmiedmayer
Implement an additional "AccountHeader" that looks something like the
one in the Apple Settings app, as suggested by @Supereg

## ⚙️ Release Notes 
This PR adds two major things. 

1. A customizable content section ViewBuilder to the `AccountOverview`.
Simply pass any view as a trailing closure of AccountOverview and it
will be rendered as a Section right between the other AccountOverview
information and the log out button. Here is an example:
```
AccountOverview() {
    NavigationLink {
        // ...
    } label: {
        Text("General Settings")
    }
    NavigationLink {
        // ...
    } label: {
        Text("License Information")
    }
}
```
<img width="255" alt="Screenshot 2023-09-27 at 17 30 04"
src="https://github.com/StanfordSpezi/SpeziAccount/assets/33159293/6a34258a-0f0d-4c73-85bb-8a6311bc1828">

2. A `AccountHeader`, similar to the one of the iOS Settings App, that
could be used as a button that brings a user to the `AccountOverview`.
Here is an example:
```
Section {
    NavigationLink {
        AccountOverview()
    } label: {
        AccountHeader(details: details)
    }
}
```
<img width="251" alt="Screenshot 2023-09-27 at 17 29 33"
src="https://github.com/StanfordSpezi/SpeziAccount/assets/33159293/e9d4f3aa-b5f2-4a62-9843-99e88ae77e1d">


## 📚 Documentation
*in progress*


## ✅ Testing
UI tests for the customizable content section have been written.

## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).

---------

Co-authored-by: Paul Schmiedmayer <[email protected]>
  • Loading branch information
NikolaiMadlener and PSchmiedmayer authored Oct 16, 2023
1 parent 5937ed6 commit 1eca293
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 93 deletions.
6 changes: 0 additions & 6 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ jobs:
artifactname: SpeziAccount.xcresult
runsonlabels: '["macOS", "self-hosted"]'
scheme: SpeziAccount
build:
name: Build Swift Package on Xcode 14
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macos-13"]'
scheme: SpeziAccount
buildandtestuitests:
name: Build and Test UI Tests
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ Spezi Account Contributors

* [Paul Schmiedmayer](https://github.com/PSchmiedmayer)
* [Andreas Bauer](https://github.com/Supereg)
* [Nikolai Madlener](https://github.com/NikolaiMadlener)
106 changes: 106 additions & 0 deletions Sources/SpeziAccount/AccountHeader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SpeziViews
import SwiftUI


/// A summary view for ``SpeziAccountOverview`` that can be used as a Button to link to ``SpeziAccountOverview``.
///
/// Below is a short code example on how to use the `AccountHeader` view.
///
/// ```swift
/// struct MyView: View {
/// var body: some View {
/// NavigationStack {
/// Form {
/// Section {
/// NavigationLink {
/// AccountOverview()
/// } label: {
/// AccountHeader(details: details)
/// }
/// }
/// }
/// }
/// }
/// }
/// ```
public struct AccountHeader: View {
/// Default values for the ``AccountHeader`` view.
@_documentation(visibility: internal)
public enum Defaults {
/// Default caption.
@_documentation(visibility: internal)
public static let caption = LocalizedStringResource("ACCOUNT_HEADER_CAPTION", bundle: .atURL(from: .module)) // swiftlint:disable:this attributes
}

@EnvironmentObject private var account: Account
private var caption: LocalizedStringResource

public var body: some View {
let accountDetails = account.details

HStack {
UserProfileView(name: accountDetails?.name ?? PersonNameComponents(givenName: "Placeholder", familyName: "Placeholder"))
.frame(height: 60)
.redacted(reason: account.details == nil ? .placeholder : [])
.accessibilityHidden(true)
VStack(alignment: .leading) {
Text(accountDetails?.name?.formatted() ?? "Placeholder")
.font(.title2)
.fontWeight(.semibold)
.redacted(reason: account.details == nil ? .placeholder : [])
Text(caption)
.font(.caption)
}
}
}

/// Display a new Account Header.
/// - Parameter caption: A descriptive text displayed under the account name giving the user a brief explanation of what to expect when they interact with the header.
public init(caption: LocalizedStringResource = Defaults.caption) {
self.caption = caption
}
}


#if DEBUG
#Preview {
let details = AccountDetails.Builder()
.set(\.userId, value: "[email protected]")
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))

return AccountHeader()
.environmentObject(Account(building: details, active: MockUserIdPasswordAccountService()))
}

#Preview {
AccountHeader(caption: "Email, Password, Preferences")
.environmentObject(Account(MockUserIdPasswordAccountService()))
}

#Preview {
let details = AccountDetails.Builder()
.set(\.userId, value: "[email protected]")
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))

return NavigationStack {
Form {
Section {
NavigationLink {
AccountOverview()
} label: {
AccountHeader()
}
}
}
}
.environmentObject(Account(building: details, active: MockUserIdPasswordAccountService()))
}
#endif
61 changes: 49 additions & 12 deletions Sources/SpeziAccount/AccountOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,35 @@ import SwiftUI
/// AccountOverview()
/// }
/// }
/// ```
///
/// Optionally, additional sections can be passed to AccountOverview within the trailing closure, providing the opportunity for customization and extension of the view."
/// Below is a short code example.
///
/// ```swift
/// struct MyView: View {
/// var body: some View {
/// AccountOverview {
/// NavigationLink {
/// // ... next view
/// } label: {
/// Text("General Settings")
/// }
/// }
/// }
/// }
/// ```
///
/// - Note: The ``init(isEditing:)`` initializer allows to pass an optional `Bool` Binding to retrieve the
/// current edit mode of the view. This can be helpful to, e.g., render a custom `Close` Button if the
/// view is not editing when presenting the AccountOverview in a sheet.
/// ```
public struct AccountOverview: View {
public struct AccountOverview<AdditionalSections: View>: View {
@EnvironmentObject private var account: Account

@Binding private var isEditing: Bool


let additionalSections: AdditionalSections

public var body: some View {
VStack {
if let details = account.details {
Expand All @@ -53,7 +72,9 @@ public struct AccountOverview: View {
account: account,
details: details,
isEditing: $isEditing
)
) {
additionalSections
}
}
.padding(.top, -20)
} else {
Expand All @@ -68,12 +89,15 @@ public struct AccountOverview: View {
.navigationTitle(Text("ACCOUNT_OVERVIEW", bundle: .module))
.navigationBarTitleDisplayMode(.inline)
}


/// Display a new Account Overview.
/// - Parameter isEditing: A Binding that allows you to read the current editing state of the Account Overview view.
public init(isEditing: Binding<Bool> = .constant(false)) {
/// - Parameters:
/// - isEditing: A Binding that allows you to read the current editing state of the Account Overview view.
/// - additionalSections: Optional additional sections displayed between the other AccountOverview information and the log out button.
public init(isEditing: Binding<Bool> = .constant(false), @ViewBuilder additionalSections: () -> AdditionalSections = { EmptyView() }) {
self._isEditing = isEditing
self.additionalSections = additionalSections()
}
}

Expand All @@ -84,13 +108,26 @@ struct AccountOverView_Previews: PreviewProvider {
.set(\.userId, value: "[email protected]")
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))
.set(\.genderIdentity, value: .male)

static var previews: some View {
NavigationStack {
AccountOverview()
AccountOverview {
NavigationLink {
Text("")
.navigationTitle(Text("Settings"))
} label: {
Text("General Settings")
}
NavigationLink {
Text("")
.navigationTitle(Text("Package Dependencies"))
} label: {
Text("License Information")
}
}
}
.environmentObject(Account(building: details, active: MockUserIdPasswordAccountService()))

NavigationStack {
AccountOverview()
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/SpeziAccount/Resources/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
"VALUE_ADD %@" = "%@ Hinzufügen";
"CHANGE_PASSWORD" = "Passwort Ändern";

// MARK - Account Header
"ACCOUNT_HEADER_CAPTION" = "Kontoinformationen & Details";

// MARK - Confirmation Dialogs
"CONFIRMATION_DISCARD_CHANGES_TITLE" = "Willst du alle Änderungen verwerfen?";
"CONFIRMATION_DISCARD_INPUT_TITLE" = "Willst du deine Eingaben verwerfen?";
Expand Down
3 changes: 3 additions & 0 deletions Sources/SpeziAccount/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
"VALUE_ADD %@" = "Add %@";
"CHANGE_PASSWORD" = "Change Password";

// MARK - Account Header
"ACCOUNT_HEADER_CAPTION" = "Account Information & Details";

// MARK - Confirmation Dialogs
"CONFIRMATION_DISCARD_CHANGES_TITLE" = "Are you sure you want to discard your changes?";
"CONFIRMATION_DISCARD_INPUT_TITLE" = "Are you sure you want to discard your input?";
Expand Down
Loading

0 comments on commit 1eca293

Please sign in to comment.