Merge branch 'master' into jsm/migrate-studio-turbo

This commit is contained in:
Jonathan Summers-Muir committed 2022-09-22 14:16:10 +02:00
commit 167e93fd78
12 files changed
+277 -125

No files matched your search

+135 -20
View File
@@ -9,13 +9,17 @@
[Supabase](https://supabase.com)は、オープンソースの Firebase 代替製品です。エンタープライズグレードのオープンソースツールを使って、Firebase の機能を構築しています。
- [x] ホスティングされた Postgres データベース
- [x] リアルタイムサブスクリプション
- [x] 認証・認可
- [x] ホスティングされた Postgres データベース [Docs](https://supabase.com/docs/guides/database)
- [x] 認証・認可 [Docs](https://supabase.com/docs/guides/auth)
- [x] API を自動生成
- [x] ダッシュボード
- [x] ストレージ
- [x] REST [Docs](https://supabase.com/docs/guides/api#rest-api)
- [x] リアルタイムサブスクリプション [Docs](https://supabase.com/docs/guides/api#realtime-api)
- [x] GraphQL (Beta) [Docs](https://supabase.com/docs/guides/api#graphql-api)
- [x] 関数
- [x] データベース関数 [Docs](https://supabase.com/docs/guides/database/functions)
- [x] Edge Functions [Docs](https://supabase.com/docs/guides/functions)
- [x] ストレージ
- [x] ダッシュボード
![Supabase Dashboard](https://raw.githubusercontent.com/supabase/supabase/master/apps/www/public/images/github/supabase-dashboard.png)
@@ -23,11 +27,14 @@
詳しいドキュメントは[supabase.com/docs](https://supabase.com/docs)をご覧ください。
コントリビュート方法は[Getting Started](./DEVELOPERS.md)をご覧ください。
## コミュニティとサポート
- [コミュニティフォーラム](https://github.com/supabase/supabase/discussions) どんな時に使うか:構築の手助け、データベースのベストプラクティスに関する議論など
- [GitHub Issue](https://github.com/supabase/supabase/issues) どんな時に使うか: Supabase で起こったバグやエラーについて
- [Email サポート](https://supabase.com/docs/support#business-support) どんな時に使うか: ユーザー自身のデータベースやインフラに何か問題が発生した場合
- [Discord](https://discord.supabase.com) どんな時に使うか: アプリケーションの共有やコミュニティとの交流
## ステータス
@@ -36,9 +43,9 @@
- [x] Public Beta: 企業以外のほとんどのユースケースに耐えうる十分な安定性を確保。
- [ ] Public: 実用的な用途に対応
現在、Public Beta を実施しています。このリポジトリの"release"にてメジャーアップデートに関する情報を発信しています。
現在、Public Beta を実施しています。このリポジトリの"releases"にてメジャーアップデートに関する情報を発信しています。
<kbd><img src="https://gitcdn.link/repo/supabase/supabase/master/web/static/watch-repo.gif" alt="Watch this repo"/></kbd>
<kbd><img src="https://raw.githubusercontent.com/supabase/supabase/d5f7f413ab356dc1a92075cb3cee4e40a957d5b1/web/static/watch-repo.gif" alt="Watch this repo"/></kbd>
---
@@ -49,9 +56,9 @@ Supabase は、オープンソースのツールを組み合わせてできて
**現在のアーキテクチャ**
Supabase は[ホスティングされたプラットフォーム](https://app.supabase.com)です。登録するだけで、何もインストールせずに使い始めることができます。
更に現在ローカルでの開発環境を整えており、これはプラットフォームの安定性と並んで今最優先で進めているプロジェクトになります。
さらに、 [セルフホスティング](https://supabase.com/docs/guides/hosting/overview) や [ローカル開発](https://supabase.com/docs/guides/local-development)も可能です。
![アーキテクチャー](https://supabase.com/docs/assets/images/supabase-architecture-9050a7317e9ec7efb7807f5194122e48.png)
![アーキテクチャー](https://user-images.githubusercontent.com/70828596/187547862-ffa9d058-0c3a-4851-a3e7-92ccfca4b596.png)
- [PostgreSQL](https://www.postgresql.org/)は、30 年以上にわたって開発・改善されてきたオブジェクトリレーショナルデータベースシステムで、信頼性、機能の堅牢性、パフォーマンスの面で高い評価を得ています。
- [Realtime](https://github.com/supabase/realtime)は、PostgreSQL の insert、update、delete の情報を WebSocket で受信できる Elixir サーバです。Supabase は Postgres に組み込まれたレプリケーション機能をリッスンし、レプリケーションのバイトストリームを JSON に変換し、その JSON を WebSocket でブロードキャストします。
@@ -65,17 +72,125 @@ Supabase は[ホスティングされたプラットフォーム](https://app.su
Supabase クライアントライブラリはモジュール化されています。それぞれのサブライブラリが、一つの外部システムのための独立した実装となっています。こうすることで、既存のツールをサポートしています。
- **`supabase-{lang}`**: 下記全てのライブラリを内包したクライアントライブラリ
- `postgrest-{lang}`: [PostgREST](https://github.com/postgrest/postgrest)用のクライアントライブラリ
- `realtime-{lang}`: [Realtime](https://github.com/supabase/realtime)用のクライアントライブラリ
- `gotrue-{lang}`: [GoTrue](https://github.com/netlify/gotrue)用のクライアントライブラリ
| レポジトリ | 公式 | コミュニティ |
| --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`supabase-{lang}`** | [`JS`](https://github.com/supabase/supabase-js) | [`C#`](https://github.com/supabase/supabase-csharp) \| [`Dart`](https://github.com/supabase/supabase-dart) \| [`Python`](https://github.com/supabase/supabase-py) \| `Rust` \| [`Ruby`](https://github.com/supabase/supabase-rb) \| `Go` |
| `postgrest-{lang}` | [`JS`](https://github.com/supabase/postgrest-js) | [`C#`](https://github.com/supabase/postgrest-csharp) \| [`Dart`](https://github.com/supabase/postgrest-dart) \| [`Python`](https://github.com/supabase/postgrest-py) \| [`Rust`](https://github.com/supabase/postgrest-rs) \| [`Ruby`](https://github.com/supabase/postgrest-rb) \| [`Go`](https://github.com/supabase/postgrest-go) |
| `realtime-{lang}` | [`JS`](https://github.com/supabase/realtime-js) | [`C#`](https://github.com/supabase/realtime-csharp) \| [`Dart`](https://github.com/supabase/realtime-dart) \| [`Python`](https://github.com/supabase/realtime-py) \| `Rust` \| `Ruby` \| `Go` |
| `gotrue-{lang}` | [`JS`](https://github.com/supabase/gotrue-js) | [`C#`](https://github.com/supabase/gotrue-csharp) \| [`Dart`](https://github.com/supabase/gotrue-dart) \| [`Python`](https://github.com/supabase/gotrue-py) \| `Rust` \| `Ruby` \| `Go` |
<table style="table-layout:fixed; white-space: nowrap;">
<tr>
<th>言語</th>
<th>クライアント</th>
<th colspan="5">機能別クライアント (Supabaseクライアントに同梱)</th>
</tr>
<tr>
<th></th>
<th>Supabase</th>
<th><a href="https://github.com/postgrest/postgrest" target="_blank" rel="noopener noreferrer">PostgREST</a></th>
<th><a href="https://github.com/supabase/gotrue" target="_blank" rel="noopener noreferrer">GoTrue</a></th>
<th><a href="https://github.com/supabase/realtime" target="_blank" rel="noopener noreferrer">Realtime</a></th>
<th><a href="https://github.com/supabase/storage-api" target="_blank" rel="noopener noreferrer">Storage</a></th>
<th>Functions</th>
</tr>
<!-- TEMPLATE FOR NEW ROW -->
<!-- START ROW
<tr>
<td>lang</td>
<td><a href="https://github.com/supabase-community/supabase-lang" target="_blank" rel="noopener noreferrer">supabase-lang</a></td>
<td><a href="https://github.com/supabase-community/postgrest-lang" target="_blank" rel="noopener noreferrer">postgrest-lang</a></td>
<td><a href="https://github.com/supabase-community/gotrue-lang" target="_blank" rel="noopener noreferrer">gotrue-lang</a></td>
<td><a href="https://github.com/supabase-community/realtime-lang" target="_blank" rel="noopener noreferrer">realtime-lang</a></td>
<td><a href="https://github.com/supabase-community/storage-lang" target="_blank" rel="noopener noreferrer">storage-lang</a></td>
</tr>
END ROW -->
<th colspan="7">⚡️ 公式 ⚡️</th>
<tr>
<td>JavaScript (TypeScript)</td>
<td><a href="https://github.com/supabase/supabase-js" target="_blank" rel="noopener noreferrer">supabase-js</a></td>
<td><a href="https://github.com/supabase/postgrest-js" target="_blank" rel="noopener noreferrer">postgrest-js</a></td>
<td><a href="https://github.com/supabase/gotrue-js" target="_blank" rel="noopener noreferrer">gotrue-js</a></td>
<td><a href="https://github.com/supabase/realtime-js" target="_blank" rel="noopener noreferrer">realtime-js</a></td>
<td><a href="https://github.com/supabase/storage-js" target="_blank" rel="noopener noreferrer">storage-js</a></td>
<td><a href="https://github.com/supabase/functions-js" target="_blank" rel="noopener noreferrer">functions-js</a></td>
</tr>
<th colspan="7">💚 コミュニティ 💚</th>
<tr>
<td>C#</td>
<td><a href="https://github.com/supabase-community/supabase-csharp" target="_blank" rel="noopener noreferrer">supabase-csharp</a></td>
<td><a href="https://github.com/supabase-community/postgrest-csharp" target="_blank" rel="noopener noreferrer">postgrest-csharp</a></td>
<td><a href="https://github.com/supabase-community/gotrue-csharp" target="_blank" rel="noopener noreferrer">gotrue-csharp</a></td>
<td><a href="https://github.com/supabase-community/realtime-csharp" target="_blank" rel="noopener noreferrer">realtime-csharp</a></td>
<td><a href="https://github.com/supabase-community/storage-csharp" target="_blank" rel="noopener noreferrer">storage-csharp</a></td>
<td><a href="https://github.com/supabase-community/functions-csharp" target="_blank" rel="noopener noreferrer">functions-csharp</a></td>
</tr>
<tr>
<td>Dart (Flutter)</td>
<td><a href="https://github.com/supabase/supabase-dart" target="_blank" rel="noopener noreferrer">supabase-dart</a></td>
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-dart</a></td>
<td><a href="https://github.com/supabase/realtime-dart" target="_blank" rel="noopener noreferrer">realtime-dart</a></td>
<td><a href="https://github.com/supabase/storage-dart" target="_blank" rel="noopener noreferrer">storage-dart</a></td>
<td><a href="https://github.com/supabase-community/functions-dart" target="_blank" rel="noopener noreferrer">functions-dart</a></td>
</tr>
<tr>
<td>Go</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-go" target="_blank" rel="noopener noreferrer">postgrest-go</a></td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-go" target="_blank" rel="noopener noreferrer">storage-go</a></td>
<td>-</td>
</tr>
<tr>
<td>Java</td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/gotrue-java" target="_blank" rel="noopener noreferrer">gotrue-java</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Kotlin</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-kt" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
<td><a href="https://github.com/supabase-community/gotrue-kt" target="_blank" rel="noopener noreferrer">gotrue-kt</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Python</td>
<td><a href="https://github.com/supabase-community/supabase-py" target="_blank" rel="noopener noreferrer">supabase-py</a></td>
<td><a href="https://github.com/supabase-community/postgrest-py" target="_blank" rel="noopener noreferrer">postgrest-py</a></td>
<td><a href="https://github.com/supabase-community/gotrue-py" target="_blank" rel="noopener noreferrer">gotrue-py</a></td>
<td><a href="https://github.com/supabase-community/realtime-py" target="_blank" rel="noopener noreferrer">realtime-py</a></td>
<td><a href="https://github.com/supabase-community/storage-py" target="_blank" rel="noopener noreferrer">storage-py</a></td>
<td><a href="https://github.com/supabase-community/functions-py" target="_blank" rel="noopener noreferrer">functions-py</a></td>
</tr>
<tr>
<td>Ruby</td>
<td><a href="https://github.com/supabase-community/supabase-rb" target="_blank" rel="noopener noreferrer">supabase-rb</a></td>
<td><a href="https://github.com/supabase-community/postgrest-rb" target="_blank" rel="noopener noreferrer">postgrest-rb</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Rust</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-rs" target="_blank" rel="noopener noreferrer">postgrest-rs</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Swift</td>
<td><a href="https://github.com/supabase-community/supabase-swift" target="_blank" rel="noopener noreferrer">supabase-swift</a></td>
<td><a href="https://github.com/supabase-community/postgrest-swift" target="_blank" rel="noopener noreferrer">postgrest-swift</a></td>
<td><a href="https://github.com/supabase-community/gotrue-swift" target="_blank" rel="noopener noreferrer">gotrue-swift</a></td>
<td><a href="https://github.com/supabase-community/realtime-swift" target="_blank" rel="noopener noreferrer">realtime-swift</a></td>
<td><a href="https://github.com/supabase-community/storage-swift" target="_blank" rel="noopener noreferrer">storage-swift</a></td>
<td>-</td>
</tr>
</table>
<!--- Remove this list if you're traslating to another language, it's hard to keep updated across multiple files-->
<!--- Keep only the link to the list of translation files-->
@@ -1,16 +1,19 @@
import { ResponseCodeFormatter, TimestampLocalFormatter } from '../LogsFormatters'
import {
ResponseCodeFormatter,
RowLayout,
TextFormatter,
TimestampLocalFormatter,
} from '../LogsFormatters'
export default [
{
formatter: (data: any) => (
<div className="flex h-full w-full items-center justify-between gap-3">
<RowLayout>
<TimestampLocalFormatter value={data.row.timestamp!} />
<div className="flex h-full w-full items-center gap-4">
<ResponseCodeFormatter value={data.row.status_code} />
<span className="text-xs w-14">{data.row.method}</span>
<span className="font-mono text-xs">{data.row.path}</span>
</div>
</div>
<ResponseCodeFormatter row={data} value={data.row.status_code} />
<TextFormatter className="w-20" value={data.row.method} />
<TextFormatter className="w-full" value={data.row.path} />
</RowLayout>
),
},
]
@@ -1,19 +1,18 @@
import { SeverityFormatter, TimestampLocalFormatter } from '../LogsFormatters'
import {
RowLayout,
SeverityFormatter,
TextFormatter,
TimestampLocalFormatter,
} from '../LogsFormatters'
export default [
{
formatter: (data: any) => (
<div className="flex w-full items-center gap-2 h-full">
<div className="flex items-center gap-2 h-full">
<TimestampLocalFormatter className="w-24" value={data.row.timestamp!} />
<div className="w-16 flex items-center">
<SeverityFormatter value={data.row.error_severity} />
</div>
</div>
<div className="flex truncate">
<span className="font-mono text-xs truncate">{data.row.event_message}</span>
</div>
</div>
<RowLayout>
<TimestampLocalFormatter value={data.row.timestamp!} />
<SeverityFormatter value={data.row.error_severity} />
<TextFormatter className="w-full" value={data.row.event_message} />
</RowLayout>
),
},
]
@@ -1,16 +1,13 @@
import { PreviewLogData } from '..'
import { TimestampLocalFormatter } from '../LogsFormatters'
import { RowLayout, TextFormatter, TimestampLocalFormatter } from '../LogsFormatters'
const DefaultPreviewColumnRenderer = [
export default [
{
formatter: (data: { row: PreviewLogData }) => {
return (
<div className="flex w-full justify-start items-center gap-4 h-full">
<TimestampLocalFormatter value={data.row.timestamp!} />
<span className="font-mono text-xs truncate">{data.row.event_message}</span>
</div>
)
},
formatter: (data: { row: PreviewLogData }) => (
<RowLayout>
<TimestampLocalFormatter value={data.row.timestamp!} />
<TextFormatter className="w-full" value={data.row.event_message} />
</RowLayout>
),
},
]
export default DefaultPreviewColumnRenderer
@@ -1,36 +1,19 @@
import { HeaderFormmater, ResponseCodeFormatter, TimestampLocalFormatter } from '../LogsFormatters'
import {
ResponseCodeFormatter,
RowLayout,
TextFormatter,
TimestampLocalFormatter,
} from '../LogsFormatters'
export default [
{
key: 'timestamp',
headerRenderer: () => (
<div className="flex w-full justify-end h-full">
<HeaderFormmater value={'timestamp'} />
</div>
formatter: (data: any) => (
<RowLayout>
<TimestampLocalFormatter value={data.row.timestamp!} />
<ResponseCodeFormatter row={data} value={data.row.status_code} />
<TextFormatter value={data.row.method} />
<TextFormatter value={data.row.id} />
</RowLayout>
),
name: 'timestamp',
formatter: (data: any) => <TimestampLocalFormatter value={data.row.timestamp!} />,
width: 128,
},
{
key: 'status_code',
headerRenderer: () => <HeaderFormmater value={'Status'} />,
name: 'status_code',
formatter: (data: any) => <ResponseCodeFormatter row={data} value={data.row.status_code} />,
width: 0,
resizable: true,
},
{
key: 'method',
headerRenderer: () => <HeaderFormmater value={'method'} />,
width: 0,
resizable: true,
},
{
key: 'id',
headerRenderer: () => <HeaderFormmater value={'id'} />,
name: 'id',
resizable: true,
},
]
@@ -1,36 +1,22 @@
import { HeaderFormmater, SeverityFormatter, TimestampLocalFormatter } from '../LogsFormatters'
import {
RowLayout,
SeverityFormatter,
TextFormatter,
TimestampLocalFormatter,
} from '../LogsFormatters'
export default [
{
key: 'timestamp',
headerRenderer: () => (
<div className="flex w-full justify-end h-full">
<HeaderFormmater value={'timestamp'} />
</div>
),
name: 'timestamp',
formatter: (data: any) => (
<TimestampLocalFormatter className="!text-scale-1100" value={data.row.timestamp!} />
<RowLayout>
<TimestampLocalFormatter value={data.row.timestamp!} />
{data.row.event_type === 'uncaughtException' ? (
<SeverityFormatter value={data.row.event_type} uppercase={false} />
) : (
<SeverityFormatter value={data.row.level} />
)}
<TextFormatter className="w-full" value={data.row.event_message} />
</RowLayout>
),
width: 128,
resizable: true,
},
{
key: 'level',
headerRenderer: () => <HeaderFormmater value={'Level'} />,
name: 'level',
formatter: (data: any) => {
if (data.row.event_type === 'uncaughtException') {
return <SeverityFormatter value={data.row.event_type} uppercase={false} />
}
return <SeverityFormatter value={data.row.level} />
},
width: 24,
resizable: true,
},
{
key: 'event_message',
headerRenderer: () => <HeaderFormmater value={'Event message'} />,
resizable: true,
},
]
@@ -6,8 +6,18 @@
import { IconAlertCircle, IconInfo } from '@supabase/ui'
import dayjs from 'dayjs'
import React from 'react'
import { isUnixMicro, unixMicroToIsoTimestamp } from '.'
export const RowLayout: React.FC = ({ children }) => (
<div className="flex h-full w-full items-center gap-4">{children}</div>
)
export const TextFormatter: React.FC<{ className?: string; value: string }> = ({
value,
className,
}) => <span className={'font-mono text-xs truncate ' + className}>{value}</span>
export const ResponseCodeFormatter = ({ value }: any) => {
if (!value) {
return (
@@ -109,6 +119,9 @@ export const SeverityFormatter = ({
const uppercasedValue = value.toUpperCase()
const text = uppercase ? uppercasedValue : value
const Layout: React.FC<{ className?: string }> = ({ className, children }) => (
<div className={`w-24 flex items-center h-full ${className}`}>{children}</div>
)
switch (uppercasedValue) {
case 'UNCAUGHTEXCEPTION':
@@ -116,12 +129,12 @@ export const SeverityFormatter = ({
case 'FATAL':
case 'ERROR':
return (
<div className="flex items-center h-full gap-1">
<Layout className="gap-1">
<div className=" p-0.5 rounded !text-red-900">
<IconAlertCircle size={14} strokeWidth={2} />
</div>
<span className="!text-red-900 !block titlecase">{text}</span>
</div>
</Layout>
)
break
@@ -129,45 +142,45 @@ export const SeverityFormatter = ({
case 'DEBUG':
return (
<div className="flex items-center h-full gap-1">
<Layout className="gap-1">
<div className=" p-0.5 rounded !text-blue-900">
<IconAlertCircle size={14} strokeWidth={2} />
</div>
<span className="!text-blue-900 !block titlecase">{text}</span>
</div>
</Layout>
)
break
case 'LOG':
return (
<div className="flex items-center h-full gap-1">
<Layout className="gap-1">
<div className=" p-0.5 rounded !text-blue-900">
<IconInfo size={14} strokeWidth={2} />
</div>
<span className="!text-blue-900 !block titlecase">{text}</span>
</div>
</Layout>
)
break
case 'WARNING':
return (
<div className="flex items-center h-full gap-1">
<Layout className="gap-1">
<div className=" p-0.5 rounded !text-amber-900">
<IconAlertCircle size={14} strokeWidth={2} />
</div>
<span className="!text-amber-900 !block titlecase">{text}</span>
</div>
</Layout>
)
break
// All other responses
default:
return (
<div className="flex items-center h-full">
<Layout>
<div className="relative rounded px-2 py-1 text-center h-6 flex justify-center items-center bg-scale-300">
<label className="block font-mono text-sm text-scale-900">{text}</label>
</div>
</div>
</Layout>
)
break
}
@@ -78,6 +78,7 @@ const TabSqlQuery = observer(() => {
<MonacoEditor
error={sqlEditorStore.activeTab.sqlQueryError}
updateSqlSnippet={updateSqlSnippet}
setUpdatingRequired={contentStore.setUpdatingRequired.bind(contentStore)}
/>
<div>
<UtilityPanel updateSqlSnippet={updateSqlSnippet} />
@@ -88,7 +89,7 @@ const TabSqlQuery = observer(() => {
})
export default TabSqlQuery
const MonacoEditor = ({ error, updateSqlSnippet }) => {
const MonacoEditor = ({ error, updateSqlSnippet, setUpdatingRequired }) => {
const sqlEditorStore = useSqlStore()
const editorRef = useRef(null)
const monacoRef = useRef(null)
@@ -173,6 +174,10 @@ const MonacoEditor = ({ error, updateSqlSnippet }) => {
// this is so any SQL run will be whatever is currently in monaco editor
sqlEditorStore.activeTab.setQuery(value)
// inform the content store that the SQL has changed and needs to be persisted
// this is so we can block the tab being closed if an update is required
setUpdatingRequired?.()
// debounce changes
debounceUpdateSqlSnippet(value)
}
@@ -233,7 +233,7 @@ const FileExplorerColumn = ({
{/* Drag drop upload CTA for when column is empty */}
{column.items.length === 0 && column.status !== STORAGE_ROW_STATUS.LOADING && (
<div className="h-full w-full flex flex-col items-center justify-center">
<img src="/img/storage-placeholder.svg" className="opacity-75" />
<img src="/img/storage-placeholder.svg" className="opacity-75 pointer-events-none" />
<p className="my-3 opacity-75">Drop your files here</p>
<p className="text-sm text-center w-40 text-scale-1100">
Or upload them via the "Upload file" button above
+31
View File
@@ -52,6 +52,37 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
handleEmailVerificationError()
}, [])
const getSavingState = () => rootStore.content.savingState
// prompt the user if they try and leave with unsaved content store changes
useEffect(() => {
const warningText = 'You have unsaved changes - are you sure you wish to leave this page?'
const handleWindowClose = (e: BeforeUnloadEvent) => {
const savingState = getSavingState()
const unsavedChanges =
savingState === 'UPDATING_REQUIRED' ||
savingState === 'UPDATING' ||
savingState === 'UPDATING_FAILED'
if (!unsavedChanges) {
return
}
e.preventDefault()
return (e.returnValue = warningText)
}
window.addEventListener('beforeunload', handleWindowClose)
return () => {
window.removeEventListener('beforeunload', handleWindowClose)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const getLayout = Component.getLayout ?? ((page) => page)
return (
+21 -2
View File
@@ -15,7 +15,13 @@ export interface IProjectContentStore {
isLoading: boolean
isInitialized: boolean
isLoaded: boolean
savingState: 'IDLE' | 'CREATING' | 'CREATING_FAILED' | 'UPDATING' | 'UPDATING_FAILED'
savingState:
| 'IDLE'
| 'CREATING'
| 'CREATING_FAILED'
| 'UPDATING_REQUIRED'
| 'UPDATING'
| 'UPDATING_FAILED'
error: any
recentLogSqlSnippets: LogSqlSnippets.Content[]
@@ -81,6 +87,7 @@ export interface IProjectContentStore {
del(id: any): Promise<{ data: boolean; error: unknown }>
delOptimistically(id: string): { data: boolean; error: null }
setUpdatingRequired(): void
}
export default class ProjectContentStore implements IProjectContentStore {
@@ -102,7 +109,13 @@ export default class ProjectContentStore implements IProjectContentStore {
recentLogSqlSnippets: LogSqlSnippets.Content[] = []
state = this.STATES.INITIAL
savingState: 'IDLE' | 'CREATING' | 'CREATING_FAILED' | 'UPDATING' | 'UPDATING_FAILED'
savingState:
| 'IDLE'
| 'CREATING'
| 'CREATING_FAILED'
| 'UPDATING_REQUIRED'
| 'UPDATING'
| 'UPDATING_FAILED'
error = null
constructor(rootStore: IRootStore, options: { projectRef: string }) {
@@ -354,6 +367,12 @@ export default class ProjectContentStore implements IProjectContentStore {
return { data: true, error: null }
}
setUpdatingRequired() {
if (this.savingState === 'IDLE' || this.savingState === 'UPDATING_FAILED') {
this.savingState = 'UPDATING_REQUIRED'
}
}
setProjectRef(ref?: string) {
if (ref) {
this.projectRef = ref
+2 -1
View File
@@ -14,7 +14,8 @@
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"downlevelIteration": true
"downlevelIteration": true,
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
"exclude": ["node_modules"]