feat: new payment (#4629)
* feat: new payment * update * update * update * update * feat: enhance plan feature formatting and update localization strings * update * update * update * update * refactor: remove NeedActivationToast and related activation modal logic * update * feat(settings): guard paid settings Centralized paid-level metadata in @follow/shared and exposed the SettingNamespace union. Updated the builder helpers and every settings tab to pull paid badges from that shared map. Hardened the atoms helper to block writes and surface default values when a user lacks the required role. Added the necessary workspace dependencies for the atoms package. * fix: types * feat(rate-limit-notice): update error messaging and add upgrade prompt * feat(settings): integrate review checks into settings components * update * fix(plan): correct priority calculation in setting page data * feat(ai-summary): add upgrade suggestion for AI summary when plan is insufficient * feat(settings): enhance PaidBadge component to conditionally render based on MAS review status --------- Co-authored-by: DIYgod <i@diygod.me>
This commit is contained in:
parent
d255d7d6a2
commit
53e3578af2
|
|
@ -16,6 +16,10 @@ export const [, , useServerConfigs, , getServerConfigs, setServerConfigs] = crea
|
|||
),
|
||||
)
|
||||
|
||||
export type ServerConfigs = ExtractResponseData<GetStatusConfigsResponse>
|
||||
export type PaymentPlan = ServerConfigs["PAYMENT_PLAN_LIST"][number]
|
||||
export type PaymentFeature = PaymentPlan["limit"]
|
||||
|
||||
export const useIsInMASReview = () => {
|
||||
const serverConfigs = useServerConfigs()
|
||||
return (
|
||||
|
|
@ -24,3 +28,12 @@ export const useIsInMASReview = () => {
|
|||
serverConfigs?.MAS_IN_REVIEW_VERSION === PKG.version
|
||||
)
|
||||
}
|
||||
|
||||
export const getIsInMASReview = () => {
|
||||
const serverConfigs = getServerConfigs()
|
||||
return (
|
||||
typeof process !== "undefined" &&
|
||||
process.mas &&
|
||||
serverConfigs?.MAS_IN_REVIEW_VERSION === PKG.version
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import { AutoResizeHeight } from "@follow/components/ui/auto-resize-height/index.js"
|
||||
import { MotionButtonBase } from "@follow/components/ui/button/index.js"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { isNeedUpgradeError } from "@follow-app/client-sdk"
|
||||
import type { ReactNode } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useIsInMASReview } from "~/atoms/server-configs"
|
||||
import { CopyButton } from "~/components/ui/button/CopyButton"
|
||||
import { Markdown } from "~/components/ui/markdown/Markdown"
|
||||
import { useFeature } from "~/hooks/biz/useFeature"
|
||||
import { useSettingModal } from "~/modules/settings/modal/useSettingModal"
|
||||
|
||||
interface AISummaryCardBaseProps {
|
||||
/** Summary content to display */
|
||||
|
|
@ -21,8 +24,6 @@ interface AISummaryCardBaseProps {
|
|||
footerContent?: ReactNode
|
||||
/** Custom loading state component */
|
||||
loadingComponent?: ReactNode
|
||||
/** Custom empty state component */
|
||||
emptyComponent?: ReactNode
|
||||
/** Title text for the AI Summary header */
|
||||
title?: string
|
||||
/** Whether to show the copy button */
|
||||
|
|
@ -31,6 +32,8 @@ interface AISummaryCardBaseProps {
|
|||
showAskAIButton?: boolean
|
||||
/** Callback when Ask AI button is clicked */
|
||||
onAskAI?: () => void
|
||||
/** Error code returned when requesting the summary */
|
||||
errorCode?: number
|
||||
}
|
||||
|
||||
const DefaultLoadingState = () => (
|
||||
|
|
@ -41,12 +44,24 @@ const DefaultLoadingState = () => (
|
|||
</div>
|
||||
)
|
||||
|
||||
const DefaultEmptyState = ({ message }: { message: string }) => (
|
||||
<div className="py-4 text-center">
|
||||
<i className="i-mingcute-document-line mb-2 text-2xl text-text-tertiary" />
|
||||
<p className="text-sm text-text-secondary">{message}</p>
|
||||
</div>
|
||||
)
|
||||
const DefaultEmptyState = ({
|
||||
message,
|
||||
shouldSuggestUpgrade,
|
||||
}: {
|
||||
message: string
|
||||
shouldSuggestUpgrade?: boolean
|
||||
}) => {
|
||||
const settingModalPresent = useSettingModal()
|
||||
|
||||
return (
|
||||
<div
|
||||
className="text-center"
|
||||
onClick={shouldSuggestUpgrade ? () => settingModalPresent("plan") : undefined}
|
||||
>
|
||||
<p className="text-sm text-text-secondary">{message}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const AISummaryCardBase: React.FC<AISummaryCardBaseProps> = ({
|
||||
content,
|
||||
|
|
@ -55,16 +70,20 @@ export const AISummaryCardBase: React.FC<AISummaryCardBaseProps> = ({
|
|||
headerContent,
|
||||
footerContent,
|
||||
loadingComponent,
|
||||
emptyComponent,
|
||||
title = "AI Summary",
|
||||
showCopyButton = true,
|
||||
showAskAIButton = false,
|
||||
onAskAI,
|
||||
errorCode,
|
||||
}) => {
|
||||
const { t } = useTranslation("app")
|
||||
const aiEnabled = useFeature("ai")
|
||||
const isInMASReview = useIsInMASReview()
|
||||
|
||||
const hasContent = !isLoading && content
|
||||
const normalizedErrorCode = typeof errorCode === "number" ? errorCode : undefined
|
||||
const shouldSuggestUpgrade =
|
||||
normalizedErrorCode !== undefined && !isInMASReview && isNeedUpgradeError(normalizedErrorCode)
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -167,12 +186,16 @@ export const AISummaryCardBase: React.FC<AISummaryCardBaseProps> = ({
|
|||
loadingComponent || <DefaultLoadingState />
|
||||
) : hasContent ? (
|
||||
<Markdown className="prose-sm max-w-none prose-p:m-0">{String(content)}</Markdown>
|
||||
) : shouldSuggestUpgrade ? (
|
||||
<DefaultEmptyState
|
||||
message={t("ai.summary_upgrade_required_title")}
|
||||
shouldSuggestUpgrade
|
||||
/>
|
||||
) : (
|
||||
emptyComponent || <DefaultEmptyState message={t("ai.summary_not_available")} />
|
||||
<DefaultEmptyState message={t("ai.summary_not_available")} />
|
||||
)}
|
||||
</AutoResizeHeight>
|
||||
|
||||
{/* Footer */}
|
||||
{footerContent}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -357,7 +357,6 @@ export const useEntryActions = ({ entryId, view }: { entryId: string; view: Feed
|
|||
view,
|
||||
),
|
||||
active: isShowAISummaryOnce,
|
||||
disabled: userRole === UserRole.Free || userRole === UserRole.Trial,
|
||||
entryId,
|
||||
}),
|
||||
new EntryActionMenuItem({
|
||||
|
|
|
|||
|
|
@ -1,63 +1,17 @@
|
|||
import { UserRole } from "@follow/constants"
|
||||
import { getFeedByIdOrUrl } from "@follow/store/feed/getter"
|
||||
import { getSubscriptionByFeedId } from "@follow/store/subscription/getter"
|
||||
import {
|
||||
useFeedSubscriptionCount,
|
||||
useListSubscriptionCount,
|
||||
} from "@follow/store/subscription/hooks"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { t } from "i18next"
|
||||
import { useCallback } from "react"
|
||||
import { useNavigate } from "react-router"
|
||||
import { withoutTrailingSlash, withTrailingSlash } from "ufo"
|
||||
import { useEventCallback } from "usehooks-ts"
|
||||
|
||||
import { previewBackPath } from "~/atoms/preview"
|
||||
import { useServerConfigs } from "~/atoms/server-configs"
|
||||
import { useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import { CustomSafeError } from "~/errors/CustomSafeError"
|
||||
import { useActivationModal } from "~/modules/activation"
|
||||
import type { FeedFormDataValuesType } from "~/modules/discover/FeedForm"
|
||||
import { FeedForm } from "~/modules/discover/FeedForm"
|
||||
import type { ListFormDataValuesType } from "~/modules/discover/ListForm"
|
||||
import { ListForm } from "~/modules/discover/ListForm"
|
||||
|
||||
const useCanFollowMoreInboxAndNotify = () => {
|
||||
const role = useUserRole()
|
||||
const listCurrentCount = useListSubscriptionCount()
|
||||
const feedCurrentCount = useFeedSubscriptionCount()
|
||||
const presentActivationModal = useActivationModal()
|
||||
const serverConfigs = useServerConfigs()
|
||||
|
||||
return useEventCallback((type: "list" | "feed") => {
|
||||
if (role === UserRole.Free || role === UserRole.Trial) {
|
||||
const LIMIT =
|
||||
(type !== "list"
|
||||
? serverConfigs?.MAX_TRIAL_USER_FEED_SUBSCRIPTION
|
||||
: serverConfigs?.MAX_TRIAL_USER_LIST_SUBSCRIPTION) || 50
|
||||
const CURRENT = type === "list" ? listCurrentCount : feedCurrentCount
|
||||
const can = CURRENT < LIMIT
|
||||
if (!can) {
|
||||
presentActivationModal()
|
||||
|
||||
throw new CustomSafeError(
|
||||
`Trial user cannot create more ${type}, limit: ${LIMIT}, current: ${CURRENT}`,
|
||||
true,
|
||||
)
|
||||
}
|
||||
return can
|
||||
} else {
|
||||
// const can = currentInboxCount < MAX_INBOX_COUNT
|
||||
// if (!can) {
|
||||
// // TODO
|
||||
// }
|
||||
// return can
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export interface FollowOptions {
|
||||
isList: boolean
|
||||
id?: string
|
||||
|
|
@ -68,17 +22,10 @@ export interface FollowOptions {
|
|||
}
|
||||
export const useFollow = () => {
|
||||
const { present } = useModalStack()
|
||||
const canFollowMoreInboxAndNotify = useCanFollowMoreInboxAndNotify()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return useCallback(
|
||||
(options?: FollowOptions) => {
|
||||
if (options?.isList) {
|
||||
canFollowMoreInboxAndNotify("list")
|
||||
} else {
|
||||
canFollowMoreInboxAndNotify("feed")
|
||||
}
|
||||
|
||||
// Some feeds redirect xxx.com/feed to xxx.com/feed/
|
||||
// Try to get a valid feed, then we can check isFollowed correctly
|
||||
const feed =
|
||||
|
|
@ -117,6 +64,6 @@ export const useFollow = () => {
|
|||
},
|
||||
})
|
||||
},
|
||||
[canFollowMoreInboxAndNotify, present],
|
||||
[present],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ import { userActions } from "@follow/store/user/store"
|
|||
import { createDesktopAPIHeaders } from "@follow/utils/headers"
|
||||
import { FollowClient } from "@follow-app/client-sdk"
|
||||
import PKG from "@pkg"
|
||||
import { createElement } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { NetworkStatus, setApiStatus } from "~/atoms/network"
|
||||
import { setLoginModalShow } from "~/atoms/user"
|
||||
import { NeedActivationToast } from "~/modules/activation/NeedActivationToast"
|
||||
|
||||
import { getClientId, getSessionId } from "./client-session"
|
||||
|
||||
|
|
@ -78,25 +75,6 @@ followClient.addResponseInterceptor(async ({ response }) => {
|
|||
if (response.status === 400 && json.code === 1003) {
|
||||
router.navigate("/invitation")
|
||||
}
|
||||
if (json.code.toString().startsWith("11")) {
|
||||
setTimeout(() => {
|
||||
const toastId = toast.error(
|
||||
createElement(NeedActivationToast, {
|
||||
dimiss: () => {
|
||||
toast.dismiss(toastId)
|
||||
},
|
||||
}),
|
||||
{
|
||||
closeButton: true,
|
||||
duration: 10e4,
|
||||
|
||||
classNames: {
|
||||
content: tw`w-full`,
|
||||
},
|
||||
},
|
||||
)
|
||||
}, 500)
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
import { DEV } from "@follow/shared/constants"
|
||||
import { env } from "@follow/shared/env.desktop"
|
||||
import { userActions } from "@follow/store/user/store"
|
||||
import { createDesktopAPIHeaders } from "@follow/utils/headers"
|
||||
import PKG from "@pkg"
|
||||
import { ofetch } from "ofetch"
|
||||
import { createElement } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { NetworkStatus, setApiStatus } from "~/atoms/network"
|
||||
import { setLoginModalShow } from "~/atoms/user"
|
||||
import { NeedActivationToast } from "~/modules/activation/NeedActivationToast"
|
||||
import { DebugRegistry } from "~/modules/debug/registry"
|
||||
|
||||
import { getClientId, getSessionId } from "./client-session"
|
||||
|
||||
|
|
@ -57,48 +52,8 @@ export const apiFetch = ofetch.create({
|
|||
if (context.response.status === 400 && json.code === 1003) {
|
||||
router.navigate("/invitation")
|
||||
}
|
||||
if (json.code.toString().startsWith("11")) {
|
||||
setTimeout(() => {
|
||||
const toastId = toast.error(
|
||||
createElement(NeedActivationToast, {
|
||||
dimiss: () => {
|
||||
toast.dismiss(toastId)
|
||||
},
|
||||
}),
|
||||
{
|
||||
closeButton: true,
|
||||
duration: 10e4,
|
||||
|
||||
classNames: {
|
||||
content: tw`w-full`,
|
||||
},
|
||||
},
|
||||
)
|
||||
}, 500)
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
if (DEV) {
|
||||
DebugRegistry.add("Activation Toast", () => {
|
||||
setTimeout(() => {
|
||||
const toastId = toast.error(
|
||||
createElement(NeedActivationToast, {
|
||||
dimiss: () => {
|
||||
toast.dismiss(toastId)
|
||||
},
|
||||
}),
|
||||
{
|
||||
closeButton: true,
|
||||
duration: 10e4,
|
||||
classNames: {
|
||||
content: tw`w-full`,
|
||||
},
|
||||
},
|
||||
)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { DEV } from "@follow/shared/constants"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { FollowAPIError, isNeedUpgradeError } from "@follow-app/client-sdk"
|
||||
import { t } from "i18next"
|
||||
import { FetchError } from "ofetch"
|
||||
import { createElement } from "react"
|
||||
import type { ExternalToast } from "sonner"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { getIsInMASReview } from "~/atoms/server-configs"
|
||||
import { CopyButton } from "~/components/ui/button/CopyButton"
|
||||
import { Markdown } from "~/components/ui/markdown/Markdown"
|
||||
import { DebugRegistry } from "~/modules/debug/registry"
|
||||
|
|
@ -32,6 +34,20 @@ export const getFetchErrorInfo = (
|
|||
}
|
||||
}
|
||||
|
||||
if (error instanceof FollowAPIError && error.code) {
|
||||
const code = Number(error.code)
|
||||
try {
|
||||
const i18nKey = `errors:${code}` as any
|
||||
const i18nMessage = t(i18nKey) === i18nKey ? error.message : t(i18nKey)
|
||||
return {
|
||||
message: i18nMessage,
|
||||
code,
|
||||
}
|
||||
} catch {
|
||||
return { message: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
return { message: error.message }
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +94,17 @@ export const toastFetchError = (
|
|||
}
|
||||
}
|
||||
|
||||
if ("code" in error && error.code) {
|
||||
code = Number(error.code)
|
||||
try {
|
||||
const tValue = t(`errors:${code}` as any)
|
||||
const i18nMessage = tValue === code?.toString() ? error.message : tValue
|
||||
message = i18nMessage
|
||||
} catch {
|
||||
message = error.message
|
||||
}
|
||||
}
|
||||
|
||||
// 2fa errors are handled by the form
|
||||
if (code === 4007 || code === 4008) {
|
||||
return
|
||||
|
|
@ -94,9 +121,24 @@ export const toastFetchError = (
|
|||
}
|
||||
|
||||
if (!_reason) {
|
||||
const title = _title || message
|
||||
toastOptions.description = _title ? message : undefined
|
||||
return toast.error(title, toastOptions)
|
||||
const title = _title || message || "Unknown error occurred"
|
||||
toastOptions.description = _title ? message : ""
|
||||
const isInMASReview = getIsInMASReview()
|
||||
const needUpgradeError = code && !isInMASReview ? isNeedUpgradeError(code) : false
|
||||
if (needUpgradeError) {
|
||||
toastOptions.description = "Please upgrade your plan."
|
||||
}
|
||||
return toast.error(title, {
|
||||
...toastOptions,
|
||||
action: needUpgradeError
|
||||
? {
|
||||
label: "Upgrade",
|
||||
onClick: () => {
|
||||
window.router.showSettings({ tab: "plan" })
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
} else {
|
||||
return toast.error(message || _title, {
|
||||
duration: 5000,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { useDialog } from "~/components/ui/modal/stacked/hooks"
|
|||
import { useContextMenu } from "~/hooks/common/useContextMenu"
|
||||
import { getI18n } from "~/i18n"
|
||||
import { copyToClipboard, readFromClipboard } from "~/lib/clipboard"
|
||||
import { toastFetchError } from "~/lib/error-parser"
|
||||
import { downloadJsonFile, selectJsonFile } from "~/lib/export"
|
||||
import { RuleCard } from "~/modules/action/rule-card"
|
||||
import {
|
||||
|
|
@ -421,7 +422,7 @@ const ActionButtonGroup = ({ onCreateRule }: { onCreateRule: () => void }) => {
|
|||
toast(t("actions.saveSuccess"))
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error)
|
||||
toastFetchError(error)
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
import { toastStyles } from "@follow/components/ui/toast/styles.js"
|
||||
import { stopPropagation } from "@follow/utils/dom"
|
||||
import { useCallback } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useSettingModal } from "../settings/modal/use-setting-modal-hack"
|
||||
|
||||
export const NeedActivationToast = (props: { dimiss: () => void }) => {
|
||||
const settingModalPresent = useSettingModal()
|
||||
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className="flex justify-between gap-3">
|
||||
<div>{t("activation.plan.description")}</div>
|
||||
|
||||
<button
|
||||
className={toastStyles.actionButton}
|
||||
type="button"
|
||||
onPointerDown={stopPropagation}
|
||||
onClick={useCallback(() => {
|
||||
settingModalPresent("plan")
|
||||
props.dimiss()
|
||||
}, [settingModalPresent, props])}
|
||||
>
|
||||
{t("activation.activate")}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ import { cn } from "@follow/utils"
|
|||
import { m } from "motion/react"
|
||||
import * as React from "react"
|
||||
|
||||
import { useSettingModal } from "~/modules/settings/modal/useSettingModal"
|
||||
|
||||
import { parseAIError } from "../../utils/error"
|
||||
|
||||
interface RateLimitNoticeProps {
|
||||
|
|
@ -16,6 +18,7 @@ interface RateLimitNoticeProps {
|
|||
export const RateLimitNotice: React.FC<RateLimitNoticeProps> = ({ error, className }) => {
|
||||
const parsedError = React.useMemo(() => parseAIError(error), [error])
|
||||
const { isRateLimitError, errorData } = parsedError
|
||||
const settingModalPresent = useSettingModal()
|
||||
|
||||
// Only render for rate limit errors
|
||||
if (!isRateLimitError || !errorData) {
|
||||
|
|
@ -57,7 +60,7 @@ export const RateLimitNotice: React.FC<RateLimitNoticeProps> = ({ error, classNa
|
|||
parts.push(`${remainingTokens.toLocaleString()} tokens left`)
|
||||
}
|
||||
} else {
|
||||
parts.push("AI usage limit reached")
|
||||
parts.push("Upgrade plan to get more AI credits.")
|
||||
}
|
||||
|
||||
// Reset time
|
||||
|
|
@ -79,9 +82,10 @@ export const RateLimitNotice: React.FC<RateLimitNoticeProps> = ({ error, classNa
|
|||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={cn("mb-3", className)}
|
||||
onClick={() => settingModalPresent("plan")}
|
||||
>
|
||||
<div className="flex items-center gap-2 rounded-lg border border-border bg-fill/50 px-3 py-2 backdrop-blur-sm">
|
||||
<i className="i-mgc-information-cute-re size-4 flex-shrink-0 text-text-tertiary" />
|
||||
<i className="i-mgc-power size-4 flex-shrink-0 text-text" />
|
||||
<span className="min-w-0 flex-1 truncate text-xs text-text-secondary">
|
||||
{buildMessage()}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"
|
|||
import { useEntryIsInReadabilitySuccess } from "~/atoms/readability"
|
||||
import { useActionLanguage } from "~/atoms/settings/general"
|
||||
import { AISummaryCardBase } from "~/components/ui/ai-summary-card"
|
||||
import { getFetchErrorInfo } from "~/lib/error-parser"
|
||||
|
||||
interface EntrySummaryCardProps {
|
||||
entryId: string
|
||||
|
|
@ -22,6 +23,8 @@ export const EntrySummaryCard: React.FC<EntrySummaryCardProps> = ({ entryId, cla
|
|||
actionLanguage,
|
||||
enabled: true,
|
||||
})
|
||||
const summaryErrorCode =
|
||||
summary.error instanceof Error ? getFetchErrorInfo(summary.error).code : undefined
|
||||
|
||||
return (
|
||||
<m.div
|
||||
|
|
@ -35,6 +38,7 @@ export const EntrySummaryCard: React.FC<EntrySummaryCardProps> = ({ entryId, cla
|
|||
isLoading={summary.isLoading}
|
||||
className={className}
|
||||
title={t("ai_summary")}
|
||||
errorCode={summaryErrorCode}
|
||||
/>
|
||||
</m.div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -399,10 +399,6 @@ export const useRegisterEntryCommands = () => {
|
|||
icon: <i className="i-mgc-ai-cute-re" />,
|
||||
category,
|
||||
run: () => {
|
||||
if (role === UserRole.Free || role === UserRole.Trial) {
|
||||
presentActivationModal()
|
||||
return
|
||||
}
|
||||
toggleShowAISummaryOnce()
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,48 +1,17 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { repository } from "@pkg"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useEventCallback } from "usehooks-ts"
|
||||
|
||||
import { useModalStack } from "~/components/ui/modal/stacked/hooks"
|
||||
import { CustomSafeError } from "~/errors/CustomSafeError"
|
||||
|
||||
import { useActivationModal } from "../activation"
|
||||
import { InboxTable } from "./Inbox"
|
||||
import { InboxForm } from "./InboxForm"
|
||||
|
||||
const useCanCreateMoreInboxAndNotify = () => {
|
||||
const role = useUserRole()
|
||||
const presentActivationModal = useActivationModal()
|
||||
|
||||
return useEventCallback(() => {
|
||||
if (role === UserRole.Free || role === UserRole.Trial) {
|
||||
const can = false
|
||||
if (!can) {
|
||||
presentActivationModal()
|
||||
|
||||
throw new CustomSafeError(`Trial user cannot create more inboxes`, true)
|
||||
}
|
||||
return can
|
||||
} else {
|
||||
// const can = currentInboxCount < MAX_INBOX_COUNT
|
||||
// if (!can) {
|
||||
// // TODO
|
||||
// }
|
||||
// return can
|
||||
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
export function DiscoverInboxList() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { present } = useModalStack()
|
||||
|
||||
const preCheck = useCanCreateMoreInboxAndNotify()
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-[540px] rounded-lg border bg-material-ultra-thin p-5 shadow-sm">
|
||||
<div className="mb-4 flex flex-wrap items-center gap-2 text-sm text-zinc-500">
|
||||
|
|
@ -63,7 +32,6 @@ export function DiscoverInboxList() {
|
|||
<Button
|
||||
textClassName="flex items-center gap-2"
|
||||
onClick={() =>
|
||||
preCheck() &&
|
||||
present({
|
||||
title: t("sidebar.feed_actions.new_inbox"),
|
||||
content: () => <InboxForm asWidget />,
|
||||
|
|
|
|||
|
|
@ -13,12 +13,19 @@ import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
|
|||
import { RootPortal } from "@follow/components/ui/portal/index.js"
|
||||
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
|
||||
import { Switch } from "@follow/components/ui/switch/index.jsx"
|
||||
import { FeedViewType } from "@follow/constants"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipPortal,
|
||||
TooltipTrigger,
|
||||
} from "@follow/components/ui/tooltip/index.js"
|
||||
import { FeedViewType, UserRole } from "@follow/constants"
|
||||
import { useFeedByIdOrUrl } from "@follow/store/feed/hooks"
|
||||
import type { FeedModel } from "@follow/store/feed/types"
|
||||
import { useCategories, useSubscriptionByFeedId } from "@follow/store/subscription/hooks"
|
||||
import { subscriptionSyncService } from "@follow/store/subscription/store"
|
||||
import { whoami } from "@follow/store/user/getters"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { tracker } from "@follow/tracker"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import type { FeedAnalyticsModel, ParsedEntry } from "@follow-app/client-sdk"
|
||||
|
|
@ -30,11 +37,13 @@ import { useTranslation } from "react-i18next"
|
|||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
|
||||
import { useIsInMASReview } from "~/atoms/server-configs"
|
||||
import { Autocomplete } from "~/components/ui/auto-completion"
|
||||
import { useCurrentModal, useIsInModal } from "~/components/ui/modal/stacked/hooks"
|
||||
import { getRouteParams } from "~/hooks/biz/useRouteParams"
|
||||
import { useI18n } from "~/hooks/common"
|
||||
import { toastFetchError } from "~/lib/error-parser"
|
||||
import { useSettingModal } from "~/modules/settings/modal/useSettingModal"
|
||||
import { feed as feedQuery, useFeedQuery } from "~/queries/feed"
|
||||
|
||||
import { ViewSelectorRadioGroup } from "../shared/ViewSelectorRadioGroup"
|
||||
|
|
@ -49,6 +58,35 @@ const formSchema = z.object({
|
|||
})
|
||||
export type FeedFormDataValuesType = z.infer<typeof formSchema>
|
||||
|
||||
export const PaidBadge = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
const settingModalPresent = useSettingModal()
|
||||
const isInMASReview = useIsInMASReview()
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault()
|
||||
settingModalPresent("plan")
|
||||
},
|
||||
[settingModalPresent],
|
||||
)
|
||||
|
||||
if (isInMASReview) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<i className="i-mgc-power block text-accent" onClick={handleClick} />
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>{t("control.paid_badge.plus_or_higher")}</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export const FeedForm: Component<{
|
||||
url?: string
|
||||
id?: string
|
||||
|
|
@ -297,6 +335,10 @@ const FeedInnerForm = ({
|
|||
form.setValue("title", feed.title || "")
|
||||
}, [feed.title, form])
|
||||
|
||||
const role = useUserRole()
|
||||
const isInMASReview = useIsInMASReview()
|
||||
const disabledForRole = role === UserRole.Free && !isInMASReview
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-y-4">
|
||||
<FeedSummary isLoading={isLoading} feed={feed} analytics={analytics} showAnalytics />
|
||||
|
|
@ -367,7 +409,10 @@ const FeedInnerForm = ({
|
|||
<FormItem>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<FormLabel>{t("feed_form.private_follow")}</FormLabel>
|
||||
<FormLabel className="flex items-center gap-1">
|
||||
<span>{t("feed_form.private_follow")}</span>
|
||||
<PaidBadge />
|
||||
</FormLabel>
|
||||
<FormDescription>{t("feed_form.private_follow_description")}</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
|
|
@ -375,6 +420,7 @@ const FeedInnerForm = ({
|
|||
className="shrink-0"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={disabledForRole}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
|
@ -388,7 +434,10 @@ const FeedInnerForm = ({
|
|||
<FormItem>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<FormLabel>{t("feed_form.hide_from_timeline")}</FormLabel>
|
||||
<FormLabel className="flex items-center gap-1">
|
||||
<span>{t("feed_form.hide_from_timeline")}</span>
|
||||
<PaidBadge />
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t("feed_form.hide_from_timeline_description")}
|
||||
</FormDescription>
|
||||
|
|
@ -398,6 +447,7 @@ const FeedInnerForm = ({
|
|||
className="shrink-0"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={disabledForRole}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from "~/atoms/settings/ai"
|
||||
import { useActionLanguage } from "~/atoms/settings/general"
|
||||
import { AISummaryCardBase } from "~/components/ui/ai-summary-card"
|
||||
import { getFetchErrorInfo } from "~/lib/error-parser"
|
||||
|
||||
export function AISummary({ entryId }: { entryId: string }) {
|
||||
const { t } = useTranslation()
|
||||
|
|
@ -30,6 +31,8 @@ export function AISummary({ entryId }: { entryId: string }) {
|
|||
target: isInReadabilitySuccess ? "readabilityContent" : "content",
|
||||
enabled: showAISummary,
|
||||
})
|
||||
const summaryErrorCode =
|
||||
summary.error instanceof Error ? getFetchErrorInfo(summary.error).code : undefined
|
||||
|
||||
// Show Ask AI button when:
|
||||
// 1. Panel style is floating AND panel is not visible
|
||||
|
|
@ -54,6 +57,7 @@ export function AISummary({ entryId }: { entryId: string }) {
|
|||
title={t("entry_content.ai_summary")}
|
||||
showAskAIButton={shouldShowAskAI}
|
||||
onAskAI={handleAskAI}
|
||||
errorCode={summaryErrorCode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,57 @@ import { Input, TextArea } from "@follow/components/ui/input/index.js"
|
|||
import { Label } from "@follow/components/ui/label/index.jsx"
|
||||
import { SegmentGroup, SegmentItem } from "@follow/components/ui/segment/index.jsx"
|
||||
import { Switch } from "@follow/components/ui/switch/index.jsx"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipPortal,
|
||||
TooltipTrigger,
|
||||
} from "@follow/components/ui/tooltip/index.js"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import type { ChangeEventHandler, ReactNode } from "react"
|
||||
import { useId, useState } from "react"
|
||||
import { useCallback, useId, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { titleCase } from "title-case"
|
||||
|
||||
import { useIsInMASReview } from "~/atoms/server-configs"
|
||||
|
||||
import { SettingPaidLevels } from "./helper/setting-builder"
|
||||
import { useSetSettingTab } from "./modal/context"
|
||||
|
||||
export const PaidBadge: Component<{
|
||||
paidLevel: SettingPaidLevels
|
||||
}> = ({ paidLevel }) => {
|
||||
const { t } = useTranslation("settings")
|
||||
const setTab = useSetSettingTab()
|
||||
const isInMASReview = useIsInMASReview()
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault()
|
||||
setTab("plan")
|
||||
},
|
||||
[setTab],
|
||||
)
|
||||
|
||||
if (isInMASReview) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<i className="i-mgc-power block text-accent" onClick={handleClick} />
|
||||
</TooltipTrigger>
|
||||
<TooltipPortal>
|
||||
<TooltipContent>
|
||||
{paidLevel === SettingPaidLevels.FreeLimited && t("control.paid_badge.free_limited")}
|
||||
{paidLevel === SettingPaidLevels.Plus && t("control.paid_badge.plus_or_higher")}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export const SettingCheckbox: Component<{
|
||||
label: string
|
||||
checked: boolean
|
||||
|
|
@ -32,15 +78,20 @@ export const SettingSwitch: Component<{
|
|||
label: string
|
||||
checked: boolean
|
||||
onCheckedChange: (checked: boolean) => void
|
||||
}> = ({ checked, label, onCheckedChange, className }) => {
|
||||
disabled?: boolean
|
||||
paidLevel?: SettingPaidLevels
|
||||
}> = ({ checked, label, onCheckedChange, className, disabled, paidLevel }) => {
|
||||
const id = useId()
|
||||
const handleCheckedChange = (checked: boolean) => {
|
||||
onCheckedChange(checked)
|
||||
}
|
||||
return (
|
||||
<div className={cn("mb-3 flex items-center justify-between gap-4", className)}>
|
||||
<Label htmlFor={id}>{titleCase(label)}</Label>
|
||||
<Switch id={id} checked={checked} onCheckedChange={handleCheckedChange} />
|
||||
<Label htmlFor={id} className="flex items-center gap-1">
|
||||
<span>{titleCase(label)}</span>
|
||||
{!!paidLevel && <PaidBadge paidLevel={paidLevel} />}
|
||||
</Label>
|
||||
<Switch id={id} checked={checked} onCheckedChange={handleCheckedChange} disabled={disabled} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import type { SettingNamespace } from "@follow/shared/settings/constants"
|
||||
import { getSettingPaidLevel } from "@follow/shared/settings/constants"
|
||||
import type { JSX } from "react/jsx-runtime"
|
||||
|
||||
import type { SettingItem } from "./setting-builder"
|
||||
import { createSettingBuilder } from "./setting-builder"
|
||||
|
||||
export const createDefineSettingItem =
|
||||
<T>(_getSetting: () => T, setSetting: (key: any, value: Partial<T>) => void) =>
|
||||
<T>(
|
||||
settingNamespace: SettingNamespace,
|
||||
_getSetting: () => T,
|
||||
setSetting: (key: any, value: Partial<T>) => void,
|
||||
) =>
|
||||
<K extends keyof T>(
|
||||
key: K,
|
||||
options: {
|
||||
|
|
@ -13,9 +19,10 @@ export const createDefineSettingItem =
|
|||
onChange?: (value: T[K]) => void
|
||||
onAfterChange?: (value: T[K]) => void
|
||||
hide?: boolean
|
||||
} & Omit<SettingItem<any>, "onChange" | "description" | "label" | "hide" | "key">,
|
||||
} & Omit<SettingItem<any>, "onChange" | "description" | "label" | "hide" | "key" | "paidLevel">,
|
||||
): any => {
|
||||
const { label, description, onChange, hide, onAfterChange, ...rest } = options
|
||||
const paidLevel = getSettingPaidLevel(settingNamespace, String(key))
|
||||
|
||||
if (hide) return null
|
||||
return {
|
||||
|
|
@ -31,16 +38,18 @@ export const createDefineSettingItem =
|
|||
}
|
||||
},
|
||||
disabled: hide,
|
||||
paidLevel,
|
||||
...rest,
|
||||
} as SettingItem<any>
|
||||
}
|
||||
|
||||
export const createSetting = <T extends object>(
|
||||
settingNamespace: SettingNamespace,
|
||||
useSetting: () => T,
|
||||
setSetting: (key: any, value: Partial<T>) => void,
|
||||
) => {
|
||||
const SettingBuilder = createSettingBuilder(useSetting)
|
||||
const defineSettingItem = createDefineSettingItem(useSetting, setSetting)
|
||||
const defineSettingItem = createDefineSettingItem(settingNamespace, useSetting, setSetting)
|
||||
return {
|
||||
SettingBuilder,
|
||||
defineSettingItem,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
/* eslint-disable @eslint-react/no-array-index-key */
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { SettingPaidLevels } from "@follow/shared/settings/constants"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import type { FC, ReactNode } from "react"
|
||||
import * as React from "react"
|
||||
import { isValidElement } from "react"
|
||||
|
|
@ -6,6 +9,8 @@ import { isValidElement } from "react"
|
|||
import { SettingActionItem, SettingDescription, SettingInput, SettingSwitch } from "../control"
|
||||
import { SettingItemGroup, SettingSectionTitle } from "../section"
|
||||
|
||||
export { SettingPaidLevels } from "@follow/shared/settings/constants"
|
||||
|
||||
type SharedSettingItem = {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
|
@ -25,6 +30,7 @@ export type SettingItem<T, K extends keyof T = keyof T> = {
|
|||
className?: string
|
||||
[key: string]: any
|
||||
}
|
||||
paidLevel?: SettingPaidLevels
|
||||
} & SharedSettingItem
|
||||
|
||||
type SectionSettingItem = {
|
||||
|
|
@ -54,6 +60,7 @@ export const createSettingBuilder =
|
|||
}) => {
|
||||
const { settings } = props
|
||||
const settingObject = useSetting()
|
||||
const role = useUserRole()
|
||||
|
||||
const filteredSettings = settings.filter((i) => !!i)
|
||||
return filteredSettings.map((setting, index) => {
|
||||
|
|
@ -64,7 +71,7 @@ export const createSettingBuilder =
|
|||
const assertSetting = setting as SettingItem<T> | SectionSettingItem | ActionSettingItem
|
||||
|
||||
if (!assertSetting) return null
|
||||
if (assertSetting.disabled) return null
|
||||
// if (assertSetting.disabled) return null
|
||||
|
||||
const nextItem = filteredSettings[index + 1]
|
||||
// If has no next item or next item is also a title, then it is an empty section
|
||||
|
|
@ -90,6 +97,12 @@ export const createSettingBuilder =
|
|||
if ("type" in assertSetting && assertSetting.type === "title") {
|
||||
return null
|
||||
}
|
||||
const disabledForRole =
|
||||
role === UserRole.Free &&
|
||||
"paidLevel" in assertSetting &&
|
||||
assertSetting.paidLevel !== undefined &&
|
||||
assertSetting.paidLevel !== SettingPaidLevels.Free &&
|
||||
assertSetting.paidLevel !== SettingPaidLevels.FreeLimited
|
||||
|
||||
let ControlElement: React.ReactNode
|
||||
|
||||
|
|
@ -110,6 +123,8 @@ export const createSettingBuilder =
|
|||
assertSetting.onChange(checked as T[keyof T])
|
||||
}}
|
||||
label={assertSetting.label}
|
||||
disabled={assertSetting.disabled || disabledForRole}
|
||||
paidLevel={assertSetting.paidLevel}
|
||||
/>
|
||||
)
|
||||
break
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { TaskSchedulingSection } from "./ai/tasks"
|
|||
import { UsageAnalysisSection } from "./ai/usage"
|
||||
|
||||
const SettingBuilder = createSettingBuilder(useAISettingValue)
|
||||
const defineSettingItem = createDefineSettingItem(useAISettingValue, setAISetting)
|
||||
const defineSettingItem = createDefineSettingItem("ai", useAISettingValue, setAISetting)
|
||||
|
||||
export const AI_SETTING_SECTION_IDS = {
|
||||
shortcuts: "settings-ai-shortcuts",
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import { SettingItemGroup } from "../section"
|
|||
import { ContentFontSelector, UIFontSelector } from "../sections/fonts"
|
||||
|
||||
const SettingBuilder = createSettingBuilder(useUISettingValue)
|
||||
const _defineItem = createDefineSettingItem(useUISettingValue, setUISetting)
|
||||
const _defineItem = createDefineSettingItem("ui", useUISettingValue, setUISetting)
|
||||
|
||||
export const SettingAppearance = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import { SettingActionItem, SettingDescription } from "../control"
|
|||
import { createSetting } from "../helper/builder"
|
||||
import { SettingItemGroup } from "../section"
|
||||
|
||||
const { SettingBuilder } = createSetting(useGeneralSettingValue, setGeneralSetting)
|
||||
const { SettingBuilder } = createSetting("general", useGeneralSettingValue, setGeneralSetting)
|
||||
|
||||
export const SettingDataControl = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { ResponsiveSelect } from "@follow/components/ui/select/responsive.js"
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { useTypeScriptHappyCallback } from "@follow/hooks"
|
||||
import { ACTION_LANGUAGE_MAP } from "@follow/shared"
|
||||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import dayjs from "dayjs"
|
||||
|
|
@ -12,6 +14,7 @@ import { useTranslation } from "react-i18next"
|
|||
import { currentSupportedLanguages } from "~/@types/constants"
|
||||
import { defaultResources } from "~/@types/default-resource"
|
||||
import { langLoadingLockMapAtom } from "~/atoms/lang"
|
||||
import { useIsInMASReview } from "~/atoms/server-configs"
|
||||
import {
|
||||
DEFAULT_ACTION_LANGUAGE,
|
||||
setGeneralSetting,
|
||||
|
|
@ -26,8 +29,9 @@ import { fallbackLanguage } from "~/i18n"
|
|||
import { ipcServices } from "~/lib/client"
|
||||
import { setTranslationCache } from "~/modules/entry-content/atoms"
|
||||
|
||||
import { SettingDescription, SettingInput, SettingSwitch } from "../control"
|
||||
import { PaidBadge, SettingDescription, SettingInput, SettingSwitch } from "../control"
|
||||
import { createSetting } from "../helper/builder"
|
||||
import { SettingPaidLevels } from "../helper/setting-builder"
|
||||
import {
|
||||
useWrapEnhancedSettingItem,
|
||||
WrapEnhancedSettingTab,
|
||||
|
|
@ -35,6 +39,7 @@ import {
|
|||
import { SettingItemGroup } from "../section"
|
||||
|
||||
const { defineSettingItem: _defineSettingItem, SettingBuilder } = createSetting(
|
||||
"general",
|
||||
useGeneralSettingValue,
|
||||
setGeneralSetting,
|
||||
)
|
||||
|
|
@ -78,7 +83,7 @@ export const SettingGeneral = () => {
|
|||
|
||||
defineSettingItem("appLaunchOnStartup", {
|
||||
label: t("general.launch_at_login"),
|
||||
disabled: !ipcServices,
|
||||
hide: !ipcServices,
|
||||
onChange(value) {
|
||||
saveLoginSetting(value)
|
||||
},
|
||||
|
|
@ -290,11 +295,17 @@ export const LanguageSelector = ({
|
|||
const TranslationModeSelector = () => {
|
||||
const { t } = useTranslation("settings")
|
||||
const translationMode = useGeneralSettingKey("translationMode")
|
||||
const role = useUserRole()
|
||||
const isInMASReview = useIsInMASReview()
|
||||
const disabledForRole = role === UserRole.Free && !isInMASReview
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<span className="shrink-0 text-sm font-medium">{t("general.translation_mode.label")}</span>
|
||||
<span className="flex shrink-0 items-center gap-1 text-sm font-medium">
|
||||
<span>{t("general.translation_mode.label")}</span>
|
||||
<PaidBadge paidLevel={SettingPaidLevels.Plus} />
|
||||
</span>
|
||||
<ResponsiveSelect
|
||||
size="sm"
|
||||
triggerClassName="w-48"
|
||||
|
|
@ -307,6 +318,7 @@ const TranslationModeSelector = () => {
|
|||
{ label: t("general.translation_mode.bilingual"), value: "bilingual" },
|
||||
{ label: t("general.translation_mode.translation-only"), value: "translation-only" },
|
||||
]}
|
||||
disabled={disabledForRole}
|
||||
/>
|
||||
</div>
|
||||
<SettingDescription>{t("general.translation_mode.description")}</SettingDescription>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { SettingSectionTitle } from "../../section"
|
|||
import { CustomIntegrationModalContent } from "./CustomIntegrationModal"
|
||||
|
||||
const { defineSettingItem, SettingBuilder } = createSetting(
|
||||
"integration",
|
||||
useIntegrationSettingValue,
|
||||
setIntegrationSetting,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { SettingSectionTitle } from "../../section"
|
|||
import { CustomIntegrationSection } from "./CustomIntegrationSection"
|
||||
|
||||
const { defineSettingItem, SettingBuilder } = createSetting(
|
||||
"integration",
|
||||
useIntegrationSettingValue,
|
||||
setIntegrationSetting,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,71 +1,52 @@
|
|||
import { Button } from "@follow/components/ui/button/index.js"
|
||||
import { UserRole, UserRoleName } from "@follow/constants"
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { DEEPLINK_SCHEME, IN_ELECTRON } from "@follow/shared"
|
||||
import { env } from "@follow/shared/env.desktop"
|
||||
import { useRoleEndAt, useUserRole, useWhoami } from "@follow/store/user/hooks"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { useMutation, useQuery } from "@tanstack/react-query"
|
||||
import { useState } from "react"
|
||||
import { Trans } from "react-i18next"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import type { PaymentFeature, PaymentPlan } from "~/atoms/server-configs"
|
||||
import { useIsInMASReview, useServerConfigs } from "~/atoms/server-configs"
|
||||
import { subscription } from "~/lib/auth"
|
||||
|
||||
// Plan configuration types
|
||||
interface Plan {
|
||||
id: string
|
||||
title: string
|
||||
monthlyPrice: number
|
||||
yearlyPrice: number
|
||||
features: string[]
|
||||
isPopular?: boolean
|
||||
role: UserRole
|
||||
isComingSoon?: boolean
|
||||
tier: number // Add tier for hierarchy comparison
|
||||
const formatFeatureValue = (
|
||||
key: keyof PaymentFeature,
|
||||
value: number | boolean | null | undefined,
|
||||
): string => {
|
||||
if (value == null || value === undefined) {
|
||||
return "—"
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return value ? "✓" : "—"
|
||||
}
|
||||
|
||||
if (key === "PRIORITY_SUPPORT" && typeof value === "number") {
|
||||
return "⭐️".repeat(value)
|
||||
}
|
||||
|
||||
if (value === Number.MAX_SAFE_INTEGER) {
|
||||
return "Unlimited"
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat("en", {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
maximumFractionDigits: 1,
|
||||
}).format(value)
|
||||
}
|
||||
|
||||
// Plan hierarchy: Free (1) < Pro Preview (2) < Pro (3)
|
||||
const PLAN_TIER_MAP: Record<UserRole, number> = {
|
||||
[UserRole.Admin]: 4, // Admin has highest tier
|
||||
[UserRole.Free]: 1,
|
||||
[UserRole.Trial]: 1, // Same as Free (deprecated)
|
||||
[UserRole.PreProTrial]: 2,
|
||||
[UserRole.Pro]: 3,
|
||||
}
|
||||
|
||||
// Plan configurations
|
||||
const PLAN_CONFIGS: Plan[] = [
|
||||
{
|
||||
id: "free",
|
||||
title: UserRoleName[UserRole.Free],
|
||||
monthlyPrice: 0,
|
||||
yearlyPrice: 0,
|
||||
features: ["50 feeds", "10 lists"],
|
||||
isPopular: false,
|
||||
role: UserRole.Free,
|
||||
tier: PLAN_TIER_MAP[UserRole.Free],
|
||||
},
|
||||
{
|
||||
id: "folo pro",
|
||||
title: UserRoleName[UserRole.Pro],
|
||||
monthlyPrice: 20,
|
||||
yearlyPrice: 180,
|
||||
features: [
|
||||
"1000 feeds and lists",
|
||||
"10 inboxes",
|
||||
"10 actions",
|
||||
"100 webhooks",
|
||||
"Advanced AI features",
|
||||
],
|
||||
isPopular: true,
|
||||
role: UserRole.Pro,
|
||||
tier: PLAN_TIER_MAP[UserRole.Pro],
|
||||
},
|
||||
]
|
||||
|
||||
const useUpgradePlan = ({ plan, annual }: { plan: string; annual: boolean }) => {
|
||||
const useUpgradePlan = ({ plan, annual }: { plan: string | undefined; annual: boolean }) => {
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!plan) {
|
||||
return
|
||||
}
|
||||
|
||||
const res = await subscription.upgrade({
|
||||
plan,
|
||||
annual,
|
||||
|
|
@ -86,22 +67,20 @@ const useActiveSubscription = () => {
|
|||
queryKey: ["activeSubscription"],
|
||||
queryFn: async () => {
|
||||
const { data } = await subscription.list()
|
||||
return data
|
||||
return data?.find((sub) => sub.status === "active" || sub.status === "trialing")
|
||||
},
|
||||
enabled: !!userId,
|
||||
})
|
||||
}
|
||||
|
||||
const useCancelPlan = () => {
|
||||
const activeSubscription = useActiveSubscription()
|
||||
const latestSubscription = activeSubscription.data?.at(-1)
|
||||
const { data: latestSubscription } = useActiveSubscription()
|
||||
const subscriptionId = latestSubscription?.id
|
||||
const cancelAtPeriodEnd = latestSubscription?.cancelAtPeriodEnd
|
||||
|
||||
const cancelMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!subscriptionId) {
|
||||
toast.error("No active subscription found.")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +96,7 @@ const useCancelPlan = () => {
|
|||
},
|
||||
})
|
||||
|
||||
if (cancelAtPeriodEnd) {
|
||||
if (cancelAtPeriodEnd || !subscriptionId) {
|
||||
return null
|
||||
} else {
|
||||
return cancelMutation
|
||||
|
|
@ -125,6 +104,7 @@ const useCancelPlan = () => {
|
|||
}
|
||||
|
||||
export function SettingPlan() {
|
||||
const isInMASReview = useIsInMASReview()
|
||||
const role = useUserRole()
|
||||
const roleEndDate = useRoleEndAt()
|
||||
const [billingPeriod, setBillingPeriod] = useState<"monthly" | "yearly">("yearly")
|
||||
|
|
@ -133,13 +113,28 @@ export function SettingPlan() {
|
|||
? Math.ceil((roleEndDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24))
|
||||
: null
|
||||
|
||||
const serverConfig = useServerConfigs()
|
||||
const plans = serverConfig?.PAYMENT_PLAN_LIST || []
|
||||
const currentPlan = plans.find((plan) => plan.role === role)
|
||||
const currentTier = currentPlan?.tier || 0
|
||||
|
||||
// Calculate average savings percentage across all paid plans
|
||||
const averageSavings = Math.round(
|
||||
plans
|
||||
.filter((plan) => plan.priceInDollars > 0 && plan.priceInDollarsAnnual > 0)
|
||||
.reduce((acc, plan) => {
|
||||
const monthlyTotal = plan.priceInDollars * 12
|
||||
const yearlyTotal = plan.priceInDollarsAnnual
|
||||
const savings = ((monthlyTotal - yearlyTotal) / monthlyTotal) * 100
|
||||
return acc + savings
|
||||
}, 0) / plans.filter((plan) => plan.priceInDollars > 0).length,
|
||||
)
|
||||
if (isInMASReview) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="mt-4 space-y-8">
|
||||
{/* Description Section */}
|
||||
<p className="mb-4 space-y-2 text-sm">
|
||||
<Trans ns="settings" i18nKey="plan.description" />
|
||||
</p>
|
||||
|
||||
{/* Billing Period Toggle */}
|
||||
<div className="flex justify-center">
|
||||
<div className="inline-flex rounded-lg bg-fill-secondary p-1">
|
||||
|
|
@ -166,7 +161,9 @@ export function SettingPlan() {
|
|||
)}
|
||||
>
|
||||
<span>Yearly</span>
|
||||
<span className="ml-2 text-xs font-medium text-green">Save 25%</span>
|
||||
{averageSavings > 0 && (
|
||||
<span className="ml-2 text-xs font-medium text-green">Save {averageSavings}%</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -174,111 +171,119 @@ export function SettingPlan() {
|
|||
{/* Plans Grid */}
|
||||
<div className="@container">
|
||||
<div className="grid grid-cols-1 gap-4 @md:grid-cols-2 @xl:grid-cols-3">
|
||||
{PLAN_CONFIGS.map((plan) => (
|
||||
{plans.map((plan) => (
|
||||
<PlanCard
|
||||
key={plan.id}
|
||||
key={plan.name}
|
||||
plan={plan}
|
||||
billingPeriod={billingPeriod}
|
||||
currentUserRole={role || null}
|
||||
daysLeft={daysLeft}
|
||||
isCurrentPlan={role === plan.role}
|
||||
currentTier={currentTier}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Comparison Table */}
|
||||
<PlanComparisonTable plans={plans} />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
// Reusable PlanCard Component
|
||||
interface PlanCardProps {
|
||||
plan: Plan
|
||||
plan: PaymentPlan
|
||||
billingPeriod: "monthly" | "yearly"
|
||||
currentUserRole: UserRole | null
|
||||
isCurrentPlan: boolean
|
||||
currentTier: number
|
||||
daysLeft: number | null
|
||||
}
|
||||
|
||||
const PlanCard = ({
|
||||
plan,
|
||||
billingPeriod,
|
||||
currentUserRole,
|
||||
isCurrentPlan,
|
||||
daysLeft,
|
||||
}: PlanCardProps) => {
|
||||
const getPlanActionType = (): "current" | "upgrade" | "coming-soon" | "in-trial" | null => {
|
||||
const PlanCard = ({ plan, billingPeriod, isCurrentPlan, currentTier, daysLeft }: PlanCardProps) => {
|
||||
const { t } = useTranslation("settings")
|
||||
const getPlanActionType = ():
|
||||
| "current"
|
||||
| "upgrade"
|
||||
| "coming-soon"
|
||||
| "in-trial"
|
||||
| "switch"
|
||||
| null => {
|
||||
if (plan.isComingSoon) return "coming-soon"
|
||||
|
||||
if (!currentUserRole) {
|
||||
return plan.tier > PLAN_TIER_MAP[UserRole.Free] ? "upgrade" : "current"
|
||||
}
|
||||
|
||||
const currentTier = PLAN_TIER_MAP[currentUserRole]
|
||||
const targetTier = plan.tier
|
||||
|
||||
if (currentTier === targetTier) {
|
||||
// if (currentUserRole === UserRole.PreProTrial) {
|
||||
// return "in-trial"
|
||||
// } else {
|
||||
// return "current"
|
||||
switch (true) {
|
||||
case isCurrentPlan: {
|
||||
return "current"
|
||||
}
|
||||
case plan.tier > currentTier && !!plan.planID: {
|
||||
return "upgrade"
|
||||
}
|
||||
// case plan.tier < currentTier && !!plan.planID: {
|
||||
// return "switch"
|
||||
// }
|
||||
return "current"
|
||||
default: {
|
||||
return null
|
||||
}
|
||||
}
|
||||
if (targetTier > currentTier) return "upgrade"
|
||||
return null
|
||||
}
|
||||
|
||||
const actionType = getPlanActionType()
|
||||
const upgradePlanMutation = useUpgradePlan({
|
||||
plan: "folo pro",
|
||||
plan: plan.planID,
|
||||
annual: billingPeriod === "yearly",
|
||||
})
|
||||
const cancelPlanMutation = useCancelPlan()
|
||||
|
||||
// Calculate price and period based on billing period
|
||||
const price = billingPeriod === "yearly" ? plan.yearlyPrice : plan.monthlyPrice
|
||||
const formattedPrice = price === 0 ? "$0" : `$${price}`
|
||||
const period = plan.role === UserRole.Free ? "" : billingPeriod === "yearly" ? "year" : "month"
|
||||
const regularPrice =
|
||||
billingPeriod === "yearly" ? plan.priceInDollarsAnnual / 12 : plan.priceInDollars
|
||||
const discountPrice =
|
||||
billingPeriod === "yearly"
|
||||
? (plan.priceInDollarsInDiscountAnnual || 0) / 12
|
||||
: plan.priceInDollarsInDiscount
|
||||
const period = plan.role === UserRole.Free ? "" : "month"
|
||||
|
||||
// Calculate savings for yearly plan
|
||||
const yearlyTotalPrice = plan.yearlyPrice
|
||||
const monthlyTotalPrice = plan.monthlyPrice * 12
|
||||
const savingsPercentage =
|
||||
plan.monthlyPrice > 0 && plan.yearlyPrice > 0
|
||||
? Math.round(((monthlyTotalPrice - yearlyTotalPrice) / monthlyTotalPrice) * 100)
|
||||
: 0
|
||||
// Calculate discount percentage from prices
|
||||
const hasDiscount =
|
||||
discountPrice &&
|
||||
discountPrice > 0 &&
|
||||
discountPrice < regularPrice &&
|
||||
discountPrice !== regularPrice
|
||||
const discountPercentage = hasDiscount
|
||||
? Math.round(((regularPrice - discountPrice) / regularPrice) * 100)
|
||||
: 0
|
||||
|
||||
// Use discount price if available, otherwise use regular price
|
||||
const finalPrice = hasDiscount ? discountPrice : regularPrice
|
||||
const formattedPrice = finalPrice === 0 ? "$0" : `$${finalPrice.toFixed(2)}`
|
||||
const formattedRegularPrice =
|
||||
hasDiscount && regularPrice > 0 ? `$${regularPrice.toFixed(2)}` : undefined
|
||||
|
||||
// Get plan description from i18n
|
||||
const planDescriptionKey = `plan.descriptions.${plan.role}` as const
|
||||
const planDescription = t(planDescriptionKey, { defaultValue: "" })
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group relative flex h-full flex-col overflow-hidden rounded-xl border transition-all duration-200",
|
||||
plan.isPopular
|
||||
actionType === "upgrade"
|
||||
? "border-accent"
|
||||
: "border-fill-tertiary bg-background hover:border-fill-secondary",
|
||||
isCurrentPlan &&
|
||||
"bg-gradient-to-b from-accent/5 to-transparent shadow-lg shadow-accent/10 ring-2 ring-accent ring-offset-2 ring-offset-background",
|
||||
plan.isComingSoon && "opacity-75",
|
||||
)}
|
||||
>
|
||||
<PlanBadges isPopular={plan.isPopular || false} />
|
||||
|
||||
<div className="flex h-full flex-col p-4 @md:p-5">
|
||||
<div className="flex-1 space-y-3 @md:space-y-4">
|
||||
<PlanHeader
|
||||
title={plan.title}
|
||||
price={formattedPrice}
|
||||
period={period}
|
||||
billingPeriod={billingPeriod}
|
||||
monthlyPrice={plan.monthlyPrice}
|
||||
yearlyPrice={plan.yearlyPrice}
|
||||
savingsPercentage={savingsPercentage}
|
||||
/>
|
||||
<PlanFeatures features={plan.features} />
|
||||
<div />
|
||||
</div>
|
||||
<div className="flex h-full flex-col justify-between gap-4 p-4 @md:p-5">
|
||||
<PlanHeader
|
||||
title={plan.name}
|
||||
price={formattedPrice}
|
||||
regularPrice={formattedRegularPrice}
|
||||
period={period}
|
||||
description={planDescription}
|
||||
discountPercentage={discountPercentage}
|
||||
/>
|
||||
|
||||
<PlanAction
|
||||
isPopular={plan.isPopular || false}
|
||||
actionType={actionType}
|
||||
daysLeft={daysLeft}
|
||||
isLoading={upgradePlanMutation.isPending || cancelPlanMutation?.isPending}
|
||||
|
|
@ -290,7 +295,7 @@ const PlanCard = ({
|
|||
: undefined
|
||||
}
|
||||
onCancel={
|
||||
isCurrentPlan && plan.role !== UserRole.Free && cancelPlanMutation
|
||||
isCurrentPlan && cancelPlanMutation
|
||||
? () => {
|
||||
cancelPlanMutation.mutate()
|
||||
}
|
||||
|
|
@ -321,70 +326,48 @@ const PlanBadges = ({ isPopular }: { isPopular: boolean }) => (
|
|||
const PlanHeader = ({
|
||||
title,
|
||||
price,
|
||||
regularPrice,
|
||||
period,
|
||||
billingPeriod,
|
||||
monthlyPrice,
|
||||
yearlyPrice,
|
||||
savingsPercentage,
|
||||
description,
|
||||
discountPercentage,
|
||||
}: {
|
||||
title: string
|
||||
price: string
|
||||
regularPrice?: string
|
||||
period: string
|
||||
billingPeriod: "monthly" | "yearly"
|
||||
monthlyPrice: number
|
||||
yearlyPrice: number
|
||||
savingsPercentage: number
|
||||
description?: string
|
||||
discountPercentage?: number
|
||||
}) => (
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-semibold @md:text-lg">{title}</h3>
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-xl font-bold">{price}</span>
|
||||
{period && <span className="text-xs text-text-secondary @md:text-sm">/{period}</span>}
|
||||
</div>
|
||||
<div className="h-7 space-y-0.5 @md:h-8">
|
||||
{billingPeriod === "yearly" &&
|
||||
monthlyPrice > 0 &&
|
||||
yearlyPrice > 0 &&
|
||||
savingsPercentage > 0 && (
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-text-tertiary line-through">
|
||||
${(monthlyPrice * 12).toFixed(0)}/year
|
||||
</span>
|
||||
<span className="font-medium text-green">Save {savingsPercentage}%</span>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-xl font-bold">{price}</span>
|
||||
{period && <span className="text-xs text-text-secondary @md:text-sm">/{period}</span>}
|
||||
{regularPrice && (
|
||||
<span className="text-sm text-text-tertiary line-through">{regularPrice}</span>
|
||||
)}
|
||||
{billingPeriod === "yearly" && monthlyPrice > 0 && yearlyPrice > 0 && (
|
||||
<div className="text-xs text-text-secondary">
|
||||
${(yearlyPrice / 12).toFixed(1)}/month billed annually
|
||||
</div>
|
||||
{!!discountPercentage && discountPercentage > 0 && (
|
||||
<div className="inline-flex items-center gap-1.5 rounded-md bg-green/10 px-2 py-1">
|
||||
<span className="text-xs font-semibold text-green">-{discountPercentage}% OFF</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const PlanFeatures = ({ features }: { features: string[] }) => (
|
||||
<div className="space-y-1.5 @md:space-y-2">
|
||||
{features.map((feature) => (
|
||||
<div key={feature} className="flex items-start gap-2.5 @md:gap-3">
|
||||
<div className="mt-0.5 flex size-3.5 items-center justify-center rounded-full bg-green/10 @md:size-4">
|
||||
<i className="i-mgc-check-cute-re text-[10px] text-green @md:text-xs" />
|
||||
</div>
|
||||
<span className="text-xs leading-relaxed @md:text-sm">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
{description && (
|
||||
<p className="text-xs leading-relaxed text-text-secondary @md:text-sm">{description}</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
const PlanAction = ({
|
||||
isPopular,
|
||||
actionType,
|
||||
onSelect,
|
||||
onCancel,
|
||||
isLoading,
|
||||
daysLeft,
|
||||
}: {
|
||||
isPopular: boolean
|
||||
actionType: "current" | "upgrade" | "coming-soon" | "in-trial" | null
|
||||
actionType: "current" | "upgrade" | "coming-soon" | "in-trial" | "switch" | null
|
||||
onSelect?: () => void
|
||||
onCancel?: () => void
|
||||
isLoading?: boolean
|
||||
|
|
@ -396,33 +379,37 @@ const PlanAction = ({
|
|||
return {
|
||||
text: "Coming Soon",
|
||||
variant: "outline" as const,
|
||||
className: "w-full h-9 @md:h-10 text-xs @md:text-sm",
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
case "current": {
|
||||
return {
|
||||
text: typeof daysLeft === "number" ? `${daysLeft} days left` : "Current Plan",
|
||||
text: `${typeof daysLeft === "number" ? `${daysLeft} days left` : "Current Plan"}${onCancel ? " | Cancel" : ""}`,
|
||||
variant: "outline" as const,
|
||||
className: "w-full h-9 @md:h-10 text-xs @md:text-sm text-text-secondary",
|
||||
disabled: true,
|
||||
className: onCancel ? "" : "text-text-secondary",
|
||||
disabled: onCancel ? false : true,
|
||||
}
|
||||
}
|
||||
case "in-trial": {
|
||||
return {
|
||||
text: `In Trial (${daysLeft} days left)`,
|
||||
variant: "outline" as const,
|
||||
className: "w-full h-9 @md:h-10 text-xs @md:text-sm",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
case "upgrade": {
|
||||
return {
|
||||
text: "Upgrade",
|
||||
variant: isPopular ? undefined : ("outline" as const),
|
||||
className: isPopular
|
||||
? "w-full h-9 @md:h-10 text-xs @md:text-sm bg-gradient-to-r from-accent to-accent/80 hover:from-accent/90 hover:to-accent/70"
|
||||
: "w-full h-9 @md:h-10 text-xs @md:text-sm",
|
||||
className:
|
||||
"bg-gradient-to-r from-accent to-accent/80 hover:from-accent/90 hover:to-accent/70",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
case "switch": {
|
||||
return {
|
||||
text: "Switch",
|
||||
className:
|
||||
"bg-gradient-to-r from-accent to-accent/80 hover:from-accent/90 hover:to-accent/70",
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
|
@ -439,27 +426,92 @@ const PlanAction = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
variant={buttonConfig.variant}
|
||||
buttonClassName={buttonConfig.className}
|
||||
disabled={buttonConfig.disabled}
|
||||
onClick={buttonConfig.disabled ? undefined : onSelect}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{buttonConfig.text}
|
||||
</Button>
|
||||
<Button
|
||||
variant={buttonConfig.variant}
|
||||
buttonClassName={cn("w-full h-9 @md:h-10 text-xs @md:text-sm", buttonConfig.className)}
|
||||
disabled={buttonConfig.disabled}
|
||||
onClick={buttonConfig.disabled ? undefined : (onCancel ?? onSelect)}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{buttonConfig.text}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
{actionType === "current" && typeof daysLeft === "number" && onCancel && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
buttonClassName="w-full h-8 text-xs text-text-tertiary hover:text-red"
|
||||
onClick={onCancel}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
const PlanComparisonTable = ({ plans }: { plans: PaymentPlan[] }) => {
|
||||
const { t } = useTranslation("settings")
|
||||
|
||||
// Get all unique feature keys
|
||||
const allFeatureKeys = Array.from(
|
||||
new Set(plans.flatMap((plan) => Object.keys(plan.limit))),
|
||||
) as (keyof PaymentFeature)[]
|
||||
|
||||
// Filter out features that are all false/0/null
|
||||
const visibleFeatureKeys = allFeatureKeys.filter((key) =>
|
||||
plans.some((plan) => {
|
||||
const value = plan.limit[key]
|
||||
if (value == null) return false
|
||||
if (typeof value === "boolean" && !value) return false
|
||||
if (typeof value === "number" && value === 0) return false
|
||||
return true
|
||||
}),
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-xl border border-fill-tertiary bg-background">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-fill-tertiary bg-fill-secondary/50">
|
||||
<th className="sticky left-0 z-10 bg-fill-secondary/50 px-4 py-3 text-left text-sm font-semibold">
|
||||
Features
|
||||
</th>
|
||||
{plans.map((plan) => (
|
||||
<th key={plan.name} className="px-4 py-3 text-center text-sm font-semibold">
|
||||
{plan.name}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{visibleFeatureKeys.map((featureKey, index) => (
|
||||
<tr
|
||||
key={featureKey}
|
||||
className={cn(
|
||||
"border-b border-fill-tertiary transition-colors hover:bg-fill-secondary/30",
|
||||
index % 2 === 0 ? "bg-background" : "bg-fill-secondary/20",
|
||||
)}
|
||||
>
|
||||
<td className="sticky left-0 z-10 bg-inherit px-4 py-3 text-sm font-medium">
|
||||
{t(`plan.features.${featureKey}` as any)}
|
||||
</td>
|
||||
{plans.map((plan) => {
|
||||
const value = plan.limit[featureKey]
|
||||
const formattedValue = formatFeatureValue(featureKey, value)
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${plan.name}-${featureKey}`}
|
||||
className="px-4 py-3 text-center text-sm"
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"font-medium",
|
||||
formattedValue === "—" && "text-text-tertiary",
|
||||
formattedValue === "✓" && "text-green",
|
||||
formattedValue === "Unlimited" && "text-accent",
|
||||
)}
|
||||
>
|
||||
{formattedValue}
|
||||
</span>
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { ActionButton } from "@follow/components/ui/button/index.js"
|
|||
import { RSSHubLogo } from "@follow/components/ui/platform-icon/icons.js"
|
||||
import { RootPortal } from "@follow/components/ui/portal/index.js"
|
||||
import { EllipsisHorizontalTextWithTooltip } from "@follow/components/ui/typography/EllipsisWithTooltip.js"
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { useMeasure } from "@follow/hooks"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
|
|
@ -28,7 +27,6 @@ import { usePresentUserProfileModal } from "~/modules/profile/hooks"
|
|||
import { useSettingModal } from "~/modules/settings/modal/use-setting-modal-hack"
|
||||
import { signOut, useSession } from "~/queries/auth"
|
||||
|
||||
import { useActivationModal } from "../activation"
|
||||
import type { LoginProps } from "./LoginButton"
|
||||
import { LoginButton } from "./LoginButton"
|
||||
import { UserAvatar } from "./UserAvatar"
|
||||
|
|
@ -53,7 +51,6 @@ export const ProfileButton: FC<ProfileButtonProps> = memo((props) => {
|
|||
const navigate = useNavigate()
|
||||
|
||||
const role = useUserRole()
|
||||
const presentActivationModal = useActivationModal()
|
||||
const isInMASReview = useIsInMASReview()
|
||||
|
||||
if (status === "unauthenticated") {
|
||||
|
|
@ -83,7 +80,7 @@ export const ProfileButton: FC<ProfileButtonProps> = memo((props) => {
|
|||
<EllipsisHorizontalTextWithTooltip className="mx-auto max-w-[20ch] truncate text-lg">
|
||||
{user?.name}
|
||||
</EllipsisHorizontalTextWithTooltip>
|
||||
{serverConfig?.REFERRAL_ENABLED ? (
|
||||
{!isInMASReview && serverConfig?.PAYMENT_ENABLED ? (
|
||||
<UserProBadge
|
||||
role={role}
|
||||
withText
|
||||
|
|
@ -121,26 +118,22 @@ export const ProfileButton: FC<ProfileButtonProps> = memo((props) => {
|
|||
<DropdownMenuItem
|
||||
className="pl-3"
|
||||
onClick={() => {
|
||||
if (role !== UserRole.Trial && role !== UserRole.Free) {
|
||||
presentAchievement()
|
||||
} else {
|
||||
presentActivationModal()
|
||||
}
|
||||
presentAchievement()
|
||||
}}
|
||||
icon={<i className="i-mgc-trophy-cute-re" />}
|
||||
>
|
||||
{t("user_button.achievement")}
|
||||
</DropdownMenuItem>
|
||||
|
||||
{!isInMASReview && (
|
||||
{!isInMASReview && serverConfig?.PAYMENT_ENABLED && (
|
||||
<DropdownMenuItem
|
||||
className="pl-3"
|
||||
onClick={() => {
|
||||
navigate("/power")
|
||||
settingModalPresent("plan")
|
||||
}}
|
||||
icon={<i className="i-mgc-power-outline" />}
|
||||
>
|
||||
{t("user_button.power")}
|
||||
{t("activation.plan.title")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export const UserAvatar = ({
|
|||
{renderUserData?.name?.[0]}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
{serverConfig?.REFERRAL_ENABLED &&
|
||||
{serverConfig?.PAYMENT_ENABLED &&
|
||||
!userId &&
|
||||
role !== UserRole.Free &&
|
||||
role !== UserRole.Trial && (
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export const UserProBadge = ({
|
|||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<i className={cn("i-mgc-power block text-folo", iconClassName)} />
|
||||
<i className={cn("i-mgc-power block", iconClassName)} />
|
||||
{withText && <span className="text-xs">{UserRoleName[role]}</span>}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,16 +2,12 @@ import { Divider } from "@follow/components/ui/divider/Divider.js"
|
|||
import { useScrollElementUpdate } from "@follow/components/ui/scroll-area/hooks.js"
|
||||
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@follow/components/ui/tabs/index.jsx"
|
||||
import { UserRole } from "@follow/constants"
|
||||
import { useUserRole } from "@follow/store/user/hooks"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { createElement } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useSearchParams } from "react-router"
|
||||
|
||||
import { AppErrorBoundary } from "~/components/common/AppErrorBoundary"
|
||||
import { ErrorComponentType } from "~/components/errors/enum"
|
||||
import { useActivationModal } from "~/modules/activation"
|
||||
import { useSubViewTitle } from "~/modules/app-layout/subview/hooks"
|
||||
import { DiscoverForm } from "~/modules/discover/DiscoverForm"
|
||||
import { DiscoverImport } from "~/modules/discover/DiscoverImport"
|
||||
|
|
@ -24,7 +20,6 @@ import { Trending } from "~/modules/trending"
|
|||
const tabs: {
|
||||
name: I18nKeys
|
||||
value: string
|
||||
disableForTrial?: boolean
|
||||
}[] = [
|
||||
{
|
||||
name: "words.search",
|
||||
|
|
@ -41,7 +36,6 @@ const tabs: {
|
|||
{
|
||||
name: "words.inbox",
|
||||
value: "inbox",
|
||||
disableForTrial: true,
|
||||
},
|
||||
{
|
||||
name: "words.user",
|
||||
|
|
@ -62,18 +56,8 @@ export function Component() {
|
|||
const { t } = useTranslation()
|
||||
useSubViewTitle("words.discover")
|
||||
|
||||
const presentActivationModal = useActivationModal()
|
||||
const role = useUserRole()
|
||||
const { onUpdateMaxScroll } = useScrollElementUpdate()
|
||||
|
||||
const currentTabs = tabs.map((tab) => {
|
||||
const disabled = tab.disableForTrial && (role === UserRole.Free || role === UserRole.Trial)
|
||||
return {
|
||||
...tab,
|
||||
disabled,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex size-full flex-col px-6 py-8">
|
||||
{/* Simple Header */}
|
||||
|
|
@ -100,17 +84,12 @@ export function Component() {
|
|||
<div className="mb-8">
|
||||
<ScrollArea.ScrollArea flex orientation="horizontal" rootClassName="w-full">
|
||||
<TabsList className="relative flex w-full">
|
||||
{currentTabs.map((tab) => (
|
||||
{tabs.map((tab) => (
|
||||
<TabsTrigger
|
||||
key={tab.name}
|
||||
value={tab.value}
|
||||
className={cn(tab.disabled && "cursor-not-allowed opacity-50")}
|
||||
onClick={() => {
|
||||
if (tab.disabled) {
|
||||
presentActivationModal()
|
||||
} else {
|
||||
onUpdateMaxScroll?.()
|
||||
}
|
||||
onUpdateMaxScroll?.()
|
||||
}}
|
||||
>
|
||||
{t(tab.name)}
|
||||
|
|
@ -122,7 +101,7 @@ export function Component() {
|
|||
|
||||
{/* Tab Content */}
|
||||
<div className="space-y-8">
|
||||
{currentTabs.map((tab) => (
|
||||
{tabs.map((tab) => (
|
||||
<TabsContent key={tab.name} value={tab.value} className="mt-0">
|
||||
<div className={tab.value === "inbox" ? "" : "flex flex-col items-center"}>
|
||||
{createElement(TabComponent[tab.value]! || TabComponent.default, {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { UserRole } from "@follow/constants"
|
||||
|
||||
import { SettingLists } from "~/modules/settings/tabs/lists"
|
||||
import { SettingsTitle } from "~/modules/settings/title"
|
||||
import { defineSettingPageData, DisableWhy } from "~/modules/settings/utils"
|
||||
import { defineSettingPageData } from "~/modules/settings/utils"
|
||||
|
||||
const iconName = "i-mgc-rada-cute-re"
|
||||
const priority = (1000 << 2) + 10
|
||||
|
|
@ -11,10 +9,6 @@ export const loader = defineSettingPageData({
|
|||
icon: iconName,
|
||||
name: "titles.lists",
|
||||
priority,
|
||||
disableIf: (ctx) => [
|
||||
ctx.role === UserRole.Free || ctx.role === UserRole.Trial,
|
||||
DisableWhy.NotActivation,
|
||||
],
|
||||
hideIf: (ctx) => ctx.isInMASReview,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { UserRole } from "@follow/constants"
|
||||
|
||||
import { SettingNotifications } from "~/modules/settings/tabs/notifications"
|
||||
import { SettingsTitle } from "~/modules/settings/title"
|
||||
import { defineSettingPageData, DisableWhy } from "~/modules/settings/utils"
|
||||
import { defineSettingPageData } from "~/modules/settings/utils"
|
||||
|
||||
const iconName = "i-mgc-notification-cute-re"
|
||||
const priority = (1000 << 1) + 50
|
||||
|
|
@ -11,10 +9,6 @@ export const loader = defineSettingPageData({
|
|||
icon: iconName,
|
||||
name: "titles.notifications",
|
||||
priority,
|
||||
disableIf: (ctx) => [
|
||||
ctx.role === UserRole.Free || ctx.role === UserRole.Trial,
|
||||
DisableWhy.NotActivation,
|
||||
],
|
||||
})
|
||||
|
||||
export function Component() {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import { SettingsTitle } from "~/modules/settings/title"
|
|||
import { defineSettingPageData } from "~/modules/settings/utils"
|
||||
|
||||
const iconName = "i-mgc-power-outline"
|
||||
const priority = (1000 << 2) + 30
|
||||
const priority = (1000 << 1) + 17
|
||||
|
||||
export const loader = defineSettingPageData({
|
||||
icon: iconName,
|
||||
name: "titles.plan.short",
|
||||
title: "titles.plan.long",
|
||||
priority,
|
||||
hideIf: (ctx, serverConfigs) => ctx.isInMASReview || !serverConfigs?.REFERRAL_ENABLED,
|
||||
hideIf: (ctx, serverConfigs) => ctx.isInMASReview || !serverConfigs?.PAYMENT_ENABLED,
|
||||
})
|
||||
|
||||
export function Component() {
|
||||
|
|
|
|||
|
|
@ -1,67 +1,16 @@
|
|||
import { UserRole, UserRoleName } from "@follow/constants"
|
||||
import { getStorageNS } from "@follow/utils/ns"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { useEffect } from "react"
|
||||
|
||||
import { setIntegrationIdentify } from "~/initialize/helper"
|
||||
import { useSettingModal } from "~/modules/settings/modal/useSettingModal"
|
||||
import { useSession } from "~/queries/auth"
|
||||
|
||||
export const UserProvider = () => {
|
||||
const { session } = useSession()
|
||||
|
||||
const settingModalPresent = useSettingModal()
|
||||
|
||||
useEffect(() => {
|
||||
if (!session?.user) return
|
||||
|
||||
setIntegrationIdentify(session.user)
|
||||
}, [session?.user])
|
||||
|
||||
const roleEndDate = useMemo(
|
||||
() =>
|
||||
session?.roleEndAt
|
||||
? typeof session.roleEndAt === "string"
|
||||
? new Date(session.roleEndAt)
|
||||
: session.roleEndAt
|
||||
: undefined,
|
||||
[session?.roleEndAt],
|
||||
)
|
||||
useEffect(() => {
|
||||
if (!session?.role) return
|
||||
|
||||
const itemKey = getStorageNS("pro-preview-toast-dismissed")
|
||||
|
||||
const isToastDismissed = localStorage.getItem(itemKey)
|
||||
|
||||
const sessionRole = session.role as string
|
||||
|
||||
if (sessionRole && sessionRole !== UserRole.Pro && !isToastDismissed) {
|
||||
const message =
|
||||
sessionRole === UserRole.Free || sessionRole === UserRole.Trial
|
||||
? `You are currently on the ${UserRoleName[UserRole.Free]} plan. Some features may be limited.`
|
||||
: sessionRole === UserRole.PreProTrial
|
||||
? `You are currently on the ${UserRoleName[UserRole.PreProTrial]} plan.${roleEndDate ? ` It will end on ${roleEndDate.toLocaleDateString()}.` : ""}`
|
||||
: ""
|
||||
if (!message) {
|
||||
localStorage.setItem(itemKey, "true")
|
||||
return
|
||||
}
|
||||
toast.warning(message, {
|
||||
duration: Number.POSITIVE_INFINITY,
|
||||
action: {
|
||||
label: "More",
|
||||
onClick: () => {
|
||||
settingModalPresent("plan")
|
||||
localStorage.setItem(itemKey, "true")
|
||||
},
|
||||
},
|
||||
onDismiss: () => {
|
||||
localStorage.setItem(itemKey, "true")
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [roleEndDate, session?.role, settingModalPresent])
|
||||
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@better-auth/expo": "1.3.28",
|
||||
"@expo/metro-runtime": "5.0.4",
|
||||
"@expo/react-native-action-sheet": "4.1.1",
|
||||
"@follow-app/client-sdk": "catalog:",
|
||||
"@follow/components": "workspace:*",
|
||||
"@follow/constants": "workspace:*",
|
||||
"@follow/database": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { FollowAPIError } from "@follow-app/client-sdk"
|
||||
import { t } from "i18next"
|
||||
import { FetchError } from "ofetch"
|
||||
|
||||
|
|
@ -25,6 +26,20 @@ export const getFetchErrorInfo = (
|
|||
}
|
||||
}
|
||||
|
||||
if (error instanceof FollowAPIError && error.code) {
|
||||
const code = Number(error.code)
|
||||
try {
|
||||
const i18nKey = `errors:${code}` as any
|
||||
const i18nMessage = t(i18nKey) === i18nKey ? error.message : t(i18nKey)
|
||||
return {
|
||||
message: i18nMessage,
|
||||
code,
|
||||
}
|
||||
} catch {
|
||||
return { message: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
return { message: error.message }
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +82,17 @@ export const toastFetchError = (error: Error, { title: _title }: { title?: strin
|
|||
}
|
||||
}
|
||||
|
||||
if (error instanceof FollowAPIError && error.code) {
|
||||
code = Number(error.code)
|
||||
try {
|
||||
const tValue = t(`errors:${code}` as any)
|
||||
const i18nMessage = tValue === code?.toString() ? error.message : tValue
|
||||
message = i18nMessage
|
||||
} catch {
|
||||
message = error.message
|
||||
}
|
||||
}
|
||||
|
||||
// 2fa errors are handled by the form
|
||||
if (code === 4007 || code === 4008) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { Switch } from "@/src/components/ui/switch/Switch"
|
|||
import { Text } from "@/src/components/ui/typography/Text"
|
||||
import { Book6CuteReIcon } from "@/src/icons/book_6_cute_re"
|
||||
import { Magic2CuteFiIcon } from "@/src/icons/magic_2_cute_fi"
|
||||
import { toastFetchError } from "@/src/lib/error-parser"
|
||||
import { useNavigation } from "@/src/lib/navigation/hooks"
|
||||
import { toast } from "@/src/lib/toast"
|
||||
import { accentColor } from "@/src/theme/colors"
|
||||
|
|
@ -129,8 +130,8 @@ const SaveRuleButton = ({ disabled }: { disabled?: boolean }) => {
|
|||
navigation.back()
|
||||
toast.success("Actions saved")
|
||||
},
|
||||
onError(errorMessage) {
|
||||
toast.error(errorMessage)
|
||||
onError(error) {
|
||||
toastFetchError(error)
|
||||
},
|
||||
})
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -7,22 +7,17 @@ import type { ProductPurchase } from "expo-iap"
|
|||
import { useIAP } from "expo-iap"
|
||||
import { openURL } from "expo-linking"
|
||||
import { useEffect } from "react"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
import { Linking, Pressable, View } from "react-native"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Pressable, View } from "react-native"
|
||||
|
||||
import { useServerConfigs } from "@/src/atoms/server-configs"
|
||||
import {
|
||||
NavigationBlurEffectHeaderView,
|
||||
SafeNavigationScrollView,
|
||||
} from "@/src/components/layouts/views/SafeNavigationScrollView"
|
||||
import {
|
||||
GroupedInformationCell,
|
||||
GroupedInsetListCard,
|
||||
} from "@/src/components/ui/grouped/GroupedList"
|
||||
// Plan configuration types
|
||||
import { Text } from "@/src/components/ui/typography/Text"
|
||||
import { CheckLineIcon } from "@/src/icons/check_line"
|
||||
import { PowerOutlineIcon } from "@/src/icons/power_outline"
|
||||
import { TimeCuteReIcon } from "@/src/icons/time_cute_re"
|
||||
import { followClient } from "@/src/lib/api-client"
|
||||
import { authClient } from "@/src/lib/auth"
|
||||
|
|
@ -30,7 +25,6 @@ import { useNavigation } from "@/src/lib/navigation/hooks"
|
|||
import type { NavigationControllerView } from "@/src/lib/navigation/types"
|
||||
import { isIOS } from "@/src/lib/platform"
|
||||
import { proxyEnv } from "@/src/lib/proxy-env"
|
||||
import { accentColor } from "@/src/theme/colors"
|
||||
|
||||
import { ReferralScreen } from "./Referral"
|
||||
|
||||
|
|
@ -55,6 +49,7 @@ const PLAN_TIER_MAP: Record<UserRole, number> = {
|
|||
// Same as Free (deprecated)
|
||||
[UserRole.PreProTrial]: 2,
|
||||
[UserRole.Pro]: 3,
|
||||
[UserRole.Plus]: 4,
|
||||
}
|
||||
|
||||
// Plan configurations
|
||||
|
|
@ -129,7 +124,6 @@ export const PlanScreen: NavigationControllerView = () => {
|
|||
const navigation = useNavigation()
|
||||
const { t } = useTranslation("settings")
|
||||
const serverConfigs = useServerConfigs()
|
||||
const ruleLink = serverConfigs?.REFERRAL_RULE_LINK
|
||||
const requiredInvitationsAmount = serverConfigs?.REFERRAL_REQUIRED_INVITATIONS || 3
|
||||
const { data: referralInfo } = useReferralInfoQuery()
|
||||
const validInvitationsAmount = referralInfo?.invitations.filter((i) => i.usedAt).length || 0
|
||||
|
|
@ -165,40 +159,6 @@ export const PlanScreen: NavigationControllerView = () => {
|
|||
className="bg-system-grouped-background"
|
||||
Header={<NavigationBlurEffectHeaderView title={t("titles.plan.long")} />}
|
||||
>
|
||||
<View className="mt-6">
|
||||
<GroupedInsetListCard>
|
||||
<GroupedInformationCell
|
||||
title={t("titles.plan.short")}
|
||||
icon={<PowerOutlineIcon height={40} width={40} color="#fff" />}
|
||||
iconBackgroundColor={accentColor}
|
||||
>
|
||||
<Trans
|
||||
ns="settings"
|
||||
i18nKey="plan.description"
|
||||
parent={({ children }: { children: React.ReactNode }) => (
|
||||
<Text className="mt-3 text-left text-base leading-tight text-label">
|
||||
{children}
|
||||
</Text>
|
||||
)}
|
||||
components={{
|
||||
Link: (
|
||||
<Text
|
||||
className="text-accent"
|
||||
onPress={() => {
|
||||
if (ruleLink) {
|
||||
Linking.openURL(ruleLink)
|
||||
}
|
||||
}}
|
||||
>
|
||||
Learn more
|
||||
</Text>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</GroupedInformationCell>
|
||||
</GroupedInsetListCard>
|
||||
</View>
|
||||
|
||||
<View className="gap-4 p-4">
|
||||
{PLAN_CONFIGS.map((plan) => {
|
||||
const isProPreview = plan.id === "pro-preview"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"activation.plan.upgrade": "Upgrade",
|
||||
"activation.title": "Invitation Code",
|
||||
"ai.summary_not_available": "Summary not available",
|
||||
"ai.summary_upgrade_required_title": "Upgrade plan to continue using AI summary",
|
||||
"app.copy_logo_svg": "Copy Logo SVG",
|
||||
"app.copy_logo_text_svg": "Copy Logo Text SVG",
|
||||
"app.toggle_sidebar": "Toggle Sidebar",
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@
|
|||
"appearance.use_pointer_cursor.label": "Use Hand Cursor",
|
||||
"appearance.words.customize": "Customize",
|
||||
"common.give_star": "<HeartIcon />Love our product? <Link>Give us a star on GitHub!</Link>",
|
||||
"control.paid_badge.free_limited": "This feature is limited for free plan",
|
||||
"control.paid_badge.plus_or_higher": "This feature requires a Plus plan or higher to use",
|
||||
"customizeToolbar.more_actions.description": "Will be shown in the dropdown menu.",
|
||||
"customizeToolbar.more_actions.title": "More Actions",
|
||||
"customizeToolbar.quick_actions.description": "Customize and reorder your frequently used actions.",
|
||||
|
|
@ -553,7 +555,23 @@
|
|||
"notifications.test": "Test Notification",
|
||||
"notifications.test_success": "Test notification sent successfully.",
|
||||
"notifications.token": "Client Token",
|
||||
"plan.description": "Enjoy every Pro feature from day one—new users get a full‑access trial, and you can keep those perks simply by inviting friends or making a small support contribution. <Link>Learn more</Link>.",
|
||||
"plan.descriptions.free": "Great for beginners.",
|
||||
"plan.descriptions.plus": "Unlock AI features and more feeds.",
|
||||
"plan.descriptions.pro": "Full access to the best of Folo.",
|
||||
"plan.features.AI_CREDIT": "AI Credits",
|
||||
"plan.features.AI_ENTRY_TRANSLATION": "AI Entry Translation",
|
||||
"plan.features.BOOSTS": "Feed Boosts",
|
||||
"plan.features.INTEGRATION_SUPPORTED": "Third-Party Integrations",
|
||||
"plan.features.MAX_ACTIONS": "Actions",
|
||||
"plan.features.MAX_AI_ENTRY_SUMMARY_PER_DAY": "AI Summaries Per Day",
|
||||
"plan.features.MAX_AI_REQUESTS_PER_DAY": "AI Requests Per Day",
|
||||
"plan.features.MAX_AI_TASKS": "AI Tasks",
|
||||
"plan.features.MAX_INBOXES": "Inbox",
|
||||
"plan.features.MAX_LISTS": "List",
|
||||
"plan.features.MAX_RSSHUB_SUBSCRIPTIONS": "RSSHub Subscriptions",
|
||||
"plan.features.MAX_SUBSCRIPTIONS": "Feed Subscriptions",
|
||||
"plan.features.PRIORITY_SUPPORT": "Priority Support",
|
||||
"plan.features.PRIVATE_SUBSCRIPTION": "Private Subscriptions",
|
||||
"privacy.privacy": "Privacy",
|
||||
"privacy.terms": "Terms",
|
||||
"profile.avatar.cropInstructions": "Drag the crop area to adjust your avatar",
|
||||
|
|
|
|||
|
|
@ -208,6 +208,8 @@
|
|||
"appearance.use_pointer_cursor.description": "マウスがインタラクティブな要素の上にあるとき、カーソルは手の形になります。",
|
||||
"appearance.use_pointer_cursor.label": "手の形のカーソルを使用する",
|
||||
"common.give_star": "<HeartIcon />私たちの製品が好きですか?<Link>GitHub で Star を付けましょう!</Link>",
|
||||
"control.paid_badge.free_limited": "この機能は無料プランでは制限されています",
|
||||
"control.paid_badge.plus_or_higher": "この機能を利用するには Plus プラン以上が必要です",
|
||||
"customizeToolbar.more_actions.description": "ドロップダウンメニューに表示されます",
|
||||
"customizeToolbar.more_actions.title": "その他のアクション",
|
||||
"customizeToolbar.quick_actions.description": "よく使用するアクションをカスタマイズして並べ替える。",
|
||||
|
|
@ -549,7 +551,20 @@
|
|||
"notifications.test": "テスト通知",
|
||||
"notifications.test_success": "テスト通知が正常に送信されました。",
|
||||
"notifications.token": "クライアントトークン",
|
||||
"plan.description": "すべての Pro 機能を初日から利用できます。新しいユーザーはフルアクセスのトライアルを受け取ります。友達を招待するか、少額のサポート寄付をすることで、その特典を維持できます。<Link>詳細はこちら</Link>。",
|
||||
"plan.features.AI_CREDIT": "AI クレジット",
|
||||
"plan.features.AI_ENTRY_TRANSLATION": "AI エントリ翻訳",
|
||||
"plan.features.BOOSTS": "購読フィードの加速",
|
||||
"plan.features.INTEGRATION_SUPPORTED": "サードパーティ統合",
|
||||
"plan.features.MAX_ACTIONS": "自動化アクション",
|
||||
"plan.features.MAX_AI_ENTRY_SUMMARY_PER_DAY": "1日あたりのAI要約数",
|
||||
"plan.features.MAX_AI_REQUESTS_PER_DAY": "1日あたりのAIリクエスト数",
|
||||
"plan.features.MAX_AI_TASKS": "AI タスク",
|
||||
"plan.features.MAX_INBOXES": "受信トレイフィード",
|
||||
"plan.features.MAX_LISTS": "カスタムリスト",
|
||||
"plan.features.MAX_RSSHUB_SUBSCRIPTIONS": "RSSHub サブスクリプション",
|
||||
"plan.features.MAX_SUBSCRIPTIONS": "フィードサブスクリプション",
|
||||
"plan.features.PRIORITY_SUPPORT": "優先サポート",
|
||||
"plan.features.PRIVATE_SUBSCRIPTION": "プライベートサブスクリプション",
|
||||
"privacy.privacy": "プライバシー",
|
||||
"privacy.terms": "利用規約",
|
||||
"profile.avatar.cropInstructions": "ドラッグしてトリミングエリアを調整します",
|
||||
|
|
|
|||
|
|
@ -208,6 +208,8 @@
|
|||
"appearance.use_pointer_cursor.description": "当鼠标悬停在任何可交互元素上时,光标会显示为手型光标。",
|
||||
"appearance.use_pointer_cursor.label": "使用手型光标",
|
||||
"common.give_star": "<HeartIcon />喜欢我们的产品吗? <Link>在 GitHub 上给我们「Star」吧!</Link>",
|
||||
"control.paid_badge.free_limited": "此功能在免费计划中有限制",
|
||||
"control.paid_badge.plus_or_higher": "此功能需要 Plus 计划或更高级别才能使用",
|
||||
"customizeToolbar.more_actions.description": "将显示在下拉菜单中",
|
||||
"customizeToolbar.more_actions.title": "更多操作",
|
||||
"customizeToolbar.quick_actions.description": "自定义并重新排列您常用的操作",
|
||||
|
|
@ -549,7 +551,20 @@
|
|||
"notifications.test": "测试通知",
|
||||
"notifications.test_success": "测试通知发送成功。",
|
||||
"notifications.token": "客户端令牌",
|
||||
"plan.description": "从第一天起享受所有专业版功能——新用户可获得完整试用,只需邀请朋友或小额支持贡献即可保留这些权益。<Link>了解更多</Link>。",
|
||||
"plan.features.AI_CREDIT": "AI 积分",
|
||||
"plan.features.AI_ENTRY_TRANSLATION": "AI 条目翻译",
|
||||
"plan.features.BOOSTS": "订阅源加速",
|
||||
"plan.features.INTEGRATION_SUPPORTED": "第三方集成",
|
||||
"plan.features.MAX_ACTIONS": "自动化操作",
|
||||
"plan.features.MAX_AI_ENTRY_SUMMARY_PER_DAY": "每日 AI 摘要次数",
|
||||
"plan.features.MAX_AI_REQUESTS_PER_DAY": "每日 AI 请求次数",
|
||||
"plan.features.MAX_AI_TASKS": "AI 任务",
|
||||
"plan.features.MAX_INBOXES": "收件箱订阅源",
|
||||
"plan.features.MAX_LISTS": "自定义列表",
|
||||
"plan.features.MAX_RSSHUB_SUBSCRIPTIONS": "RSSHub 订阅",
|
||||
"plan.features.MAX_SUBSCRIPTIONS": "订阅源数量",
|
||||
"plan.features.PRIORITY_SUPPORT": "优先支持",
|
||||
"plan.features.PRIVATE_SUBSCRIPTION": "私有订阅",
|
||||
"privacy.privacy": "隐私政策",
|
||||
"privacy.terms": "服务条款",
|
||||
"profile.avatar.cropInstructions": "拖动裁剪区域以调整头像",
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@
|
|||
"appearance.use_pointer_cursor.label": "使用手指遊標",
|
||||
"appearance.words.customize": "自訂",
|
||||
"common.give_star": "<HeartIcon />喜歡我們的產品嗎? <Link>在 GitHub 上給我們 Star 吧!</Link>",
|
||||
"control.paid_badge.free_limited": "此功能在免費方案中受限",
|
||||
"control.paid_badge.plus_or_higher": "此功能需要 Plus 方案或更高級別才能使用",
|
||||
"customizeToolbar.more_actions.description": "將顯示在下拉選單中",
|
||||
"customizeToolbar.more_actions.title": "更多操作",
|
||||
"customizeToolbar.quick_actions.description": "自訂並重新排列您常用的操作",
|
||||
|
|
@ -553,7 +555,6 @@
|
|||
"notifications.test": "測試通知",
|
||||
"notifications.test_success": "測試通知發送成功。",
|
||||
"notifications.token": "客户端令牌",
|
||||
"plan.description": "享受每個專業功能,從第一天開始 — 新用戶獲得完整訪問試用,您可以通過邀請朋友或進行小額支持貢獻來保留這些特權。<Link>了解更多</Link>。",
|
||||
"privacy.privacy": "隱私政策",
|
||||
"privacy.terms": "服務條款",
|
||||
"profile.avatar.cropInstructions": "拖曳裁切區域以調整你的頭像",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@follow/configs": "workspace:*",
|
||||
"@follow/constants": "workspace:*",
|
||||
"@follow/hooks": "workspace:*",
|
||||
"@follow/shared": "workspace:*",
|
||||
"@follow/store": "workspace:*",
|
||||
"@follow/types": "workspace:*",
|
||||
"@follow/utils": "workspace:*",
|
||||
"jotai": "2.15.0"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { UserRole } from "@follow/constants"
|
||||
import { useRefValue } from "@follow/hooks"
|
||||
import { getSettingPaidLevel, SettingPaidLevels } from "@follow/shared/settings/constants"
|
||||
import { useUserStore } from "@follow/store/user/store"
|
||||
import { EventBus } from "@follow/utils/event-bus"
|
||||
import { createAtomHooks } from "@follow/utils/jotai"
|
||||
import { getStorageNS } from "@follow/utils/ns"
|
||||
|
|
@ -25,21 +28,66 @@ export const createSettingAtom = <T extends object>(
|
|||
getOnInit: true,
|
||||
})
|
||||
|
||||
const [, , useSettingValue, , getSettings, setSettings] = createAtomHooks(atom)
|
||||
const [, , useSettingValueRaw, , getSettingsRaw, setSettings] = createAtomHooks(atom)
|
||||
|
||||
const initializeDefaultSettings = () => {
|
||||
const currentSettings = getSettings()
|
||||
const currentSettings = getSettingsRaw()
|
||||
const defaultSettings = createDefaultSettings()
|
||||
if (typeof currentSettings !== "object") setSettings(defaultSettings)
|
||||
const newSettings = { ...defaultSettings, ...currentSettings }
|
||||
setSettings(newSettings)
|
||||
}
|
||||
|
||||
const selectAtomCacheMap = {} as Record<keyof ReturnType<typeof getSettings>, any>
|
||||
const selectAtomCacheMap = {} as Record<keyof ReturnType<typeof getSettingsRaw>, any>
|
||||
|
||||
const noopAtom = jotaiAtom(null)
|
||||
|
||||
const useMaybeSettingKey = <T extends keyof ReturnType<typeof getSettings>>(key: Nullable<T>) => {
|
||||
const canUpdatePaidSetting = (requiredLevel?: SettingPaidLevels) => {
|
||||
if (requiredLevel === undefined) return true
|
||||
if (
|
||||
requiredLevel === SettingPaidLevels.Free ||
|
||||
requiredLevel === SettingPaidLevels.FreeLimited
|
||||
) {
|
||||
return true
|
||||
}
|
||||
const role = useUserStore.getState().role ?? UserRole.Free
|
||||
return role !== UserRole.Free && role !== UserRole.Trial
|
||||
}
|
||||
|
||||
const resolveAccessibleValue = (
|
||||
key: string,
|
||||
value: unknown,
|
||||
defaults: Record<string, unknown>,
|
||||
) => {
|
||||
const requiredLevel = getSettingPaidLevel(settingKey, key)
|
||||
if (requiredLevel === undefined || canUpdatePaidSetting(requiredLevel)) {
|
||||
return value
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(defaults, key)) {
|
||||
return defaults[key]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const sanitizeSettingsSnapshot = (settings: ReturnType<typeof getSettingsRaw>) => {
|
||||
const defaults = createDefaultSettings() as Record<string, unknown>
|
||||
const raw = settings as Record<string, unknown>
|
||||
let sanitized: Record<string, unknown> | null = null
|
||||
|
||||
for (const key of Object.keys(defaults)) {
|
||||
const safeValue = resolveAccessibleValue(key, raw[key], defaults)
|
||||
if (safeValue !== raw[key]) {
|
||||
if (!sanitized) sanitized = { ...raw }
|
||||
sanitized[key] = safeValue
|
||||
}
|
||||
}
|
||||
|
||||
return (sanitized ?? raw) as ReturnType<typeof getSettingsRaw>
|
||||
}
|
||||
|
||||
const useMaybeSettingKey = <T extends keyof ReturnType<typeof getSettingsRaw>>(
|
||||
key: Nullable<T>,
|
||||
) => {
|
||||
// @ts-expect-error
|
||||
let selectedAtom: Record<keyof T, any>[T] | null = null
|
||||
if (key) {
|
||||
|
|
@ -52,15 +100,20 @@ export const createSettingAtom = <T extends object>(
|
|||
selectedAtom = noopAtom
|
||||
}
|
||||
|
||||
return useAtomValue(selectedAtom) as ReturnType<typeof getSettings>[T]
|
||||
const value = useAtomValue(selectedAtom) as ReturnType<typeof getSettingsRaw>[T]
|
||||
if (!key) return value
|
||||
const defaults = createDefaultSettings() as Record<string, unknown>
|
||||
return resolveAccessibleValue(String(key), value, defaults) as ReturnType<
|
||||
typeof getSettingsRaw
|
||||
>[T]
|
||||
}
|
||||
|
||||
const useSettingKey = <T extends keyof ReturnType<typeof getSettings>>(key: T) => {
|
||||
return useMaybeSettingKey(key) as ReturnType<typeof getSettings>[T]
|
||||
const useSettingKey = <T extends keyof ReturnType<typeof getSettingsRaw>>(key: T) => {
|
||||
return useMaybeSettingKey(key) as ReturnType<typeof getSettingsRaw>[T]
|
||||
}
|
||||
|
||||
function useSettingKeys<
|
||||
T extends keyof ReturnType<typeof getSettings>,
|
||||
T extends keyof ReturnType<typeof getSettingsRaw>,
|
||||
K1 extends T,
|
||||
K2 extends T,
|
||||
K3 extends T,
|
||||
|
|
@ -84,22 +137,22 @@ export const createSettingAtom = <T extends object>(
|
|||
useMaybeSettingKey(keys[8]),
|
||||
useMaybeSettingKey(keys[9]),
|
||||
] as [
|
||||
ReturnType<typeof getSettings>[K1],
|
||||
ReturnType<typeof getSettings>[K2],
|
||||
ReturnType<typeof getSettings>[K3],
|
||||
ReturnType<typeof getSettings>[K4],
|
||||
ReturnType<typeof getSettings>[K5],
|
||||
ReturnType<typeof getSettings>[K6],
|
||||
ReturnType<typeof getSettings>[K7],
|
||||
ReturnType<typeof getSettings>[K8],
|
||||
ReturnType<typeof getSettings>[K9],
|
||||
ReturnType<typeof getSettings>[K10],
|
||||
ReturnType<typeof getSettingsRaw>[K1],
|
||||
ReturnType<typeof getSettingsRaw>[K2],
|
||||
ReturnType<typeof getSettingsRaw>[K3],
|
||||
ReturnType<typeof getSettingsRaw>[K4],
|
||||
ReturnType<typeof getSettingsRaw>[K5],
|
||||
ReturnType<typeof getSettingsRaw>[K6],
|
||||
ReturnType<typeof getSettingsRaw>[K7],
|
||||
ReturnType<typeof getSettingsRaw>[K8],
|
||||
ReturnType<typeof getSettingsRaw>[K9],
|
||||
ReturnType<typeof getSettingsRaw>[K10],
|
||||
]
|
||||
}
|
||||
|
||||
const useSettingSelector = <
|
||||
T extends keyof ReturnType<typeof getSettings>,
|
||||
S extends ReturnType<typeof getSettings>,
|
||||
T extends keyof ReturnType<typeof getSettingsRaw>,
|
||||
S extends ReturnType<typeof getSettingsRaw>,
|
||||
R = S[T],
|
||||
>(
|
||||
selector: (s: S) => R,
|
||||
|
|
@ -107,18 +160,29 @@ export const createSettingAtom = <T extends object>(
|
|||
const stableSelector = useRefValue(selector)
|
||||
|
||||
return useAtomValue(
|
||||
// @ts-expect-error
|
||||
useMemo(() => selectAtom(atom, stableSelector.current, shallow), [stableSelector]),
|
||||
useMemo(
|
||||
() =>
|
||||
selectAtom(
|
||||
atom,
|
||||
(state) => stableSelector.current(sanitizeSettingsSnapshot(state) as S),
|
||||
shallow,
|
||||
),
|
||||
[stableSelector],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const setSetting = <K extends keyof ReturnType<typeof getSettings>>(
|
||||
const setSetting = <K extends keyof ReturnType<typeof getSettingsRaw>>(
|
||||
key: K,
|
||||
value: ReturnType<typeof getSettings>[K],
|
||||
value: ReturnType<typeof getSettingsRaw>[K],
|
||||
) => {
|
||||
const requiredLevel = getSettingPaidLevel(settingKey, String(key))
|
||||
if (!canUpdatePaidSetting(requiredLevel)) {
|
||||
return
|
||||
}
|
||||
const updated = Date.now()
|
||||
setSettings({
|
||||
...getSettings(),
|
||||
...getSettingsRaw(),
|
||||
[key]: value,
|
||||
|
||||
updated,
|
||||
|
|
@ -135,6 +199,15 @@ export const createSettingAtom = <T extends object>(
|
|||
setSettings(createDefaultSettings())
|
||||
}
|
||||
|
||||
const useSettingValue = () => {
|
||||
const value = useSettingValueRaw()
|
||||
return useMemo(() => sanitizeSettingsSnapshot(value), [value])
|
||||
}
|
||||
|
||||
const getSettings = () => {
|
||||
return sanitizeSettingsSnapshot(getSettingsRaw())
|
||||
}
|
||||
|
||||
Object.defineProperty(useSettingValue, "select", {
|
||||
value: useSettingSelector,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export enum UserRole {
|
|||
// TODO: remove this
|
||||
Trial = "trial",
|
||||
Pro = "pro",
|
||||
Plus = "plus",
|
||||
}
|
||||
|
||||
export const UserRoleName: Record<UserRole, string> = {
|
||||
|
|
@ -27,4 +28,5 @@ export const UserRoleName: Record<UserRole, string> = {
|
|||
*/
|
||||
[UserRole.Trial]: "Free",
|
||||
[UserRole.Pro]: "Pro",
|
||||
[UserRole.Plus]: "Plus",
|
||||
} as const
|
||||
|
|
|
|||
|
|
@ -1,4 +1,57 @@
|
|||
import type { AccentColor } from "./interface"
|
||||
import type {
|
||||
AccentColor,
|
||||
AISettings,
|
||||
GeneralSettings,
|
||||
IntegrationSettings,
|
||||
UISettings,
|
||||
} from "./interface"
|
||||
|
||||
export enum SettingPaidLevels {
|
||||
Free,
|
||||
FreeLimited,
|
||||
Plus,
|
||||
}
|
||||
|
||||
type PartialRecord<K extends PropertyKey, V> = Partial<Record<K, V>>
|
||||
|
||||
export const PAID_SETTINGS = {
|
||||
general: {
|
||||
summary: SettingPaidLevels.FreeLimited,
|
||||
translation: SettingPaidLevels.Plus,
|
||||
translationMode: SettingPaidLevels.Plus,
|
||||
hidePrivateSubscriptionsInTimeline: SettingPaidLevels.Plus,
|
||||
},
|
||||
ui: {
|
||||
hideExtraBadge: SettingPaidLevels.Plus,
|
||||
hideRecentReader: SettingPaidLevels.Plus,
|
||||
},
|
||||
integration: {
|
||||
enableCubox: SettingPaidLevels.Plus,
|
||||
enableObsidian: SettingPaidLevels.Plus,
|
||||
enableOutline: SettingPaidLevels.Plus,
|
||||
enableReadwise: SettingPaidLevels.Plus,
|
||||
enableZotero: SettingPaidLevels.Plus,
|
||||
enableInstapaper: SettingPaidLevels.Plus,
|
||||
enableReadeck: SettingPaidLevels.Plus,
|
||||
enableEagle: SettingPaidLevels.Plus,
|
||||
enableQBittorrent: SettingPaidLevels.Plus,
|
||||
enableCustomIntegration: SettingPaidLevels.Plus,
|
||||
},
|
||||
ai: {},
|
||||
} as const satisfies {
|
||||
general: PartialRecord<keyof GeneralSettings, SettingPaidLevels>
|
||||
ui: PartialRecord<keyof UISettings, SettingPaidLevels>
|
||||
integration: PartialRecord<keyof IntegrationSettings, SettingPaidLevels>
|
||||
ai: PartialRecord<keyof AISettings, SettingPaidLevels>
|
||||
}
|
||||
|
||||
export type SettingNamespace = keyof typeof PAID_SETTINGS
|
||||
|
||||
export const getSettingPaidLevel = (namespace: string, key: string) => {
|
||||
const group = PAID_SETTINGS[namespace as keyof typeof PAID_SETTINGS]
|
||||
if (!group) return
|
||||
return group[key as keyof typeof group]
|
||||
}
|
||||
|
||||
const ACCENT_COLOR_MAP = {
|
||||
orange: {
|
||||
|
|
@ -40,5 +93,6 @@ export const getAccentColorValue = (color: AccentColor) => {
|
|||
if (color.startsWith("#")) {
|
||||
return { light: color, dark: color }
|
||||
}
|
||||
return ACCENT_COLOR_MAP[color] || ACCENT_COLOR_MAP.orange
|
||||
const preset = ACCENT_COLOR_MAP[color as keyof typeof ACCENT_COLOR_MAP]
|
||||
return preset || ACCENT_COLOR_MAP.orange
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { ActionConditionIndex } from "@follow-app/client-sdk"
|
||||
import { useMutation, useQuery } from "@tanstack/react-query"
|
||||
import { FetchError } from "ofetch"
|
||||
import { useCallback } from "react"
|
||||
|
||||
import type { GeneralMutationOptions } from "../../types"
|
||||
|
|
@ -16,19 +15,8 @@ export const usePrefetchActions = () => {
|
|||
|
||||
export const useUpdateActionsMutation = (options?: GeneralMutationOptions) => {
|
||||
return useMutation({
|
||||
...options,
|
||||
mutationFn: () => actionSyncService.saveRules(),
|
||||
onSuccess() {
|
||||
options?.onSuccess?.()
|
||||
},
|
||||
onError(err) {
|
||||
if (err instanceof FetchError && err.response?._data) {
|
||||
const { message } = err.response._data
|
||||
options?.onError?.(message)
|
||||
return
|
||||
}
|
||||
|
||||
options?.onError?.("Error saving actions")
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { ModuleAPIs } from "@follow-app/client-sdk"
|
|||
|
||||
export type GeneralMutationOptions = {
|
||||
onSuccess?: () => void
|
||||
onError?: (errorMessage: string) => void
|
||||
onError?: (errorMessage: Error) => void
|
||||
}
|
||||
|
||||
export type GeneralQueryOptions = {
|
||||
|
|
|
|||
430
pnpm-lock.yaml
430
pnpm-lock.yaml
|
|
@ -7,8 +7,8 @@ settings:
|
|||
catalogs:
|
||||
default:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 0.3.76
|
||||
version: 0.3.76
|
||||
specifier: 0.3.77
|
||||
version: 0.3.77
|
||||
tailwindcss-uikit-colors:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
|
|
@ -339,7 +339,7 @@ importers:
|
|||
version: 4.3.0
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow-app/readability':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../packages/readability
|
||||
|
|
@ -457,7 +457,7 @@ importers:
|
|||
version: 3.0.2(electron@38.3.0)
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/database':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../packages/internal/database
|
||||
|
|
@ -807,6 +807,9 @@ importers:
|
|||
'@expo/react-native-action-sheet':
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(react@19.0.0)
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/components':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/internal/components
|
||||
|
|
@ -1187,7 +1190,7 @@ importers:
|
|||
version: 6.2.1
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/tracker':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/internal/tracker
|
||||
|
|
@ -1427,9 +1430,18 @@ importers:
|
|||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
'@follow/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../constants
|
||||
'@follow/hooks':
|
||||
specifier: workspace:*
|
||||
version: link:../hooks
|
||||
'@follow/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
'@follow/store':
|
||||
specifier: workspace:*
|
||||
version: link:../store
|
||||
'@follow/types':
|
||||
specifier: workspace:*
|
||||
version: link:../types
|
||||
|
|
@ -1673,7 +1685,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
|
|
@ -1685,7 +1697,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../constants
|
||||
|
|
@ -1757,7 +1769,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../constants
|
||||
|
|
@ -1788,7 +1800,7 @@ importers:
|
|||
version: 2.0.0(@types/node@24.8.1)
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@folo-services/drizzle':
|
||||
specifier: 0.1.34
|
||||
version: 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
|
|
@ -1818,7 +1830,7 @@ importers:
|
|||
dependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
|
|
@ -1867,7 +1879,7 @@ importers:
|
|||
devDependencies:
|
||||
'@follow-app/client-sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
version: 0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@follow/configs':
|
||||
specifier: workspace:*
|
||||
version: link:../../configs
|
||||
|
|
@ -4343,23 +4355,29 @@ packages:
|
|||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@follow-app/client-sdk@0.3.76':
|
||||
resolution: {integrity: sha512-cVZgG8BTXoqEomE+WzxrT47O1SYJEY1YDLI+InQuku/L2mQcwZCxxYJbPUicNIcH4b3smj7c+E4qr7QJGwgchg==}
|
||||
'@follow-app/client-sdk@0.3.77':
|
||||
resolution: {integrity: sha512-zr1FneNho5/wdDn2UWcjixlUf29kxbUqKXGnlvjJ6TOUJrygF8bh8GshgoHEkNj+4JUwMKo+WUtimyx6QpRrag==}
|
||||
|
||||
'@folo-services/ai-tools@0.2.49':
|
||||
resolution: {integrity: sha512-QsKTW8N18812Thkdcc7wDTff3QkQxxTYHxHi1RM321jk4DbCq+mHlAr10Krwj0mRtKuULtqFHPm13udmfIv1Bw==}
|
||||
|
||||
'@folo-services/constants@0.1.40':
|
||||
resolution: {integrity: sha512-sAYr/I+dlBrgQKTy085Xjler64wawOneeqvU5SYOugwf36RpWvVtz1aTo0KdiF1+M7gM1W+G1vkfnXAzOeBEaA==}
|
||||
'@folo-services/constants@0.1.41':
|
||||
resolution: {integrity: sha512-fXJ5DVWMgc4czW8i9JmVx3WckKhUksiwXkzzOClH0GePEOeS+DQ08o21UfZl1pBvhvhZm0pS+klHLljeBaRb1g==}
|
||||
|
||||
'@folo-services/drizzle@0.1.34':
|
||||
resolution: {integrity: sha512-p6dsveNwXwNxmy+8+FHHKiFjVFXwc7yx7c4902k13hd1vcBihxiWUD4MdCwAcQTAPSJ3dYx1UZojHSdyiXwqnw==}
|
||||
|
||||
'@folo-services/drizzle@0.1.35':
|
||||
resolution: {integrity: sha512-b+5OPwjKW3gnf0lkLj7PuhjLCldD9TItXNWRwIvJrPYmIGlrQmUiKgQIuLR8dsksF9L7w8IcIi4Vt40Qppx7nQ==}
|
||||
|
||||
'@folo-services/exceptions@0.1.19':
|
||||
resolution: {integrity: sha512-rXgLssE3r3w2WzC3knCX9nYe0cszHXpYwAllrYOsPpzjQSHWRwJ6LuJyt0xlvdeX/9i8KQ/UYgf4to6Abt2whg==}
|
||||
|
||||
'@folo-services/shared@0.0.33':
|
||||
resolution: {integrity: sha512-fN4tp7Euum0XyTfrNLPdkxqUwTxHOOYq15vSJm/xqVGZxMQx9DTwtZasydEGVzns9zqnb7HlrkjjvG7kxlJdXA==}
|
||||
'@folo-services/exceptions@0.1.20':
|
||||
resolution: {integrity: sha512-iuV6F22XI6kvSSVQRTCoS/wfJnPGvI3XQbA3YQGgXa8fT28SE+8LBGCyAj0rqbfnXlXdpxZE/IooXdpJyxkp0Q==}
|
||||
|
||||
'@folo-services/shared@0.0.34':
|
||||
resolution: {integrity: sha512-T0gZRBlDVP9WLG0Dg8tLgWdk+TVjbdKfSvRHEyBY+YioQ1LO4vulN8ddCv1oBvMQxl0iESY9mnmB4ua9iGGkyQ==}
|
||||
|
||||
'@fontsource/sn-pro@5.2.5':
|
||||
resolution: {integrity: sha512-rBdBv/0ygj6bkO7xDMMFpwobLdSrcQ2Jncb6DIwdeYGoAgeWkQRwVYhGDKasfLjEYCNYxrqr6wsXsM9+aU39RA==}
|
||||
|
|
@ -21312,12 +21330,12 @@ snapshots:
|
|||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@follow-app/client-sdk@0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@follow-app/client-sdk@0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.40
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.19
|
||||
'@folo-services/shared': 0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@folo-services/shared': 0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
better-auth: 1.3.28(better-sqlite3@12.4.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -21361,12 +21379,12 @@ snapshots:
|
|||
- svelte
|
||||
- vue
|
||||
|
||||
'@follow-app/client-sdk@0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@follow-app/client-sdk@0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.40
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.19
|
||||
'@folo-services/shared': 0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@folo-services/shared': 0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
better-auth: 1.3.28(better-sqlite3@12.4.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -21410,12 +21428,61 @@ snapshots:
|
|||
- svelte
|
||||
- vue
|
||||
|
||||
'@follow-app/client-sdk@0.3.76(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@follow-app/client-sdk@0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.40
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.19
|
||||
'@folo-services/shared': 0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@folo-services/shared': 0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
better-auth: 1.3.28(better-sqlite3@12.4.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@lynx-js/react'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@sveltejs/kit'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- next
|
||||
- pg
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- react
|
||||
- react-dom
|
||||
- solid-js
|
||||
- sql.js
|
||||
- sqlite3
|
||||
- svelte
|
||||
- vue
|
||||
|
||||
'@follow-app/client-sdk@0.3.77(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@folo-services/shared': 0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)
|
||||
better-auth: 1.3.28(better-sqlite3@12.4.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -21499,7 +21566,7 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/constants@0.1.40':
|
||||
'@folo-services/constants@0.1.41':
|
||||
dependencies:
|
||||
zod: 4.1.12
|
||||
|
||||
|
|
@ -21544,47 +21611,6 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.19
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
nanoid: 5.1.6
|
||||
pg: 8.16.3
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.19
|
||||
|
|
@ -21626,11 +21652,178 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
nanoid: 5.1.6
|
||||
pg: 8.16.3
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
nanoid: 5.1.6
|
||||
pg: 8.16.3
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
nanoid: 5.1.6
|
||||
pg: 8.16.3
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/drizzle@0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)':
|
||||
dependencies:
|
||||
'@folo-services/exceptions': 0.1.20
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
nanoid: 5.1.6
|
||||
pg: 8.16.3
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/exceptions@0.1.19': {}
|
||||
|
||||
'@folo-services/shared@0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
'@folo-services/exceptions@0.1.20': {}
|
||||
|
||||
'@folo-services/shared@0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
|
|
@ -21668,9 +21861,51 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/shared@0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
'@folo-services/shared@0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-rds-data'
|
||||
- '@cloudflare/workers-types'
|
||||
- '@electric-sql/pglite'
|
||||
- '@libsql/client'
|
||||
- '@libsql/client-wasm'
|
||||
- '@neondatabase/serverless'
|
||||
- '@op-engineering/op-sqlite'
|
||||
- '@opentelemetry/api'
|
||||
- '@planetscale/database'
|
||||
- '@prisma/client'
|
||||
- '@tidbcloud/serverless'
|
||||
- '@types/better-sqlite3'
|
||||
- '@types/pg'
|
||||
- '@types/sql.js'
|
||||
- '@upstash/redis'
|
||||
- '@vercel/postgres'
|
||||
- '@xata.io/client'
|
||||
- better-sqlite3
|
||||
- bun-types
|
||||
- expo-sqlite
|
||||
- gel
|
||||
- hono
|
||||
- knex
|
||||
- kysely
|
||||
- mysql2
|
||||
- pg
|
||||
- pg-native
|
||||
- postgres
|
||||
- prisma
|
||||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/shared@0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
|
|
@ -21708,9 +21943,10 @@ snapshots:
|
|||
- sql.js
|
||||
- sqlite3
|
||||
|
||||
'@folo-services/shared@0.0.33(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
'@folo-services/shared@0.0.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)':
|
||||
dependencies:
|
||||
'@folo-services/drizzle': 0.1.34(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@folo-services/constants': 0.1.41
|
||||
'@folo-services/drizzle': 0.1.35(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)
|
||||
'@hono/zod-openapi': 1.1.4(hono@4.9.8)(zod@4.1.12)
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
drizzle-zod: 0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12)
|
||||
|
|
@ -24103,7 +24339,7 @@ snapshots:
|
|||
debug: 2.6.9
|
||||
invariant: 2.2.4
|
||||
metro: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
metro-config: 0.82.4(bufferutil@4.0.9)
|
||||
metro-config: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
metro-core: 0.82.4
|
||||
semver: 7.7.3
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -27953,6 +28189,15 @@ snapshots:
|
|||
kysely: 0.28.8
|
||||
pg: 8.16.3
|
||||
|
||||
drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3):
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/pg': 8.15.5
|
||||
better-sqlite3: 12.4.1
|
||||
expo-sqlite: 15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0)
|
||||
kysely: 0.28.8
|
||||
pg: 8.16.3
|
||||
|
||||
drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3):
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
|
|
@ -27994,6 +28239,11 @@ snapshots:
|
|||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.0)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
zod: 4.1.12
|
||||
|
||||
drizzle-zod@0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12):
|
||||
dependencies:
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.4)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.4)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
zod: 4.1.12
|
||||
|
||||
drizzle-zod@0.8.3(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3))(zod@4.1.12):
|
||||
dependencies:
|
||||
drizzle-orm: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0)(utf-8-validate@6.0.5))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)(utf-8-validate@6.0.5))(react@19.0.0))(kysely@0.28.8)(pg@8.16.3)
|
||||
|
|
@ -32222,6 +32472,22 @@ snapshots:
|
|||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
metro-config@0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5):
|
||||
dependencies:
|
||||
connect: 3.7.0
|
||||
cosmiconfig: 5.2.1
|
||||
flow-enums-runtime: 0.0.6
|
||||
jest-validate: 29.7.0
|
||||
metro: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
metro-cache: 0.82.4
|
||||
metro-core: 0.82.4
|
||||
metro-runtime: 0.82.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
optional: true
|
||||
|
||||
metro-core@0.82.4:
|
||||
dependencies:
|
||||
flow-enums-runtime: 0.0.6
|
||||
|
|
@ -32407,7 +32673,7 @@ snapshots:
|
|||
metro-babel-transformer: 0.82.4
|
||||
metro-cache: 0.82.4
|
||||
metro-cache-key: 0.82.4
|
||||
metro-config: 0.82.4(bufferutil@4.0.9)
|
||||
metro-config: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5)
|
||||
metro-core: 0.82.4
|
||||
metro-file-map: 0.82.4
|
||||
metro-resolver: 0.82.4
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ packages:
|
|||
- "!**/example/**"
|
||||
|
||||
catalog:
|
||||
"@follow-app/client-sdk": 0.3.76
|
||||
"@follow-app/client-sdk": 0.3.77
|
||||
tailwindcss-uikit-colors: 1.0.0
|
||||
typescript: 5.9.3
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue