Skip to content

Commit

Permalink
Allow to supply active account details with the AccountConfiguration …
Browse files Browse the repository at this point in the history
…for previews (StanfordSpezi#46)

# Allow to supply active account details with the AccountConfiguration
for previews

## ♻️ Current situation & Problem
As of right now the desired way to provide access to the `Account`
object for previewing purposes with active account details, is by
creating the `Account` instance yourself and putting it into the
environment.
With the introduction of the new `previewWith` modifier in Spezi `1.0`,
we now have a framework-defined way of configuring modules for Previews.
Therefore, this PR adds support to configure the `AccountConfiguration`
for previewing purposes.


## ⚙️ Release Notes 
* Add `AccountConfiguration(building:active:configuration:)` initializer
to easily preview Spezi Account using the new spezi `previewWith`
modifier.


## 📚 Documentation
Documentation was added.


## ✅ Testing
_TBA_


## 📝 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).
  • Loading branch information
Supereg authored Jan 11, 2024
1 parent f1d9262 commit 714f01a
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 63 deletions.
3 changes: 2 additions & 1 deletion Sources/SpeziAccount/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public final class Account: Sendable {
/// - services: A collection of ``AccountService`` that are used to handle account-related functionality.
/// - supportedConfiguration: The ``AccountValueConfiguration`` to user intends to support.
/// - details: A initial ``AccountDetails`` object. The ``signedIn`` is set automatically based on the presence of this argument.
private nonisolated init(
nonisolated init(
services: [any AccountService],
supportedConfiguration: AccountValueConfiguration = .default,
details: AccountDetails? = nil
Expand Down Expand Up @@ -159,6 +159,7 @@ public final class Account: Sendable {
/// - builder: A ``AccountValuesBuilder`` for ``AccountDetails`` with all account details for the logged in user.
/// - accountService: The ``AccountService`` that is managing the provided ``AccountDetails``.
/// - configuration: The ``AccountValueConfiguration`` to user intends to support.
@available(*, deprecated, message: "Use the AccountConfiguration(building:active:configuration) and previewWith(_:) modifier for previews.")
public nonisolated convenience init<Service: AccountService>(
building builder: AccountDetails.Builder,
active accountService: Service,
Expand Down
25 changes: 23 additions & 2 deletions Sources/SpeziAccount/AccountConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ public final class AccountConfiguration: Module {
/// An array of ``AccountService``s provided directly in the initializer of the configuration object.
private let providedAccountServices: [any AccountService]

@Model private var account: Account
@Model private(set) var account: Account

@StandardActor private var standard: any Standard

/// The array of ``AccountService``s provided through other Spezi `Components`.
@Collect private var accountServices: [any AccountService]
/// Default active Account Details provided for previewing opportunities.
private let defaultActiveDetails: AccountDetails?


/// Initializes a `AccountConfiguration` without directly providing any ``AccountService`` instances.
Expand All @@ -46,6 +48,7 @@ public final class AccountConfiguration: Module {
public init(configuration: AccountValueConfiguration = .default) {
self.configuredAccountKeys = configuration
self.providedAccountServices = []
self.defaultActiveDetails = nil
}

/// Initializes a `AccountConfiguration` by directly providing a set of ``AccountService`` instances.
Expand All @@ -62,6 +65,23 @@ public final class AccountConfiguration: Module {
) {
self.configuredAccountKeys = configuration
self.providedAccountServices = accountServices()
self.defaultActiveDetails = nil
}

/// Configure the Account Module for previewing purposes with default `AccountDetails`.
///
/// - Parameters:
/// - builder: The ``AccountDetails`` Builder for the account details that you want to supply.
/// - accountService: The ``AccountService`` that is responsible for the supplied account details.
/// - configuration: The user-defined configuration of account values that all user accounts need to support.
public init<Service: AccountService>(
building builder: AccountDetails.Builder,
active accountService: Service,
configuration: AccountValueConfiguration = .default
) {
self.configuredAccountKeys = configuration
self.providedAccountServices = [accountService]
self.defaultActiveDetails = builder.build(owner: accountService)
}


Expand All @@ -81,7 +101,8 @@ public final class AccountConfiguration: Module {

self.account = Account(
services: accountServices,
configuration: configuredAccountKeys
supportedConfiguration: configuredAccountKeys,
details: defaultActiveDetails
)

self.account.injectWeakAccount(into: standard)
Expand Down
33 changes: 25 additions & 8 deletions Sources/SpeziAccount/AccountHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Spezi
import SpeziPersonalInfo
import SwiftUI

Expand Down Expand Up @@ -42,8 +43,8 @@ public struct AccountHeader: View {
}

@Environment(Account.self) private var account
private var caption: LocalizedStringResource
private let caption: Text

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

Expand All @@ -68,7 +69,7 @@ public struct AccountHeader: View {
.font(.title2)
.fontWeight(.semibold)
.redacted(reason: account.details == nil ? .placeholder : [])
Text(caption)
caption
.font(.caption)
}
}
Expand All @@ -77,6 +78,12 @@ public struct AccountHeader: View {
/// 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.init(caption: Text(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: Text) {
self.caption = caption
}
}
Expand All @@ -89,11 +96,13 @@ public struct AccountHeader: View {
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))

return AccountHeader()
.environment(Account(building: details, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}

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

Expand All @@ -113,7 +122,9 @@ public struct AccountHeader: View {
}
}
}
.environment(Account(building: details, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}

#Preview {
Expand All @@ -131,7 +142,9 @@ public struct AccountHeader: View {
}
}
}
.environment(Account(building: details, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}

#Preview {
Expand All @@ -146,6 +159,10 @@ public struct AccountHeader: View {
}
}
}
.environment(Account(MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration {
MockUserIdPasswordAccountService()
}
}
}
#endif
5 changes: 4 additions & 1 deletion Sources/SpeziAccount/AccountOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Spezi
import SpeziViews
import SwiftUI

Expand Down Expand Up @@ -126,7 +127,9 @@ struct AccountOverView_Previews: PreviewProvider {
}
}
}
.environment(Account(building: details, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}

NavigationStack {
AccountOverview()
Expand Down
12 changes: 9 additions & 3 deletions Sources/SpeziAccount/AccountSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,14 @@ struct AccountView_Previews: PreviewProvider {
}

AccountSetup()
.environment(Account(building: detailsBuilder, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: detailsBuilder, active: MockUserIdPasswordAccountService())
}

AccountSetup(state: .setupShown)
.environment(Account(building: detailsBuilder, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: detailsBuilder, active: MockUserIdPasswordAccountService())
}

AccountSetup(continue: {
Button(action: {
Expand All @@ -255,7 +259,9 @@ struct AccountView_Previews: PreviewProvider {
})
.buttonStyle(.borderedProminent)
})
.environment(Account(building: detailsBuilder, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: detailsBuilder, active: MockUserIdPasswordAccountService())
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ class AccountOverviewFormViewModel {

for describedKey in details.accountService.configuration.requiredAccountKeys
where describedKey.key.category == category {
result.append(describedKey.key)
if !result.contains(where: { $0 == describedKey.key }) {
result.append(describedKey.key)
}
}

return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Spezi
import SwiftUI


Expand Down Expand Up @@ -84,16 +85,16 @@ struct AccountKeyEditRow_Previews: PreviewProvider {
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))
.set(\.genderIdentity, value: .male)

static let account = Account(building: details, active: MockUserIdPasswordAccountService())

@State private static var model = AccountOverviewFormViewModel(account: account)

static var previews: some View {
if let details = account.details {
AccountDetailsReader { account, details in
let model = AccountOverviewFormViewModel(account: account)

AccountKeyOverviewRow(details: details, for: GenderIdentityKey.self, model: model)
.injectEnvironmentObjects(service: details.accountService, model: model)
.environment(account)
}
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import OrderedCollections
import Spezi
import SpeziValidation
import SpeziViews
import SwiftUI
Expand Down Expand Up @@ -285,7 +286,9 @@ struct AccountOverviewSections_Previews: PreviewProvider {
}
}
}
.environment(Account(building: details, active: MockUserIdPasswordAccountService()))
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}
}
#endif
22 changes: 10 additions & 12 deletions Sources/SpeziAccount/Views/AccountOverview/NameOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,24 @@ struct NameOverview_Previews: PreviewProvider {
static let detailsWithoutName = AccountDetails.Builder()
.set(\.userId, value: "[email protected]")

static let account = Account(building: details, active: MockUserIdPasswordAccountService())
static let accountWithoutName = Account(building: detailsWithoutName, active: MockUserIdPasswordAccountService())

// be aware, modifications won't be displayed due to declaration in PreviewProvider that do not trigger an UI update
@State static var model = AccountOverviewFormViewModel(account: account)

static var previews: some View {
NavigationStack {
if let details = account.details {
NameOverview(model: model, details: details)
AccountDetailsReader { account, details in
NameOverview(model: AccountOverviewFormViewModel(account: account), details: details)
}
}
.environment(account)
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}

NavigationStack {
if let details = accountWithoutName.details {
NameOverview(model: model, details: details)
AccountDetailsReader { account, details in
NameOverview(model: AccountOverviewFormViewModel(account: account), details: details)
}
}
.environment(accountWithoutName)
.previewWith {
AccountConfiguration(building: detailsWithoutName, active: MockUserIdPasswordAccountService())
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Spezi
import SpeziValidation
import SpeziViews
import SwiftUI
Expand Down Expand Up @@ -136,18 +137,15 @@ struct PasswordChangeSheet_Previews: PreviewProvider {
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))
.set(\.genderIdentity, value: .male)

static let account = Account(building: details, active: MockUserIdPasswordAccountService())

// be aware, modifications won't be displayed due to declaration in PreviewProvider that do not trigger an UI update
@State static var model = AccountOverviewFormViewModel(account: account)

static var previews: some View {
NavigationStack {
if let details = account.details {
PasswordChangeSheet(model: model, details: details)
AccountDetailsReader { account, details in
PasswordChangeSheet(model: AccountOverviewFormViewModel(account: account), details: details)
}
}
.environment(account)
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Spezi
import SpeziViews
import SwiftUI

Expand Down Expand Up @@ -79,18 +80,15 @@ struct SecurityOverview_Previews: PreviewProvider {
.set(\.name, value: PersonNameComponents(givenName: "Andreas", familyName: "Bauer"))
.set(\.genderIdentity, value: .male)

static let account = Account(building: details, active: MockUserIdPasswordAccountService())

// be aware, modifications won't be displayed due to declaration in PreviewProvider that do not trigger an UI update
@State static var model = AccountOverviewFormViewModel(account: account)

static var previews: some View {
NavigationStack {
if let details = account.details {
SecurityOverview(model: model, details: details)
AccountDetailsReader { account, details in
SecurityOverview(model: AccountOverviewFormViewModel(account: account), details: details)
}
}
.environment(account)
.previewWith {
AccountConfiguration(building: details, active: MockUserIdPasswordAccountService())
}
}
}
#endif
Loading

0 comments on commit 714f01a

Please sign in to comment.