diff --git a/package-lock.json b/package-lock.json index 8afa3a9..bdbfd1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index dff1849..3659fe4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/@types/pocketbase-types.ts b/src/@types/pocketbase-types.ts index 9db7e0b..f0332cb 100644 --- a/src/@types/pocketbase-types.ts +++ b/src/@types/pocketbase-types.ts @@ -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 } diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index bff91db..1eeb982 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -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, - login: (email: string, password: string) => Promise, - refreshUser: () => Promise, + currentUser: UsersRecordFull | null, + signUp: (email: string, password: string, username: string) => Promise, + login: (email: string, password: string) => Promise, + refreshUser: () => Promise, sendEmailVerification: () => Promise, confirmEmailVerification: (token: string) => Promise, confirmResetPassword: (token: string, password: string) => Promise, @@ -57,21 +57,24 @@ const AuthContext = createContext(null); const useAuth = () => useContext(AuthContext as any); +const users = client.collection(Collections.Users); + const AuthProvider = (props: { children: ReactNode }) => { const { children } = props; - const [currentUser, setCurrentUser] = useState(null); + const [currentUser, setCurrentUser] = useState(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); }; }, []); diff --git a/src/hooks/useDb.ts b/src/hooks/useDb.ts index 113d5f0..53a269f 100644 --- a/src/hooks/useDb.ts +++ b/src/hooks/useDb.ts @@ -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}"`, }); diff --git a/src/pages/Hub.tsx b/src/pages/Hub.tsx index 08ab313..41fafbb 100644 --- a/src/pages/Hub.tsx +++ b/src/pages/Hub.tsx @@ -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), diff --git a/src/pages/Info.tsx b/src/pages/Info.tsx index 8027fa5..46c63f0 100644 --- a/src/pages/Info.tsx +++ b/src/pages/Info.tsx @@ -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 }) => { - + diff --git a/src/pages/Upload.tsx b/src/pages/Upload.tsx index 3626c7c..d53b5b8 100644 --- a/src/pages/Upload.tsx +++ b/src/pages/Upload.tsx @@ -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(); }); diff --git a/src/pages/auth/Login.tsx b/src/pages/auth/Login.tsx index ec775e4..548768b 100644 --- a/src/pages/auth/Login.tsx +++ b/src/pages/auth/Login.tsx @@ -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} /> + {!isLogin && + } + placeholder="Username" + autoComplete="name" + /> + } { )} Your Profile - {(currentUser?.profile?.username?.length || 0) === 0 && }
{ fields={[ { name: 'username', - value: currentUser?.profile?.username, + value: currentUser!.username, }, { name: 'email', - value: currentUser?.email, + value: currentUser!.email, }, ]} > diff --git a/src/pages/auth/notifications.ts b/src/pages/auth/notifications.ts index 24547e4..21b5c9e 100644 --- a/src/pages/auth/notifications.ts +++ b/src/pages/auth/notifications.ts @@ -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, diff --git a/src/pocketbase.ts b/src/pocketbase.ts index a5e5259..e5f76a2 100644 --- a/src/pocketbase.ts +++ b/src/pocketbase.ts @@ -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, }; diff --git a/src/types/dbData.ts b/src/types/dbData.ts index a2a57ff..6e2bcfb 100644 --- a/src/types/dbData.ts +++ b/src/types/dbData.ts @@ -1,8 +1,8 @@ import { Record } from '../pocketbase'; import { IniFilesRecord, - ProfilesRecord, TunesRecord, + UsersRecord, } from '../@types/pocketbase-types'; type Partial = { @@ -13,6 +13,6 @@ export type TunesRecordPartial = Partial; export interface TunesRecordFull extends TunesRecord, Record { } -export interface ProfilesRecordFull extends ProfilesRecord, Record { } +export interface UsersRecordFull extends UsersRecord, Record { } export interface IniFilesRecordFull extends IniFilesRecord, Record { } diff --git a/src/utils/time.ts b/src/utils/time.ts new file mode 100644 index 0000000..a63f47b --- /dev/null +++ b/src/utils/time.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export const formatTime = (time: string) => new Date(time).toLocaleString();