Skip to content

refactor: 💡 [HCPSDKFIORIUIKIT-2679] InfoViewModel #1060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import FioriSwiftUICore
import SwiftUI

class InfoViewDataModel: InfoViewModel {
class InfoViewDataModel: _InfoViewModel {
var title: String = "SAP BTP SDK for iOS"
var descriptionText: String? = "SAP BTP SDK for iOS enables you to quickly develop your own native apps, with Swift. The SDK extends the standard Swift Apple iOS frameworks with the reusable UI components from the SAP Fiori for iOS Design Language, and provides APIs which seamlessly integrate apps with SAP BTP services. "
var showLoadingIndicator: Bool? = true
Expand Down Expand Up @@ -45,12 +45,19 @@ struct InfoViewSample: View {

var body: some View {
VStack {
InfoView(model: self.model)
let loadingIndicator = LoadingIndicator(title: "", isPresented: .constant(true))

InfoView(title: AttributedString(self.model.title), descriptionText: AttributedString(self.model.descriptionText ?? ""), action: FioriButton(title: "Next", action: { _ in
print("InfoView Primary button clicked")
}), secondaryAction: FioriButton(title: "Start Tutorial", action: { _ in
print("InfoView secondary button clicked")
}), loadingIndicator: loadingIndicator)
}
}
}

struct InfoViewWithLoadingLabel: View {
@State var showLoadingView: Bool = true
private var model = InfoViewDataModel()

public init() {
Expand All @@ -59,7 +66,16 @@ struct InfoViewWithLoadingLabel: View {

var body: some View {
VStack {
InfoView(model: self.model)
let loadingIndicator = LoadingIndicator(title: {
Text(AttributedString(self.model.loadingIndicatorText ?? ""))
.font(.fiori(forTextStyle: .body))
}, progress: { ProgressView() }, isPresented: $showLoadingView)

InfoView(title: AttributedString(self.model.title), descriptionText: AttributedString(self.model.descriptionText ?? ""), action: FioriButton(title: "Next", action: { _ in
print("InfoView Primary button clicked")
}), secondaryAction: FioriButton(title: "Start Tutorial", action: { _ in
print("InfoView secondary button clicked")
}), loadingIndicator: loadingIndicator)
}
}
}
Expand All @@ -71,7 +87,59 @@ struct InfoViewCustomized: View {

var body: some View {
VStack {
InfoView(model: self.model)
let loadingIndicator = LoadingIndicator(title: { Text("") }, progress: { ProgressView().progressViewStyle(CircularProgressViewStyle(tint: .red)) }, isPresented: .constant(true))

InfoView(title: { Text(AttributedString(self.model.title)) },
descriptionText: { Text(AttributedString(self.model.descriptionText ?? "")).foregroundColor(.blue) },
action: {
FioriButton(title: "Next") { _ in
print("InfoView Primary button clicked")
}
},
secondaryAction: {
Button("Start Tutorial") {
print("InfoView secondary button clicked")
}
},
loadingIndicator: { loadingIndicator })
}
}
}

struct _InfoViewSample: View {
private var model = InfoViewDataModel()

public init() {}

var body: some View {
VStack {
_InfoView(model: self.model)
}
}
}

struct _InfoViewWithLoadingLabel: View {
private var model = InfoViewDataModel()

public init() {
self.model.loadingIndicatorText = "Loading..."
}

var body: some View {
VStack {
_InfoView(model: self.model)
}
}
}

struct _InfoViewCustomized: View {
private var model = InfoViewDataModel()

public init() {}

var body: some View {
VStack {
_InfoView(model: self.model)
.descriptionTextModifier { $0.font(.fiori(forTextStyle: .subheadline)).foregroundColor(.blue) }
.actionModifier { $0.foregroundColor(.blue) }
.loadingIndicatorStyle(CustomLoadingStyle())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ struct OnboardingExamples: View {
Text("ActivationScreen")
}

NavigationLink(
destination: InfoViewExamples(),
label: {
if self._isNewObjectItem {
NavigationLink(
destination: InfoViewExamples(isNewInfoView: true))
{
Text("InfoView Examples")
}
)
if self._isNewObjectItem {
NavigationLink(
destination: EULAExamples(isNewEULAView: true))
{
Expand All @@ -46,6 +45,11 @@ struct OnboardingExamples: View {
Text("AuthenticationScreen Examples")
}
} else {
NavigationLink(
destination: InfoViewExamples())
{
Text("_InfoView Examples")
}
NavigationLink(
destination: EULAExamples())
{
Expand Down Expand Up @@ -120,23 +124,35 @@ struct ActivationScreenExamples: View {
}

struct InfoViewExamples: View {
var isNewInfoView = false
var body: some View {
List {
NavigationLink(
destination: InfoViewSample(),
label: {
Text("InfoView")
NavigationLink {
if self.isNewInfoView {
InfoViewSample()
} else {
_InfoViewSample()
}
)
NavigationLink(
destination: InfoViewWithLoadingLabel())
{
} label: {
Text("InfoView")
}
NavigationLink {
if self.isNewInfoView {
InfoViewWithLoadingLabel()
} else {
_InfoViewWithLoadingLabel()
}
} label: {
Text("InfoView With Loading Label")
}

NavigationLink(
destination: InfoViewCustomized())
{
NavigationLink {
if self.isNewInfoView {
InfoViewCustomized()
} else {
_InfoViewCustomized()
}
} label: {
Text("InfoView Customized")
}
}.navigationBarTitle("InfoView Examples", displayMode: .inline)
Expand Down
6 changes: 5 additions & 1 deletion Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public protocol _ActivationScreenModel: TitleComponent, DescriptionTextComponent
public protocol ActivationScreenModel {}

// sourcery: generated_component_composite
public protocol InfoViewModel: TitleComponent, DescriptionTextComponent {
public protocol _InfoViewModel: TitleComponent, DescriptionTextComponent {
// sourcery: default.value = nil
// sourcery: no_view
var showLoadingIndicator: Bool? { get }
Expand All @@ -239,6 +239,10 @@ public protocol InfoViewModel: TitleComponent, DescriptionTextComponent {
var secondaryAction: _ActionModel? { get }
}

/// Deprecated InfoViewModel
@available(*, deprecated, renamed: "_InfoViewModel", message: "Will be removed in the future release. Please create InfoViewModel with other initializers instead.")
public protocol InfoViewModel {}

// sourcery: generated_component_composite
// sourcery: virtualPropContentHeight = "@State var contentHeight: CGFloat = .zero"
// sourcery: add_env_props = ["presentationMode"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import SwiftUI

extension Fiori {
enum InfoView {
enum _InfoView {
struct Title: ViewModifier {
func body(content: Content) -> some View {
content
Expand Down Expand Up @@ -60,7 +60,7 @@ extension Fiori {
}
}

extension InfoView: View {
extension _InfoView: View {
public var body: some View {
@State var isPresented: Bool = _showLoadingIndicator ?? false
return VStack {
Expand All @@ -86,6 +86,6 @@ extension InfoView: View {
struct InfoViewLibraryContent: LibraryContentProvider {
@LibraryContentBuilder
var views: [LibraryItem] {
LibraryItem(InfoView(title: "SAP BTP SDK for iOS", descriptionText: "SAP BTP SDK for iOS enables you to quickly develop your own native apps, with Swift. The SDK extends the standard Swift Apple iOS frameworks with the reusable UI components from the SAP Fiori for iOS Design Language, and provides APIs which seamlessly integrate apps with SAP BTP services.", showLoadingIndicator: false, loadingIndicatorText: "", action: _Action(actionText: "Primary Button"), secondaryAction: _Action(actionText: "Secondary Button")))
LibraryItem(_InfoView(title: "SAP BTP SDK for iOS", descriptionText: "SAP BTP SDK for iOS enables you to quickly develop your own native apps, with Swift. The SDK extends the standard Swift Apple iOS frameworks with the reusable UI components from the SAP Fiori for iOS Design Language, and provides APIs which seamlessly integrate apps with SAP BTP services.", showLoadingIndicator: false, loadingIndicatorText: "", action: _Action(actionText: "Primary Button"), secondaryAction: _Action(actionText: "Secondary Button")))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2015,6 +2015,77 @@ protocol _AuthenticationComponent: _DetailImageComponent, _TitleComponent, _Subt
var didSignIn: (() -> Void)? { get }
}

/// `InfoView` is a multifunctional view for displaying Information or Splash screen.
/// The UI elements can be displayed or hidden depending on functionality.
/// The text properties must be set before displaying the view.
///
/// ## Initialization Parameters
/// - Required:
/// - title: The primary heading text (AttributedString or ViewBuilder)
/// - Optional:
/// - descriptionText: Supplemental information text
/// - action: Primary action control
/// - secondaryAction: Secondary action control
/// - loadingIndicator: Loading state visualization
///
/// ## Usage
/// ## AttributedString Shortcut (Quick Setup)
/// ```
/// InfoView(
/// title: AttributedString("Title"),
/// descriptionText: AttributedString("Description Text"),
/// action: FioriButton(title: "Update Now") {
/// startUpdate()
/// },
/// secondaryAction: FioriButton(title: "Remind Later") {
/// scheduleReminder()
/// }
/// )
/// ```
///
/// ## ViewBuilder Approach (Fully Customizable)
/// ```
/// // Custom loading indicator with red circular style
/// let loadingIndicator = LoadingIndicator(
/// title: { Text("") },
/// progress: {
/// ProgressView()
/// .progressViewStyle(CircularProgressViewStyle(tint: .red))
/// },
/// isPresented: .constant(true)
/// )
///
/// InfoView(
/// title: {
/// HStack(spacing: 8) {
/// Image(systemName: "exclamationmark.triangle.fill")
/// .foregroundColor(.yellow)
/// Text("Title")
/// .font(.headline)
/// }
/// },
/// descriptionText: {
/// Text(AttributedString(self.model.descriptionText ?? "")) // Dynamic title from model
/// .foregroundColor(.blue) // Custom text color
/// },
/// action: {
/// Toggle("Trust Device", isOn: $trustDevice)
/// .toggleStyle(.switch)
/// },
/// secondaryAction: {
/// Button("Start Tutorial") {
/// print("InfoView secondary button clicked")
/// }
/// },
/// loadingIndicator: { loadingIndicator }
/// )
/// ```
// sourcery: CompositeComponent
protocol _InfoViewComponent: _TitleComponent, _DescriptionTextComponent, _ActionComponent, _SecondaryActionComponent {
// sourcery: @ViewBuilder
var loadingIndicator: LoadingIndicator? { get set }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this property be a view builder so that developers can customize it?

I think we should provide a default indicator as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the code based on all the suggestions and added UT. Thank you all @billzhou0223 @dyongxu @janhuachu

}

/// The Activation Screen is displayed after the Welcome Screen with title, description text, email input text field, action button and secondary action.
// sourcery: CompositeComponent
protocol _ActivationScreenComponent: _TitleComponent, _DescriptionTextComponent, _FootnoteComponent, _ActionComponent, _SecondaryActionComponent {
Expand Down
Loading
Loading