Migrate PocketBase to new version (#863)

This commit is contained in:
Piotr Rogowski 2022-10-30 13:18:28 +01:00 committed by GitHub
parent 1879c335c3
commit 84e59b10d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 175 additions and 145 deletions

128
package-lock.json generated
View File

@ -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": {

View File

@ -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",

View File

@ -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
}

View File

@ -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);
};
}, []);

View File

@ -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}"`,
});

View File

@ -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),

View File

@ -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}>

View File

@ -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();
});

View File

@ -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}

View File

@ -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,
},
]}
>

View File

@ -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,

View File

@ -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,
};

View File

@ -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 { }

2
src/utils/time.ts Normal file
View File

@ -0,0 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export const formatTime = (time: string) => new Date(time).toLocaleString();