From 039f786c160623d082f30ce3a33791ebc518b66d Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sat, 7 Jan 2023 18:01:06 +0100 Subject: [PATCH] Error state UI --- .../List/ConversationsListView.swift | 10 ++++- .../List/ConversationsListViewModel.swift | 2 + .../DesignSystem/Views/ErrorView.swift | 40 +++++++++++++++++++ .../Notifications/NotificationsListView.swift | 10 ++++- .../Status/Detail/StatusDetailVIew.swift | 11 +++-- .../Status/List/StatusesListView.swift | 11 ++++- .../Status/Row/StatusRowContextMenu.swift | 2 + 7 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 Packages/DesignSystem/Sources/DesignSystem/Views/ErrorView.swift diff --git a/Packages/Conversations/Sources/Conversations/List/ConversationsListView.swift b/Packages/Conversations/Sources/Conversations/List/ConversationsListView.swift index dcbf38aa..60a88050 100644 --- a/Packages/Conversations/Sources/Conversations/List/ConversationsListView.swift +++ b/Packages/Conversations/Sources/Conversations/List/ConversationsListView.swift @@ -38,10 +38,18 @@ public struct ConversationsListView: View { } Divider() } - } else if conversations.isEmpty && !viewModel.isLoadingFirstPage { + } else if conversations.isEmpty && !viewModel.isLoadingFirstPage && !viewModel.isError { EmptyView(iconName: "tray", title: "Inbox Zero", message: "Looking for some social media love? You'll find all your direct messages and private mentions right here. Happy messaging! 📱❤️") + } else if viewModel.isError { + ErrorView(title: "An error occurred", + message: "Error while loading your messages", + buttonTitle: "Retry") { + Task { + await viewModel.fetchConversations() + } + } } } .padding(.top, .layoutPadding) diff --git a/Packages/Conversations/Sources/Conversations/List/ConversationsListViewModel.swift b/Packages/Conversations/Sources/Conversations/List/ConversationsListViewModel.swift index 9183a406..2727696d 100644 --- a/Packages/Conversations/Sources/Conversations/List/ConversationsListViewModel.swift +++ b/Packages/Conversations/Sources/Conversations/List/ConversationsListViewModel.swift @@ -8,6 +8,7 @@ class ConversationsListViewModel: ObservableObject { @Published var isLoadingFirstPage: Bool = true @Published var conversations: [Conversation] = [] + @Published var isError: Bool = false public init() { } @@ -20,6 +21,7 @@ class ConversationsListViewModel: ObservableObject { conversations = try await client.get(endpoint: Conversations.conversations) isLoadingFirstPage = false } catch { + isError = true isLoadingFirstPage = false } } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Views/ErrorView.swift b/Packages/DesignSystem/Sources/DesignSystem/Views/ErrorView.swift new file mode 100644 index 00000000..998afeb7 --- /dev/null +++ b/Packages/DesignSystem/Sources/DesignSystem/Views/ErrorView.swift @@ -0,0 +1,40 @@ +import SwiftUI + +public struct ErrorView: View { + public let title: String + public let message: String + public let buttonTitle: String + public let onButtonPress: (() -> Void) + + public init(title: String, message: String, buttonTitle: String, onButtonPress: @escaping (() -> Void)) { + self.title = title + self.message = message + self.buttonTitle = buttonTitle + self.onButtonPress = onButtonPress + } + + public var body: some View { + VStack { + Image(systemName: "exclamationmark.triangle.fill") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxHeight: 50) + Text(title) + .font(.title) + .padding(.top, 16) + Text(message) + .font(.subheadline) + .multilineTextAlignment(.center) + .foregroundColor(.gray) + Button { + onButtonPress() + } label: { + Text(buttonTitle) + } + .buttonStyle(.bordered) + .padding(.top, 16) + } + .padding(.top, 100) + .padding(.layoutPadding) + } +} diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index 9e56eb49..5d015f8d 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -102,8 +102,14 @@ public struct NotificationsListView: View { loadingRow } - case let .error(error): - Text(error.localizedDescription) + case .error: + ErrorView(title: "An error occured", + message: "An error occured while loading your notifications, please retry.", + buttonTitle: "Retry") { + Task { + await viewModel.fetchNotifications() + } + } } } diff --git a/Packages/Status/Sources/Status/Detail/StatusDetailVIew.swift b/Packages/Status/Sources/Status/Detail/StatusDetailVIew.swift index c3c8848e..f6f7c563 100644 --- a/Packages/Status/Sources/Status/Detail/StatusDetailVIew.swift +++ b/Packages/Status/Sources/Status/Detail/StatusDetailVIew.swift @@ -61,9 +61,14 @@ public struct StatusDetailView: View { } } - case let .error(error): - Text(error.localizedDescription) - .padding(.horizontal, .layoutPadding) + case .error: + ErrorView(title: "An error occured", + message: "An error occured while this post context, please try again.", + buttonTitle: "Retry") { + Task { + await viewModel.fetch() + } + } } } .padding(.top, .layoutPadding) diff --git a/Packages/Status/Sources/Status/List/StatusesListView.swift b/Packages/Status/Sources/Status/List/StatusesListView.swift index b1d3b7d8..4349d8a4 100644 --- a/Packages/Status/Sources/Status/List/StatusesListView.swift +++ b/Packages/Status/Sources/Status/List/StatusesListView.swift @@ -24,8 +24,15 @@ public struct StatusesListView: View where Fetcher: StatusesFetcher { Divider() .padding(.vertical, .dividerPadding) } - case let .error(error): - Text(error.localizedDescription) + case .error: + ErrorView(title: "An error occured", + message: "An error occured while loading posts, please try again.", + buttonTitle: "Retry") { + Task { + await fetcher.fetchStatuses() + } + } + case let .display(statuses, nextPageState): ForEach(statuses, id: \.viewId) { status in StatusRowView(viewModel: .init(status: status, isCompact: false, isRemote: isRemote)) diff --git a/Packages/Status/Sources/Status/Row/StatusRowContextMenu.swift b/Packages/Status/Sources/Status/Row/StatusRowContextMenu.swift index a8698c04..0f255fe1 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowContextMenu.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowContextMenu.swift @@ -38,6 +38,8 @@ struct StatusRowContextMenu: View { } } + Divider() + if let url = viewModel.status.reblog?.url ?? viewModel.status.url { ShareLink(item: url) { Label("Share this post", systemImage: "square.and.arrow.up")