mirror of
https://github.com/supabase/supabase.git
synced 2026-06-10 04:26:19 +08:00
## Context There'll be an upcoming shared pooler maintenance for specific regions - so the PR here informs users about that via the following changes - Shift Terms of Service update into a float banner - Same local storage key is used for dismissal so users who already dismissed it won't see this again - This is to prevent 2 notice banners scenario <img width="342" height="235" alt="image" src="https://github.com/user-attachments/assets/0a10fa53-46a0-4c71-beef-d66e006503fd" /> - Updated NoticeBanner to inform users of the upcoming maintenance - Only projects in specific regions will be affected, so not all projects will see this <img width="658" height="75" alt="image" src="https://github.com/user-attachments/assets/83aabda5-a774-4118-a945-08052b7c6b3e" /> <img width="496" height="152" alt="image" src="https://github.com/user-attachments/assets/a1ccc440-5179-4a4b-919f-208844bb2227" /> - Cleaned up unused local storage keys ### To test - Can be tested on preview as the notice applies for eu-central-1 and us-east-1 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Dismissible Terms of Service update banner with “Learn more” dialog so users can review and acknowledge changes. * Enhanced maintenance notices showing region-specific maintenance windows, a status link per region, and formatted UTC timestamps tied to the selected project. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46191?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { createContext, useCallback, useContext, useState } from 'react'
|
|
|
|
export const BANNER_ID = {
|
|
METRICS_API: 'metrics-api-banner',
|
|
INDEX_ADVISOR: 'index-advisor-banner',
|
|
TABLE_EDITOR_QUEUE_OPERATIONS: 'table-editor-queue-operations-banner',
|
|
RLS_EVENT_TRIGGER: 'rls-event-trigger-banner',
|
|
RLS_TESTER: 'rls-tester-banner',
|
|
FREE_MICRO_UPGRADE: 'free-micro-upgrade-banner',
|
|
TOS_UPDATE: 'tos-update-banner',
|
|
} as const
|
|
|
|
export type BannerId = (typeof BANNER_ID)[keyof typeof BANNER_ID]
|
|
|
|
export interface Banner {
|
|
id: BannerId
|
|
content: React.ReactNode
|
|
isDismissed: boolean
|
|
priority?: number
|
|
onDismiss?: () => void
|
|
}
|
|
|
|
interface BannerStackContextType {
|
|
banners: Banner[]
|
|
addBanner: (banner: Banner) => void
|
|
dismissBanner: (id: BannerId) => void
|
|
}
|
|
|
|
const BannerStackContext = createContext<BannerStackContextType | undefined>(undefined)
|
|
|
|
export const BannerStackProvider = ({ children }: { children: React.ReactNode }) => {
|
|
const [banners, setBanners] = useState<Banner[]>([])
|
|
|
|
const addBanner = useCallback((banner: Banner) => {
|
|
setBanners((prev) => {
|
|
const exists = prev.some((b) => b.id === banner.id)
|
|
if (exists) return prev
|
|
const newBanners = [...prev, banner]
|
|
return newBanners.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
|
|
})
|
|
}, [])
|
|
|
|
const dismissBanner = useCallback((id: string) => {
|
|
setBanners((prev) => prev.map((b) => (b.id === id ? { ...b, isDismissed: true } : b)))
|
|
setTimeout(() => {
|
|
setBanners((prev) => prev.filter((b) => b.id !== id))
|
|
}, 300)
|
|
}, [])
|
|
|
|
return (
|
|
<BannerStackContext.Provider value={{ banners, addBanner, dismissBanner }}>
|
|
{children}
|
|
</BannerStackContext.Provider>
|
|
)
|
|
}
|
|
|
|
export const useBannerStack = () => {
|
|
const context = useContext(BannerStackContext)
|
|
if (!context) throw new Error('useBannerStack must be used within BannerStackProvider')
|
|
return context
|
|
}
|