mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Better empty state for the following tabs
This commit is contained in:
parent
b81d2a33b6
commit
c6517ebb8e
23 changed files with 354 additions and 142 deletions
|
|
@ -0,0 +1,70 @@
|
|||
// swiftlint:disable line_length
|
||||
|
||||
import Foundation
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
import Views
|
||||
|
||||
public struct FollowingViewModal: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
let message: String = """
|
||||
We've created a new place for all your newsletters and feeds called Following. You can control the destination of
|
||||
new items by changing the destination for your subscriptions in the Subscriptions view of your settings. By default
|
||||
your existing newsletters will go into your library and your existing feeds will go into Following.
|
||||
|
||||
From the library you can swipe items left to right to move them into your library. In the reader view you can tap the
|
||||
bookmark icon on the toolbar to move items into your library.
|
||||
|
||||
If you don't need the following tab you can disable it from the filters view in your settings.
|
||||
|
||||
- [Learn more about the following](https://docs.omnivore.app/using/following.html)
|
||||
|
||||
- [Tell your friends about Omnivore](https://omnivore.app/about)
|
||||
|
||||
"""
|
||||
|
||||
var closeButton: some View {
|
||||
Button(action: {
|
||||
dismiss()
|
||||
}, label: {
|
||||
ZStack {
|
||||
Circle()
|
||||
.foregroundColor(Color.circleButtonBackground)
|
||||
.frame(width: 30, height: 30)
|
||||
|
||||
Image(systemName: "xmark")
|
||||
.resizable(resizingMode: Image.ResizingMode.stretch)
|
||||
.foregroundColor(Color.circleButtonForeground)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.font(Font.title.weight(.bold))
|
||||
.frame(width: 12, height: 12)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack {
|
||||
Text("Your new Following tab")
|
||||
.font(Font.system(size: 20, weight: .bold))
|
||||
Spacer()
|
||||
closeButton
|
||||
}
|
||||
.padding(.top, 16)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
List {
|
||||
Section {
|
||||
let parsedMessage = try? AttributedString(markdown: message,
|
||||
options: .init(interpretedSyntax: .inlineOnly))
|
||||
Text(parsedMessage ?? "")
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(Color.appGrayTextContrast)
|
||||
.accentColor(.blue)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,30 +79,73 @@ struct FiltersHeader: View {
|
|||
|
||||
struct EmptyState: View {
|
||||
@ObservedObject var viewModel: HomeFeedViewModel
|
||||
@EnvironmentObject var dataService: DataService
|
||||
|
||||
@State var showSendNewslettersAlert = false
|
||||
|
||||
var followingEmptyState: some View {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
if viewModel.stopUsingFollowingPrimer {
|
||||
VStack(spacing: 10) {
|
||||
Image.relaxedSlothLight
|
||||
Text("You are all caught up.").foregroundColor(Color.extensionTextSubtle)
|
||||
Button(action: {
|
||||
Task {
|
||||
await viewModel.loadItems(dataService: dataService, isRefresh: true)
|
||||
}
|
||||
}, label: { Text("Refresh").bold() })
|
||||
.foregroundColor(Color.blue)
|
||||
}
|
||||
} else {
|
||||
Text("You don't have any Feed items.")
|
||||
.font(Font.system(size: 18, weight: .bold))
|
||||
|
||||
Text("Add an RSS/Atom feed")
|
||||
.foregroundColor(Color.blue)
|
||||
.onTapGesture {
|
||||
viewModel.showAddFeedView = true
|
||||
}
|
||||
|
||||
Text("Send your newsletters to following")
|
||||
.foregroundColor(Color.blue)
|
||||
.onTapGesture {
|
||||
showSendNewslettersAlert = true
|
||||
}
|
||||
|
||||
Text("Hide the Following tab")
|
||||
.foregroundColor(Color.blue)
|
||||
.onTapGesture {
|
||||
viewModel.showHideFollowingAlert = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.frame(minHeight: 400)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
.alert("Update newsletter destination", isPresented: $showSendNewslettersAlert, actions: {
|
||||
Button(action: {
|
||||
Task {
|
||||
await viewModel.modifyingNewsletterDestinationToFollowing(dataService: dataService)
|
||||
}
|
||||
}, label: { Text("OK") })
|
||||
Button(LocalText.cancelGeneric, role: .cancel) { showSendNewslettersAlert = false }
|
||||
}, message: {
|
||||
// swiftlint:disable:next line_length
|
||||
Text("Your email address destination folders will be modified to send to this tab.\n\nAll new newsletters will appear here. You can modify the destination for each individual email address and subscription in your settings.")
|
||||
})
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if viewModel.currentFolder == "following" {
|
||||
if viewModel.isModifyingNewsletterDestination {
|
||||
return AnyView(
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
Text("You don't have any Feed items.")
|
||||
.font(Font.system(size: 18, weight: .bold))
|
||||
|
||||
Text("Add an RSS/Atom feed")
|
||||
.foregroundColor(Color.blue)
|
||||
.onTapGesture {
|
||||
viewModel.showAddFeedView = true
|
||||
}
|
||||
|
||||
Text("Hide the Following tab")
|
||||
.foregroundColor(Color.blue)
|
||||
.onTapGesture {
|
||||
viewModel.showHideFollowingAlert = true
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 400)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
VStack {
|
||||
Text("Modifying newsletter destinations...")
|
||||
ProgressView()
|
||||
}.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
)
|
||||
} else if viewModel.currentFolder == "following" {
|
||||
return AnyView(followingEmptyState)
|
||||
} else {
|
||||
return AnyView(Group {
|
||||
Spacer()
|
||||
|
|
@ -293,6 +336,11 @@ struct AnimatingCellHeight: AnimatableModifier {
|
|||
if viewModel.appliedFilter == nil {
|
||||
viewModel.setDefaultFilter()
|
||||
}
|
||||
// Once the user has seen at least one following item we stop displaying the
|
||||
// initial help view
|
||||
if viewModel.currentFolder == "following", viewModel.fetcher.items.count > 0 {
|
||||
viewModel.stopUsingFollowingPrimer = true
|
||||
}
|
||||
}
|
||||
.environment(\.editMode, self.$isEditMode)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,10 @@ import Views
|
|||
@State var lastMoreFetched: Date?
|
||||
@State var lastFiltersFetched: Date?
|
||||
|
||||
@State var isModifyingNewsletterDestination = false
|
||||
|
||||
@AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false
|
||||
@AppStorage(UserDefaultKey.stopUsingFollowingPrimer.rawValue) var stopUsingFollowingPrimer = false
|
||||
@AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false
|
||||
|
||||
@Published var appliedFilter: InternalFilter? {
|
||||
|
|
@ -325,4 +328,34 @@ import Views
|
|||
func findFilter(_: DataService, named: String) -> InternalFilter? {
|
||||
filters.first(where: { $0.name == named })
|
||||
}
|
||||
|
||||
func modifyingNewsletterDestinationToFollowing(dataService: DataService) async {
|
||||
isModifyingNewsletterDestination = true
|
||||
do {
|
||||
var errorCount = 0
|
||||
let objectIDs = try await dataService.newsletterEmails()
|
||||
let newsletters = await dataService.viewContext.perform {
|
||||
let newsletters = objectIDs.compactMap { dataService.viewContext.object(with: $0) as? NewsletterEmail }
|
||||
return newsletters
|
||||
}
|
||||
|
||||
for newsletter in newsletters {
|
||||
if let emailId = newsletter.emailId, newsletter.folder != "following" {
|
||||
do {
|
||||
try await dataService.updateNewsletterEmail(emailID: emailId, folder: "following")
|
||||
} catch {
|
||||
print("error updating newsletter: ", error)
|
||||
errorCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if errorCount > 0 {
|
||||
snackbar("There was an error modifying \(errorCount) of your emails")
|
||||
} else {
|
||||
snackbar("Email destination modified")
|
||||
}
|
||||
} catch {
|
||||
snackbar("Error modifying emails")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ struct LibraryTabView: View {
|
|||
|
||||
@AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false
|
||||
@AppStorage(UserDefaultKey.lastSelectedTabItem.rawValue) var selectedTab = "inbox"
|
||||
@AppStorage(UserDefaultKey.followingPrimerDisplayed.rawValue) var followingPrimerDisplayed = false
|
||||
|
||||
@State var showFollowingPrimer = false
|
||||
@State var showExpandedAudioPlayer = false
|
||||
|
||||
private let syncManager = LibrarySyncManager()
|
||||
|
|
@ -74,14 +71,6 @@ struct LibraryTabView: View {
|
|||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
if showFollowingPrimer {
|
||||
PresentationLink(transition: UIDevice.isIPad ? .popover : .sheet(detents: [.medium]), isPresented: $showFollowingPrimer) {
|
||||
FollowingViewModal()
|
||||
} label: {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
TabView(selection: $selectedTab) {
|
||||
if !hideFollowingTab {
|
||||
NavigationView {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public enum UserDefaultKey: String {
|
|||
case recentSearchTerms
|
||||
case audioPlayerExpanded
|
||||
case themeName
|
||||
case followingPrimerDisplayed
|
||||
case stopUsingFollowingPrimer
|
||||
case notificationsEnabled
|
||||
case deviceTokenID
|
||||
case userWordsPerMinute
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ public extension Image {
|
|||
static var readerSettings: Image { Image("reader-settings", bundle: .module) }
|
||||
static var utilityMenu: Image { Image("utility-menu", bundle: .module) }
|
||||
|
||||
static var relaxedSlothLight: Image {
|
||||
Color.isDarkMode ? Image("relaxed-sloth-dark", bundle: .module) : Image("relaxed-sloth-light", bundle: .module)
|
||||
}
|
||||
|
||||
static var addLink: Image { Image("add-link", bundle: .module) }
|
||||
static var selectMultiple: Image { Image("select-multiple", bundle: .module) }
|
||||
static var magnifyingGlass: Image { Image("magnifying-glass", bundle: .module) }
|
||||
|
|
|
|||
23
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/Contents.json
vendored
Normal file
23
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "relaxed-sloth-dark.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "sloth 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "sloth 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/sloth 1.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/sloth 1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/sloth 2.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-dark.imageset/sloth 2.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "sloth 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "sloth 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "sloth 3.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 1.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 2.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 2.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 3.png
vendored
Normal file
BIN
apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/relaxed-sloth-light.imageset/sloth 3.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -39,8 +39,8 @@
|
|||
"@opentelemetry/tracing": "^0.24.0",
|
||||
"@sendgrid/mail": "^7.6.0",
|
||||
"@sentry/integrations": "^7.10.0",
|
||||
"@sentry/node": "^5.26.0",
|
||||
"@sentry/tracing": "^7.9.0",
|
||||
"@sentry/node": "^6.10.0",
|
||||
"@sentry/tracing": "^7.10.0",
|
||||
"addressparser": "^1.0.1",
|
||||
"analytics-node": "^6.0.0",
|
||||
"apollo-datasource": "^3.3.1",
|
||||
|
|
@ -151,4 +151,4 @@
|
|||
"volta": {
|
||||
"extends": "../../package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ export function makeApolloServer(): ApolloServer {
|
|||
context: contextFunc,
|
||||
formatError: (err) => {
|
||||
logger.info('server error', err)
|
||||
Sentry.captureException(err)
|
||||
// Sentry.captureException(err)
|
||||
// hide error messages from frontend on prod
|
||||
return new Error('Unexpected server error')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@ import { env } from './env'
|
|||
import * as Sentry from '@sentry/node'
|
||||
import { CaptureConsole } from '@sentry/integrations'
|
||||
|
||||
export const sentryConfig = {
|
||||
dsn: env.sentry.dsn,
|
||||
environment: env.server.apiEnv,
|
||||
// Don't bother collecting and sending events locally (reduces overhead).
|
||||
enabled: !env.dev.isLocal,
|
||||
serverName: process.env.GAE_INSTANCE || '',
|
||||
integrations: [
|
||||
new Sentry.Integrations.OnUncaughtException(),
|
||||
new CaptureConsole({ levels: ['error'] }),
|
||||
],
|
||||
debug: (env.dev.isLocal && process.env.DEBUG === 'true') || false,
|
||||
// We recommend adjusting this value in production, or using tracesSampler
|
||||
// for finer control
|
||||
tracesSampleRate: 0,
|
||||
}
|
||||
// export const sentryConfig = {
|
||||
// dsn: env.sentry.dsn,
|
||||
// environment: env.server.apiEnv,
|
||||
// // Don't bother collecting and sending events locally (reduces overhead).
|
||||
// enabled: !env.dev.isLocal,
|
||||
// serverName: process.env.GAE_INSTANCE || '',
|
||||
// integrations: [
|
||||
// new Sentry.Integrations.OnUncaughtException(),
|
||||
// new CaptureConsole({ levels: ['error'] }),
|
||||
// ],
|
||||
// debug: (env.dev.isLocal && process.env.DEBUG === 'true') || false,
|
||||
// // We recommend adjusting this value in production, or using tracesSampler
|
||||
// // for finer control
|
||||
// tracesSampleRate: 0,
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import { userServiceRouter } from './routers/svc/user'
|
|||
import { webhooksServiceRouter } from './routers/svc/webhooks'
|
||||
import { textToSpeechRouter } from './routers/text_to_speech'
|
||||
import { userRouter } from './routers/user_router'
|
||||
import { sentryConfig } from './sentry'
|
||||
// import { sentryConfig } from './sentry'
|
||||
import {
|
||||
getClaimsByToken,
|
||||
getTokenByRequest,
|
||||
|
|
@ -56,9 +56,9 @@ export const createApp = (): {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
Sentry.init(sentryConfig)
|
||||
app.use(Sentry.Handlers.requestHandler())
|
||||
app.use(Sentry.Handlers.tracingHandler())
|
||||
// Sentry.init(sentryConfig)
|
||||
// app.use(Sentry.Handlers.requestHandler())
|
||||
// app.use(Sentry.Handlers.tracingHandler())
|
||||
|
||||
app.use(cookieParser())
|
||||
app.use(json({ limit: '100mb' }))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export type SuggestionAction = {
|
|||
type SuggestionBoxProps = {
|
||||
helpMessage: string
|
||||
suggestions: SuggestionAction[]
|
||||
isError: boolean
|
||||
|
||||
size?: 'large' | 'small'
|
||||
background?: string
|
||||
|
|
@ -25,6 +26,7 @@ type SuggestionBoxProps = {
|
|||
type InternalOrExternalLinkProps = {
|
||||
link: string
|
||||
children: ReactNode
|
||||
showArrow: boolean
|
||||
}
|
||||
|
||||
const InternalOrExternalLink = (props: InternalOrExternalLinkProps) => {
|
||||
|
|
@ -40,14 +42,16 @@ const InternalOrExternalLink = (props: InternalOrExternalLinkProps) => {
|
|||
}}
|
||||
>
|
||||
{!isExternal ? (
|
||||
<Link href={props.link} legacyBehavior>{props.children}</Link>
|
||||
<Link href={props.link} legacyBehavior>
|
||||
{props.children}
|
||||
</Link>
|
||||
) : (
|
||||
<a href={props.link} target="_blank" rel="noreferrer">
|
||||
{props.children}
|
||||
</a>
|
||||
)}
|
||||
</SpanBox>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export const SuggestionBox = (props: SuggestionBoxProps) => {
|
||||
|
|
@ -59,14 +63,13 @@ export const SuggestionBox = (props: SuggestionBoxProps) => {
|
|||
flexDirection: props.size == 'large' ? 'column' : 'row',
|
||||
width: 'fit-content',
|
||||
borderRadius: '5px',
|
||||
background: props.background ?? '$thBackground3',
|
||||
fontSize: '15px',
|
||||
fontSize: '18px',
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
color: '$thTextContrast',
|
||||
color: '$grayText',
|
||||
px: '15px',
|
||||
py: props.size == 'large' ? '15px' : '10px',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'@smDown': {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
|
|
@ -74,7 +77,7 @@ export const SuggestionBox = (props: SuggestionBoxProps) => {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<VStack>
|
||||
<VStack css={{ alignItems: 'center' }}>
|
||||
{props.dismissible && (
|
||||
<SpanBox
|
||||
css={{
|
||||
|
|
@ -99,12 +102,13 @@ export const SuggestionBox = (props: SuggestionBoxProps) => {
|
|||
<InternalOrExternalLink
|
||||
key={`suggestions-${idx}`}
|
||||
link={suggestion.url}
|
||||
showArrow={!props.isError}
|
||||
>
|
||||
<SpanBox
|
||||
css={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: '$omnivoreCtaYellow',
|
||||
// color: '$omnivoreCtaYellow',
|
||||
pt: '15px',
|
||||
gap: '2px',
|
||||
'&:hover': {
|
||||
|
|
@ -113,10 +117,12 @@ export const SuggestionBox = (props: SuggestionBoxProps) => {
|
|||
}}
|
||||
>
|
||||
<>{suggestion.text}</>
|
||||
<ArrowRightIcon
|
||||
size={25}
|
||||
color={theme.colors.omnivoreCtaYellow.toString()}
|
||||
/>
|
||||
{props.showArrow && (
|
||||
<ArrowRightIcon
|
||||
size={25}
|
||||
color={theme.colors.omnivoreCtaYellow.toString()}
|
||||
/>
|
||||
)}
|
||||
</SpanBox>
|
||||
</InternalOrExternalLink>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import { useMemo } from 'react'
|
|||
import { LIBRARY_LEFT_MENU_WIDTH } from './LibraryFilterMenu'
|
||||
import { LayoutType } from './HomeFeedContainer'
|
||||
import { SuggestionBox, SuggestionAction } from '../../elements/SuggestionBox'
|
||||
import { ErrorSlothIcon } from '../../elements/icons/ErrorSlothIcon'
|
||||
|
||||
type EmptyLibraryProps = {
|
||||
isError: boolean
|
||||
searchTerm: string | undefined
|
||||
onAddLinkClicked: () => void
|
||||
|
||||
|
|
@ -26,10 +28,14 @@ type MessageType =
|
|||
|
||||
type HelpMessageProps = {
|
||||
type: MessageType
|
||||
isError: boolean
|
||||
}
|
||||
|
||||
export const ErrorBox = (props: HelpMessageProps) => {
|
||||
const errorTitle = useMemo(() => {
|
||||
if (props.isError) {
|
||||
return 'An error occurred'
|
||||
}
|
||||
switch (props.type) {
|
||||
case 'inbox':
|
||||
return 'Your inbox is empty. The inbox will contain all your non-archived saved items.'
|
||||
|
|
@ -60,12 +66,10 @@ export const ErrorBox = (props: HelpMessageProps) => {
|
|||
css={{
|
||||
width: 'fit-content',
|
||||
borderRadius: '5px',
|
||||
background: 'rgba(255, 59, 48, 0.3)',
|
||||
fontSize: '15px',
|
||||
fontSize: '25px',
|
||||
fontFamily: '$inter',
|
||||
fontWeight: '500',
|
||||
fontWeight: '700',
|
||||
color: '$thTextContrast',
|
||||
py: '10px',
|
||||
px: '15px',
|
||||
'@smDown': {
|
||||
width: '100%',
|
||||
|
|
@ -73,6 +77,7 @@ export const ErrorBox = (props: HelpMessageProps) => {
|
|||
'@xlgDown': {
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{errorTitle}
|
||||
|
|
@ -87,6 +92,17 @@ type SuggestionMessage = {
|
|||
|
||||
export const Suggestion = (props: HelpMessageProps) => {
|
||||
const helpMessage = useMemo<SuggestionMessage>(() => {
|
||||
if (props.isError) {
|
||||
return {
|
||||
message: 'Something went wrong searching your library',
|
||||
actions: [
|
||||
{
|
||||
text: 'Join our Discord to get help',
|
||||
url: 'https://discord.gg/h2z5rppzz9',
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
switch (props.type) {
|
||||
case 'feed':
|
||||
return {
|
||||
|
|
@ -151,6 +167,7 @@ export const Suggestion = (props: HelpMessageProps) => {
|
|||
<>
|
||||
{helpMessage ? (
|
||||
<SuggestionBox
|
||||
isError={props.isError}
|
||||
helpMessage={helpMessage.message}
|
||||
suggestions={helpMessage.actions}
|
||||
/>
|
||||
|
|
@ -194,13 +211,16 @@ export const EmptyLibrary = (props: EmptyLibraryProps) => {
|
|||
<Box
|
||||
css={{
|
||||
display: 'inline-flex',
|
||||
color: '$grayTextContrast',
|
||||
color: '$thTextSubtle2',
|
||||
gap: '10px',
|
||||
pl: '0px',
|
||||
pt: '30px',
|
||||
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
|
||||
alignItems: 'center',
|
||||
|
||||
'@media (max-width: 768px)': {
|
||||
p: '15px',
|
||||
},
|
||||
|
|
@ -223,8 +243,9 @@ export const EmptyLibrary = (props: EmptyLibraryProps) => {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<ErrorBox type={type} />
|
||||
<Suggestion type={type} />
|
||||
{props.isError && <ErrorSlothIcon />}
|
||||
<ErrorBox type={type} isError={props.isError} />
|
||||
<Suggestion type={type} isError={props.isError} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
size,
|
||||
setSize,
|
||||
isValidating,
|
||||
itemsDataError,
|
||||
performActionOnItem,
|
||||
mutate,
|
||||
} = useGetLibraryItemsQuery(queryInputs)
|
||||
|
|
@ -128,6 +129,12 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
}
|
||||
}, [mutate])
|
||||
|
||||
useMemo(() => {
|
||||
if (itemsDataError) {
|
||||
console.log('search error: ', itemsDataError)
|
||||
}
|
||||
}, [itemsDataError])
|
||||
|
||||
useEffect(() => {
|
||||
if (queryValue.startsWith('#')) {
|
||||
debouncedFetchSearchResults(
|
||||
|
|
@ -854,6 +861,7 @@ export function HomeFeedContainer(): JSX.Element {
|
|||
hasMore={hasMore}
|
||||
hasData={!!itemsPages}
|
||||
totalItems={itemsPages?.[0].search.pageInfo.totalCount || 0}
|
||||
isError={!!itemsDataError}
|
||||
isValidating={isValidating}
|
||||
labelsTarget={labelsTarget}
|
||||
setLabelsTarget={setLabelsTarget}
|
||||
|
|
@ -888,6 +896,7 @@ type HomeFeedContentProps = {
|
|||
hasMore: boolean
|
||||
hasData: boolean
|
||||
totalItems: number
|
||||
isError: boolean
|
||||
isValidating: boolean
|
||||
loadMore: () => void
|
||||
labelsTarget: LibraryItem | undefined
|
||||
|
|
@ -1064,6 +1073,7 @@ function LibraryItemsLayout(props: LibraryItemsLayoutProps): JSX.Element {
|
|||
>
|
||||
{!props.isValidating && props.items.length == 0 ? (
|
||||
<EmptyLibrary
|
||||
isError={props.isError}
|
||||
layoutType={props.layout}
|
||||
searchTerm={props.searchTerm}
|
||||
onAddLinkClicked={() => {
|
||||
|
|
|
|||
|
|
@ -246,6 +246,16 @@ export function useGetLibraryItemsQuery({
|
|||
},
|
||||
{ revalidateFirstPage: false }
|
||||
)
|
||||
console.log(
|
||||
'data: ',
|
||||
data,
|
||||
'size',
|
||||
size,
|
||||
'isValidating',
|
||||
isValidating,
|
||||
'error',
|
||||
error
|
||||
)
|
||||
|
||||
let responseError = error
|
||||
let responsePages = data as LibraryItemsData[] | undefined
|
||||
|
|
|
|||
106
yarn.lock
106
yarn.lock
|
|
@ -5858,6 +5858,15 @@
|
|||
"@sentry/types" "7.77.0"
|
||||
"@sentry/utils" "7.77.0"
|
||||
|
||||
"@sentry-internal/tracing@7.91.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.91.0.tgz#fbb6e1e3383e1eeee08633384e004da73ac1c37d"
|
||||
integrity sha512-JH5y6gs6BS0its7WF2DhySu7nkhPDfZcdpAXldxzIlJpqFkuwQKLU5nkYJpiIyZz1NHYYtW5aum2bV2oCOdDRA==
|
||||
dependencies:
|
||||
"@sentry/core" "7.91.0"
|
||||
"@sentry/types" "7.91.0"
|
||||
"@sentry/utils" "7.91.0"
|
||||
|
||||
"@sentry/browser@7.50.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.50.0.tgz#16c995c336322c8aec65570f90f50288678004ec"
|
||||
|
|
@ -5882,17 +5891,6 @@
|
|||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@sentry/core@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
|
||||
integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/minimal" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@7.50.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.50.0.tgz#88bc9cbfc0cb429a28489ece6f0be7a7006436c4"
|
||||
|
|
@ -5910,14 +5908,13 @@
|
|||
"@sentry/types" "7.77.0"
|
||||
"@sentry/utils" "7.77.0"
|
||||
|
||||
"@sentry/hub@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
|
||||
integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==
|
||||
"@sentry/core@7.91.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.91.0.tgz#229334d7f03dd5d90a17495e61ce4215ab730b2a"
|
||||
integrity sha512-tu+gYq4JrTdrR+YSh5IVHF0fJi/Pi9y0HZ5H9HnYy+UMcXIotxf6hIEaC6ZKGeLWkGXffz2gKpQLe/g6vy/lPA==
|
||||
dependencies:
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/types" "7.91.0"
|
||||
"@sentry/utils" "7.91.0"
|
||||
|
||||
"@sentry/integrations@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -5939,15 +5936,6 @@
|
|||
"@sentry/utils" "7.77.0"
|
||||
localforage "^1.8.1"
|
||||
|
||||
"@sentry/minimal@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b"
|
||||
integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/nextjs@^7.42.0":
|
||||
version "7.50.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.50.0.tgz#b9e7727c8f974644bb84a01f40a0adedb44ec416"
|
||||
|
|
@ -5991,20 +5979,16 @@
|
|||
"@sentry/utils" "7.77.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
|
||||
"@sentry/node@^5.26.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48"
|
||||
integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==
|
||||
"@sentry/node@^7.10.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.91.0.tgz#26bf13c3daf988f9725afd1a3cc38ba2ff90d62a"
|
||||
integrity sha512-hTIfSQxD7L+AKIqyjoq8CWBRkEQrrMZmA3GSZgPI5JFWBHgO0HBo5TH/8TU81oEJh6kqqHAl2ObMhmcnaFqlzg==
|
||||
dependencies:
|
||||
"@sentry/core" "5.30.0"
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/tracing" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
cookie "^0.4.1"
|
||||
"@sentry-internal/tracing" "7.91.0"
|
||||
"@sentry/core" "7.91.0"
|
||||
"@sentry/types" "7.91.0"
|
||||
"@sentry/utils" "7.91.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -6038,28 +6022,12 @@
|
|||
"@types/aws-lambda" "^8.10.62"
|
||||
"@types/express" "^4.17.14"
|
||||
|
||||
"@sentry/tracing@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f"
|
||||
integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==
|
||||
"@sentry/tracing@^7.10.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.91.0.tgz#2019542a9ed991c04a1f5e685a245b0fb8d69be1"
|
||||
integrity sha512-IlSAMvqfCL/2TwwN4Tmk6bGMgilGruv5oIJ1GMenVZk53bHwjpjzMbd0ms8+S5zJwAgTQXoCbRhaFFrNmptteQ==
|
||||
dependencies:
|
||||
"@sentry/hub" "5.30.0"
|
||||
"@sentry/minimal" "5.30.0"
|
||||
"@sentry/types" "5.30.0"
|
||||
"@sentry/utils" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@^7.9.0":
|
||||
version "7.77.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.77.0.tgz#39d7c30834f503fe9eb20ce1c8c8bd28f7d7c9ce"
|
||||
integrity sha512-zr6eSCW3NJ124uj4Fy/6hh77cziy43dpYE1WpgvO/yhl1+kdrY2XVJ0bXGqwHU0KBm/eSgHD7yecUxmZUXiabA==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.77.0"
|
||||
|
||||
"@sentry/types@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
|
||||
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
|
||||
"@sentry-internal/tracing" "7.91.0"
|
||||
|
||||
"@sentry/types@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -6071,13 +6039,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.77.0.tgz#c5d00fe547b89ccde59cdea59143bf145cee3144"
|
||||
integrity sha512-nfb00XRJVi0QpDHg+JkqrmEBHsqBnxJu191Ded+Cs1OJ5oPXEW6F59LVcBScGvMqe+WEk1a73eH8XezwfgrTsA==
|
||||
|
||||
"@sentry/utils@5.30.0":
|
||||
version "5.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
|
||||
integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==
|
||||
dependencies:
|
||||
"@sentry/types" "5.30.0"
|
||||
tslib "^1.9.3"
|
||||
"@sentry/types@7.91.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.91.0.tgz#5b68954e08986fecb0d4bef168df58eef62c32c7"
|
||||
integrity sha512-bcQnb7J3P3equbCUc+sPuHog2Y47yGD2sCkzmnZBjvBT0Z1B4f36fI/5WjyZhTjLSiOdg3F2otwvikbMjmBDew==
|
||||
|
||||
"@sentry/utils@7.50.0":
|
||||
version "7.50.0"
|
||||
|
|
@ -6094,6 +6059,13 @@
|
|||
dependencies:
|
||||
"@sentry/types" "7.77.0"
|
||||
|
||||
"@sentry/utils@7.91.0":
|
||||
version "7.91.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.91.0.tgz#3b1a94c053c885877908cd3e1365e3d23e21a73f"
|
||||
integrity sha512-fvxjrEbk6T6Otu++Ax9ntlQ0sGRiwSC179w68aC3u26Wr30FAIRKqHTCCdc2jyWk7Gd9uWRT/cq+g8NG/8BfSg==
|
||||
dependencies:
|
||||
"@sentry/types" "7.91.0"
|
||||
|
||||
"@sentry/webpack-plugin@1.20.0":
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
|
||||
|
|
|
|||
Loading…
Reference in a new issue