Migrate PocketBase to new version (#863)
This commit is contained in:
parent
1879c335c3
commit
84e59b10d1
|
@ -20,7 +20,7 @@
|
|||
"mlg-converter": "^0.7.1",
|
||||
"nanoid": "^4.0.0",
|
||||
"pako": "^2.0.4",
|
||||
"pocketbase": "^0.7.4",
|
||||
"pocketbase": "^0.8.0-rc1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-ga4": "^1.4.1",
|
||||
|
@ -1887,6 +1887,18 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@gar/promisify": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
||||
|
@ -1895,14 +1907,14 @@
|
|||
"optional": true
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz",
|
||||
"integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==",
|
||||
"version": "0.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
|
||||
"integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
"debug": "^4.1.1",
|
||||
"minimatch": "^3.0.4"
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.10.0"
|
||||
|
@ -2461,6 +2473,18 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@hyper-tuner/eslint-config/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@hyper-tuner/ini": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://npm.pkg.github.com/download/@hyper-tuner/ini/0.6.2/fbca9ad490d805b93c1ce705ef9d2aee103498e7",
|
||||
|
@ -4107,9 +4131,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001426",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz",
|
||||
"integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==",
|
||||
"version": "1.0.30001427",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001427.tgz",
|
||||
"integrity": "sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5708,6 +5732,18 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
|
||||
|
@ -8622,9 +8658,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/pocketbase": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.7.4.tgz",
|
||||
"integrity": "sha512-PvBRi4hbgbiBwDjhHa9lGD/ala8dSTjKeNAsHAgsXdIo4v9RgCk2s3Zqd/4UXVBgTJHVM6F7fGOZPvvJfSNVLQ=="
|
||||
"version": "0.8.0-rc1",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.8.0-rc1.tgz",
|
||||
"integrity": "sha512-PXY2d0Em639n0WlixAhUtpsUH8At7S2VH0eqPWU0Ouiv7NwJLFFNTIE1IQL9F/cEJZF9BGb6FIYZqb/I4Fj4Iw=="
|
||||
},
|
||||
"node_modules/pocketbase-typegen": {
|
||||
"version": "1.0.11",
|
||||
|
@ -10511,18 +10547,6 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/tempy/node_modules/type-fest": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
|
||||
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.15.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
|
||||
|
@ -10670,9 +10694,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
|
||||
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
@ -12832,6 +12856,12 @@
|
|||
"requires": {
|
||||
"type-fest": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -12843,14 +12873,14 @@
|
|||
"optional": true
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz",
|
||||
"integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==",
|
||||
"version": "0.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
|
||||
"integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
"debug": "^4.1.1",
|
||||
"minimatch": "^3.0.4"
|
||||
"minimatch": "^3.0.5"
|
||||
}
|
||||
},
|
||||
"@humanwhocodes/module-importer": {
|
||||
|
@ -13221,6 +13251,12 @@
|
|||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -14467,9 +14503,9 @@
|
|||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001426",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz",
|
||||
"integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==",
|
||||
"version": "1.0.30001427",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001427.tgz",
|
||||
"integrity": "sha512-lfXQ73oB9c8DP5Suxaszm+Ta2sr/4tf8+381GkIm1MLj/YdLf+rEDyDSRCzeltuyTVGm+/s18gdZ0q+Wmp8VsQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
|
@ -15294,6 +15330,12 @@
|
|||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -17642,9 +17684,9 @@
|
|||
"optional": true
|
||||
},
|
||||
"pocketbase": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.7.4.tgz",
|
||||
"integrity": "sha512-PvBRi4hbgbiBwDjhHa9lGD/ala8dSTjKeNAsHAgsXdIo4v9RgCk2s3Zqd/4UXVBgTJHVM6F7fGOZPvvJfSNVLQ=="
|
||||
"version": "0.8.0-rc1",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.8.0-rc1.tgz",
|
||||
"integrity": "sha512-PXY2d0Em639n0WlixAhUtpsUH8At7S2VH0eqPWU0Ouiv7NwJLFFNTIE1IQL9F/cEJZF9BGb6FIYZqb/I4Fj4Iw=="
|
||||
},
|
||||
"pocketbase-typegen": {
|
||||
"version": "1.0.11",
|
||||
|
@ -18982,14 +19024,6 @@
|
|||
"temp-dir": "^2.0.0",
|
||||
"type-fest": "^0.16.0",
|
||||
"unique-string": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"type-fest": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
|
||||
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
|
@ -19111,9 +19145,9 @@
|
|||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz",
|
||||
"integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"mlg-converter": "^0.7.1",
|
||||
"nanoid": "^4.0.0",
|
||||
"pako": "^2.0.4",
|
||||
"pocketbase": "^0.7.4",
|
||||
"pocketbase": "^0.8.0-rc1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-ga4": "^1.4.1",
|
||||
|
|
|
@ -10,14 +10,14 @@ export type BaseRecord = {
|
|||
id: RecordIdString
|
||||
created: IsoDateString
|
||||
updated: IsoDateString
|
||||
'@collectionId': string
|
||||
'@collectionName': string
|
||||
collectionId: string
|
||||
collectionName: string
|
||||
}
|
||||
|
||||
export enum Collections {
|
||||
IniFiles = 'iniFiles',
|
||||
Profiles = 'profiles',
|
||||
Tunes = 'tunes',
|
||||
Users = 'users',
|
||||
}
|
||||
|
||||
export type IniFilesRecord = {
|
||||
|
@ -26,15 +26,8 @@ export type IniFilesRecord = {
|
|||
ecosystem: 'speeduino' | 'rusefi'
|
||||
}
|
||||
|
||||
export type ProfilesRecord = {
|
||||
userId: UserIdString
|
||||
username: string
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
export type TunesRecord = {
|
||||
user: UserIdString
|
||||
userProfile: RecordIdString
|
||||
author: RecordIdString
|
||||
tuneId: string
|
||||
signature: string
|
||||
vehicleName: string
|
||||
|
@ -59,8 +52,12 @@ export type TunesRecord = {
|
|||
toothLogFiles?: string[]
|
||||
}
|
||||
|
||||
export type UsersRecord = {
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
export type CollectionRecords = {
|
||||
iniFiles: IniFilesRecord
|
||||
profiles: ProfilesRecord
|
||||
tunes: TunesRecord
|
||||
users: UsersRecord
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import {
|
|||
import {
|
||||
client,
|
||||
formatError,
|
||||
User,
|
||||
} from '../pocketbase';
|
||||
import { buildRedirectUrl } from '../utils/url';
|
||||
import { Collections } from '../@types/pocketbase-types';
|
||||
import { Routes } from '../routes';
|
||||
import { UsersRecordFull } from '../types/dbData';
|
||||
|
||||
// TODO: this should be imported from pocketbase but currently is not exported
|
||||
export type AuthProviderInfo = {
|
||||
|
@ -39,10 +39,10 @@ export enum OAuthProviders {
|
|||
};
|
||||
|
||||
interface AuthValue {
|
||||
currentUser: User | null,
|
||||
signUp: (email: string, password: string) => Promise<User>,
|
||||
login: (email: string, password: string) => Promise<User>,
|
||||
refreshUser: () => Promise<User | null>,
|
||||
currentUser: UsersRecordFull | null,
|
||||
signUp: (email: string, password: string, username: string) => Promise<UsersRecordFull>,
|
||||
login: (email: string, password: string) => Promise<UsersRecordFull>,
|
||||
refreshUser: () => Promise<UsersRecordFull | null>,
|
||||
sendEmailVerification: () => Promise<void>,
|
||||
confirmEmailVerification: (token: string) => Promise<void>,
|
||||
confirmResetPassword: (token: string, password: string) => Promise<void>,
|
||||
|
@ -57,21 +57,24 @@ const AuthContext = createContext<AuthValue | null>(null);
|
|||
|
||||
const useAuth = () => useContext<AuthValue>(AuthContext as any);
|
||||
|
||||
const users = client.collection(Collections.Users);
|
||||
|
||||
const AuthProvider = (props: { children: ReactNode }) => {
|
||||
const { children } = props;
|
||||
const [currentUser, setCurrentUser] = useState<User | null>(null);
|
||||
const [currentUser, setCurrentUser] = useState<UsersRecordFull | null>(null);
|
||||
|
||||
const value = useMemo(() => ({
|
||||
currentUser,
|
||||
signUp: async (email: string, password: string) => {
|
||||
signUp: async (email: string, password: string, username: string) => {
|
||||
try {
|
||||
const user = await client.users.create({
|
||||
const user = await users.create({
|
||||
email,
|
||||
password,
|
||||
passwordConfirm: password,
|
||||
username,
|
||||
});
|
||||
client.users.requestVerification(user.email);
|
||||
await client.users.authViaEmail(user.email, password);
|
||||
users.requestVerification(email);
|
||||
await users.authWithPassword(email, password);
|
||||
|
||||
return Promise.resolve(user);
|
||||
} catch (error) {
|
||||
|
@ -80,16 +83,16 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
login: async (email: string, password: string) => {
|
||||
try {
|
||||
const authResponse = await client.users.authViaEmail(email, password);
|
||||
return Promise.resolve(authResponse.user);
|
||||
const authResponse = await users.authWithPassword(email, password);
|
||||
return Promise.resolve(authResponse.record);
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
}
|
||||
},
|
||||
refreshUser: async () => {
|
||||
try {
|
||||
const authResponse = await client.users.refresh();
|
||||
return Promise.resolve(authResponse.user);
|
||||
const authResponse = await users.authRefresh();
|
||||
return Promise.resolve(authResponse.record);
|
||||
} catch (error) {
|
||||
client.authStore.clear();
|
||||
return Promise.resolve(null);
|
||||
|
@ -97,7 +100,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
sendEmailVerification: async () => {
|
||||
try {
|
||||
await client.users.requestVerification(currentUser!.email);
|
||||
await users.requestVerification(currentUser!.email);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
|
@ -105,7 +108,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
confirmEmailVerification: async (token: string) => {
|
||||
try {
|
||||
await client.users.confirmVerification(token);
|
||||
await users.confirmVerification(token);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
|
@ -113,7 +116,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
confirmResetPassword: async (token: string, password: string) => {
|
||||
try {
|
||||
await client.users.confirmPasswordReset(token, password, password);
|
||||
await users.confirmPasswordReset(token, password, password);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
|
@ -124,7 +127,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
initResetPassword: async (email: string) => {
|
||||
try {
|
||||
await client.users.requestPasswordReset(email);
|
||||
await users.requestPasswordReset(email);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
|
@ -132,14 +135,14 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
listAuthMethods: async () => {
|
||||
try {
|
||||
const methods = await client.users.listAuthMethods();
|
||||
const methods = await users.listAuthMethods();
|
||||
return Promise.resolve(methods);
|
||||
} catch (error) {
|
||||
return Promise.reject(new Error(formatError(error)));
|
||||
}
|
||||
},
|
||||
oAuth: async (provider: OAuthProviders, code: string, codeVerifier: string) => {
|
||||
client.users.authViaOAuth2(
|
||||
users.authWithOAuth2(
|
||||
provider,
|
||||
code,
|
||||
codeVerifier,
|
||||
|
@ -148,7 +151,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
},
|
||||
updateUsername: async (username: string) => {
|
||||
try {
|
||||
await client.records.update(Collections.Profiles, currentUser!.profile!.id, {
|
||||
await client.collection(Collections.Users).update(currentUser!.id, {
|
||||
username,
|
||||
});
|
||||
return Promise.resolve();
|
||||
|
@ -159,24 +162,14 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
}), [currentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentUser(client.authStore.model as User | null);
|
||||
setCurrentUser(client.authStore.model as UsersRecordFull | null);
|
||||
|
||||
const storeUnsubscribe = client.authStore.onChange((_token, model) => {
|
||||
setCurrentUser(model as User | null);
|
||||
});
|
||||
|
||||
client.realtime.subscribe(Collections.Tunes, (event) => {
|
||||
console.info('Tunes event', event);
|
||||
});
|
||||
|
||||
client.realtime.subscribe(Collections.Profiles, (event) => {
|
||||
console.info('Profiles event', event);
|
||||
setCurrentUser(model as UsersRecordFull | null);
|
||||
});
|
||||
|
||||
return () => {
|
||||
storeUnsubscribe();
|
||||
client.realtime.unsubscribe(Collections.Tunes);
|
||||
client.realtime.unsubscribe(Collections.Profiles);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -15,10 +15,13 @@ import {
|
|||
TunesRecord,
|
||||
} from '../@types/pocketbase-types';
|
||||
|
||||
const tunesCollection = client.collection(Collections.Tunes);
|
||||
const iniFilesCollection = client.collection(Collections.IniFiles);
|
||||
|
||||
const useDb = () => {
|
||||
const updateTune = async (id: string, data: TunesRecordPartial) => {
|
||||
try {
|
||||
await client.records.update(Collections.Tunes, id, data);
|
||||
await tunesCollection.update(id, data);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
Sentry.captureException(error);
|
||||
|
@ -30,7 +33,7 @@ const useDb = () => {
|
|||
|
||||
const createTune = async (data: TunesRecord) => {
|
||||
try {
|
||||
const record = await client.records.create(Collections.Tunes, data);
|
||||
const record = await tunesCollection.create(data);
|
||||
|
||||
return Promise.resolve(record as TunesRecordFull);
|
||||
} catch (error) {
|
||||
|
@ -43,17 +46,23 @@ const useDb = () => {
|
|||
|
||||
const getTune = async (tuneId: string) => {
|
||||
try {
|
||||
const tune = await client.records.getList(Collections.Tunes, 1, 1, {
|
||||
filter: `tuneId = "${tuneId}"`,
|
||||
expand: 'userProfile',
|
||||
});
|
||||
const tune = await tunesCollection.getFirstListItem(
|
||||
`tuneId = "${tuneId}"`,
|
||||
{
|
||||
expand: 'author',
|
||||
},
|
||||
);
|
||||
|
||||
return Promise.resolve(tune.totalItems > 0 ? tune.items[0] as TunesRecordFull : null);
|
||||
return Promise.resolve(tune as TunesRecordFull);
|
||||
} catch (error) {
|
||||
if ((error as ClientResponseError).isAbort) {
|
||||
return Promise.reject(new Error('Cancelled'));
|
||||
}
|
||||
|
||||
if ((error as ClientResponseError).status === 404) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
Sentry.captureException(error);
|
||||
databaseGenericError(new Error(formatError(error)));
|
||||
|
||||
|
@ -63,16 +72,18 @@ const useDb = () => {
|
|||
|
||||
const getIni = async (signature: string) => {
|
||||
try {
|
||||
const tune = await client.records.getList(Collections.IniFiles, 1, 1, {
|
||||
filter: `signature = "${signature}"`,
|
||||
});
|
||||
const ini = await iniFilesCollection.getFirstListItem(`signature = "${signature}"`);
|
||||
|
||||
return Promise.resolve(tune.totalItems > 0 ? tune.items[0] as IniFilesRecordFull : null);
|
||||
return Promise.resolve(ini as IniFilesRecordFull);
|
||||
} catch (error) {
|
||||
if ((error as ClientResponseError).isAbort) {
|
||||
return Promise.reject(new Error('Cancelled'));
|
||||
}
|
||||
|
||||
if ((error as ClientResponseError).status === 404) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
Sentry.captureException(error);
|
||||
databaseGenericError(new Error(formatError(error)));
|
||||
|
||||
|
@ -88,10 +99,10 @@ const useDb = () => {
|
|||
.join(' || ');
|
||||
|
||||
try {
|
||||
const list = await client.records.getList(Collections.Tunes, page, perPage, {
|
||||
const list = await tunesCollection.getList(page, perPage, {
|
||||
sort: '-updated',
|
||||
filter,
|
||||
expand: 'userProfile',
|
||||
expand: 'author',
|
||||
});
|
||||
|
||||
return Promise.resolve({
|
||||
|
@ -112,7 +123,7 @@ const useDb = () => {
|
|||
|
||||
const autocomplete = async (attribute: string, search: string) => {
|
||||
try {
|
||||
const items = await client.records.getFullList(Collections.Tunes, 10, {
|
||||
const items = await tunesCollection.getFullList(10, {
|
||||
filter: `${attribute} ~ "${search}"`,
|
||||
});
|
||||
|
||||
|
|
|
@ -33,10 +33,12 @@ import {
|
|||
copyToClipboard,
|
||||
isClipboardSupported,
|
||||
} from '../utils/clipboard';
|
||||
import { ProfilesRecord } from '../@types/pocketbase-types';
|
||||
import { isEscape } from '../utils/keyboard/shortcuts';
|
||||
import { TunesRecordFull } from '../types/dbData';
|
||||
import { formatTime } from '../pocketbase';
|
||||
import {
|
||||
TunesRecordFull,
|
||||
UsersRecordFull,
|
||||
} from '../types/dbData';
|
||||
import { formatTime } from '../utils/time';
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
const { Text, Title } = Typography;
|
||||
|
@ -64,7 +66,7 @@ const Hub = () => {
|
|||
...tune,
|
||||
key: tune.tuneId,
|
||||
year: tune.year,
|
||||
author: (tune['@expand'] as { userProfile: ProfilesRecord }).userProfile.username,
|
||||
author: (tune.expand.author as unknown as UsersRecordFull).username,
|
||||
displacement: `${tune.displacement}l`,
|
||||
aspiration: aspirationMapper[tune.aspiration],
|
||||
published: formatTime(tune.updated),
|
||||
|
|
|
@ -21,7 +21,8 @@ import {
|
|||
import Loader from '../components/Loader';
|
||||
import { Routes } from '../routes';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { formatTime } from '../pocketbase';
|
||||
import { formatTime } from '../utils/time';
|
||||
import { UsersRecordFull } from '../types/dbData';
|
||||
|
||||
const { Item } = Form;
|
||||
const rowProps = { gutter: 10 };
|
||||
|
@ -39,7 +40,7 @@ const Info = ({ tuneData }: { tuneData: TuneDataState }) => {
|
|||
tuneId: tuneData.tuneId,
|
||||
}));
|
||||
|
||||
const canManage = currentUser && tuneData && currentUser.id === tuneData.user;
|
||||
const canManage = currentUser && tuneData && currentUser.id === tuneData.author;
|
||||
|
||||
const manageSection = (
|
||||
<>
|
||||
|
@ -70,7 +71,7 @@ const Info = ({ tuneData }: { tuneData: TuneDataState }) => {
|
|||
<Row {...rowProps}>
|
||||
<Col {...colProps}>
|
||||
<Item>
|
||||
<Input value={tuneData['@expand'].userProfile.username} addonBefore="Author" />
|
||||
<Input value={(tuneData.expand.author as unknown as UsersRecordFull).username} addonBefore="Author" />
|
||||
</Item>
|
||||
</Col>
|
||||
<Col {...colProps}>
|
||||
|
|
|
@ -50,7 +50,6 @@ import {
|
|||
error,
|
||||
restrictedPage,
|
||||
signatureNotSupportedWarning,
|
||||
usernameNotSet,
|
||||
} from './auth/notifications';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { Routes } from '../routes';
|
||||
|
@ -211,8 +210,7 @@ const UploadPage = () => {
|
|||
const { signature } = tuneParser.parse(await tuneFile!.arrayBuffer()).getTune().details;
|
||||
|
||||
const newData: TunesRecord = {
|
||||
user: currentUser!.id,
|
||||
userProfile: currentUser!.profile!.id,
|
||||
author: currentUser!.id,
|
||||
tuneId: newTuneId!,
|
||||
signature,
|
||||
vehicleName,
|
||||
|
@ -434,7 +432,7 @@ const UploadPage = () => {
|
|||
|
||||
if (oldTune) {
|
||||
// this is someone elses tune
|
||||
if (oldTune.user !== currentUser?.id) {
|
||||
if (oldTune.author !== currentUser?.id) {
|
||||
navigateToNewTuneId();
|
||||
return;
|
||||
}
|
||||
|
@ -533,13 +531,6 @@ const UploadPage = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
if ((user.profile?.username?.length || 0) === 0) {
|
||||
usernameNotSet();
|
||||
navigate(Routes.PROFILE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setIsUserAuthorized(true);
|
||||
prepareData();
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
GithubOutlined,
|
||||
FacebookOutlined,
|
||||
UserAddOutlined,
|
||||
UserOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Link,
|
||||
|
@ -40,6 +41,7 @@ import {
|
|||
import {
|
||||
emailRules,
|
||||
requiredRules,
|
||||
usernameRules,
|
||||
} from '../../utils/form';
|
||||
import { buildRedirectUrl } from '../../utils/url';
|
||||
|
||||
|
@ -89,10 +91,10 @@ const Login = ({ formRole }: { formRole: FormRoles }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const emailSignUp = async ({ email, password }: { email: string, password: string }) => {
|
||||
const emailSignUp = async ({ email, password, username }: { email: string, password: string, username: string }) => {
|
||||
setIsEmailLoading(true);
|
||||
try {
|
||||
const user = await signUp(email, password);
|
||||
const user = await signUp(email, password, username);
|
||||
signUpSuccessful();
|
||||
|
||||
if (!user.verified) {
|
||||
|
@ -242,6 +244,17 @@ const Login = ({ formRole }: { formRole: FormRoles }) => {
|
|||
disabled={isAnythingLoading}
|
||||
/>
|
||||
</Item>
|
||||
{!isLogin && <Item
|
||||
name="username"
|
||||
rules={usernameRules}
|
||||
hasFeedback
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined />}
|
||||
placeholder="Username"
|
||||
autoComplete="name"
|
||||
/>
|
||||
</Item>}
|
||||
<Item
|
||||
name="password"
|
||||
rules={requiredRules}
|
||||
|
|
|
@ -106,7 +106,6 @@ const Profile = () => {
|
|||
</>)}
|
||||
<Divider>Your Profile</Divider>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||
{(currentUser?.profile?.username?.length || 0) === 0 && <Alert message="Remember to set your username!" type="error" showIcon />}
|
||||
<Form
|
||||
validateMessages={validateMessages}
|
||||
form={formProfile}
|
||||
|
@ -114,11 +113,11 @@ const Profile = () => {
|
|||
fields={[
|
||||
{
|
||||
name: 'username',
|
||||
value: currentUser?.profile?.username,
|
||||
value: currentUser!.username,
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
value: currentUser?.email,
|
||||
value: currentUser!.email,
|
||||
},
|
||||
]}
|
||||
>
|
||||
|
|
|
@ -19,12 +19,6 @@ const emailNotVerified = () => notification.warning({
|
|||
...baseOptions,
|
||||
});
|
||||
|
||||
const usernameNotSet = () => notification.warning({
|
||||
message: 'Update your profile',
|
||||
description: 'Your username has to be set before you can upload files!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const signUpSuccessful = () => notification.success({
|
||||
message: 'Sign Up successful',
|
||||
description: 'Welcome on board!',
|
||||
|
@ -156,7 +150,6 @@ const downloading = () => notification.success({
|
|||
export {
|
||||
error,
|
||||
emailNotVerified,
|
||||
usernameNotSet,
|
||||
signUpSuccessful,
|
||||
signUpFailed,
|
||||
logInSuccessful,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import PocketBase, {
|
||||
ClientResponseError,
|
||||
User,
|
||||
Record,
|
||||
} from 'pocketbase';
|
||||
import { fetchEnv } from './utils/env';
|
||||
|
@ -23,16 +22,11 @@ const formatError = (error: any) => {
|
|||
|
||||
const removeFilenameSuffix = (filename: string) => filename.replace(/(.+)(_\w{10})(\.\w+)$/, '$1$3');
|
||||
|
||||
// NOTE: PocketBase doesn't return ISO time, this may change here: https://github.com/pocketbase/pocketbase/issues/376
|
||||
const formatTime = (time: string) => new Date(`${time}Z`).toLocaleString();
|
||||
|
||||
export {
|
||||
API_URL,
|
||||
client,
|
||||
formatError,
|
||||
formatTime,
|
||||
removeFilenameSuffix,
|
||||
ClientResponseError,
|
||||
User,
|
||||
Record,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Record } from '../pocketbase';
|
||||
import {
|
||||
IniFilesRecord,
|
||||
ProfilesRecord,
|
||||
TunesRecord,
|
||||
UsersRecord,
|
||||
} from '../@types/pocketbase-types';
|
||||
|
||||
type Partial<T> = {
|
||||
|
@ -13,6 +13,6 @@ export type TunesRecordPartial = Partial<TunesRecord>;
|
|||
|
||||
export interface TunesRecordFull extends TunesRecord, Record { }
|
||||
|
||||
export interface ProfilesRecordFull extends ProfilesRecord, Record { }
|
||||
export interface UsersRecordFull extends UsersRecord, Record { }
|
||||
|
||||
export interface IniFilesRecordFull extends IniFilesRecord, Record { }
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const formatTime = (time: string) => new Date(time).toLocaleString();
|
Loading…
Reference in New Issue