Skip to content

Commit

Permalink
Several bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdc committed Jul 10, 2022
1 parent c749f67 commit 0ff99b5
Show file tree
Hide file tree
Showing 18 changed files with 1,014 additions and 855 deletions.
1 change: 1 addition & 0 deletions components/speciesSynonymy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const SpeciesSynonymy = ({ aliases, showAll }: SpeciesSynonymyProps): JSX.Elemen
responsive={false}
defaultSortFieldId="name"
customStyles={TABLE_CUSTOM_STYLES}
pagination={true}
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions hooks/useadmin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { yupResolver } from '@hookform/resolvers/yup';
import router from 'next/router';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Col, Row } from 'react-bootstrap';
import { DeepPartial, Path, UnpackNestedValue, useForm, UseFormReturn } from 'react-hook-form';
import { DeepPartial, Path, useForm, UseFormReturn } from 'react-hook-form';
import toast from 'react-hot-toast';
import * as yup from 'yup';
import { AnyObject, AssertsShape, ObjectShape, TypeOfShape } from 'yup/lib/object';
Expand Down Expand Up @@ -209,7 +209,7 @@ const useAdmin = <T extends WithID, FormFields extends AdminFormFields<T>, Upser

const onDataChange = useCallback(async (t: T | undefined) => {
const ff = await updatedFormFields(t);
form.reset(ff as UnpackNestedValue<DeepPartial<FormFields>>);
form.reset(ff as DeepPartial<FormFields>);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Expand Down
44 changes: 7 additions & 37 deletions libs/db/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ export const addImages = (images: ImageApi[]): TaskEither<Error, ImageApi[]> =>
};

export const updateImage = (theImage: ImageApi): TaskEither<Error, readonly ImageApi[]> => {
const connectSource = pipe(
theImage.source,
O.fold(constant({}), (s) => ({ connect: { id: s.id } })),
);

const update = (image: ImageApi) =>
db.image.update({
where: { id: image.id },
Expand All @@ -86,31 +91,10 @@ export const updateImage = (theImage: ImageApi): TaskEither<Error, readonly Imag
licenselink: image.licenselink,
sourcelink: image.sourcelink,
caption: image.caption,
// source: connectSource, // See below for why we can not do this here.
source: connectSource,
},
});

// more Prisma bugs causing more hacks: https://github.com/prisma/prisma/issues/3069
// TL;DR - prisma throws if trying to disconnect a record that is not connected even though the outcome
// is as intended and not really an error at all. This means that we either have to track state around the
// Source relationship being removed or we have to query before update. To keep it simple we will
// do the update of all but the relationship with a regular Prisma update, then raw SQL to update the Source.
// The intent here is that in teh near future Prisma will fix this and we can get rid of the transaction and
// the executeRaw call. If it were not on the immediate horizon it would be simpler to just have one raw
// update call.
const sourceid = (image: ImageApi) =>
pipe(
image.source,
O.fold(constant('NULL'), (s) => s.id.toString()),
);

const updateSourceRel = (image: ImageApi) => {
const sql = `UPDATE image
SET source_id = ${sourceid(image)}
WHERE image.id = ${image.id}`;
return db.$executeRaw(Prisma.sql([sql]));
};
// if this one is the new default, then make sure all of the other ones are not default
const setAsNewDefault = (image: ImageApi) => {
const spId = image.default ? image.speciesid : -999;
return db.image.updateMany({
Expand All @@ -127,7 +111,6 @@ export const updateImage = (theImage: ImageApi): TaskEither<Error, readonly Imag
return db.$transaction([
update(image),
setAsNewDefault(image),
updateSourceRel(image),
// refetch all of the images since some may have been updated by resetting defaults
db.image.findMany({
where: {
Expand All @@ -146,22 +129,9 @@ export const updateImage = (theImage: ImageApi): TaskEither<Error, readonly Imag

type TxRetType = ExtractTFromPromise<ReturnType<typeof doTx>>;

// eslint-disable-next-line prettier/prettier
return pipe(
TE.tryCatch<Error, TxRetType>(() => doTx(theImage), handleError),
TE.map<TxRetType, ImageApi[]>(([, , , images]) =>
images.map((i) => ({
...i,
speciesid: i.species_id,
small: makePath(i.path, SMALL),
medium: makePath(i.path, MEDIUM),
large: makePath(i.path, LARGE),
xlarge: makePath(i.path, XLARGE),
original: makePath(i.path, ORIGINAL),
source: O.fromNullable(i.source),
license: asLicenseType(i.license),
})),
),
TE.map<TxRetType, ImageApi[]>(([, , images]) => images.map(adaptImage)),
);
};

Expand Down
46 changes: 23 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.88.0",
"@aws-sdk/s3-request-presigner": "^3.88.0",
"@hookform/resolvers": "^2.8.8",
"@hookform/resolvers": "^2.9.5",
"@prisma/client": "^3.14.0",
"axios": "^0.27.2",
"bootstrap": "^5.1.3",
Expand All @@ -37,25 +37,25 @@
"fp-ts": "^2.12.1",
"gray-matter": "^4.0.3",
"io-ts": "^2.2.16",
"jest-environment-jsdom": "^28.1.0",
"jest-environment-jsdom": "^28.1.2",
"jimp": "^0.16.1",
"jquery": "^3.6.0",
"logging-ts": "^0.3.4",
"natural": "^5.2.2",
"next": "^12.1.6",
"next-auth": "^4.3.4",
"nuka-carousel": "^5.0.16",
"pino": "^7.11.0",
"next": "^12.2.2",
"next-auth": "^4.9.0",
"nuka-carousel": "^5.1.3",
"pino": "^8.1.0",
"popper.js": "^1.16.1",
"prop-types": "^15.8.1",
"react": "^18.1.0",
"react": "^18.2.0",
"react-bootstrap": "^2.4.0",
"react-bootstrap-typeahead": "^6.0.0-alpha.11",
"react-bootstrap-typeahead": "^6.0.0-rc.3",
"react-data-table-component": "^7.5.2",
"react-dom": "^18.1.0",
"react-hook-form": "^7.31.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.33.1",
"react-hot-toast": "^2.2.0",
"react-is": "^18.1.0",
"react-is": "^18.2.0",
"react-markdown": "^8.0.3",
"react-simple-maps": "^2.3.0",
"react-simple-tree-menu": "^1.1.18",
Expand All @@ -68,10 +68,10 @@
"remark-html": "^15.0.1",
"remove": "^0.1.5",
"sanitize-filename": "^1.6.3",
"sass": "^1.51.0",
"sharp": "^0.30.4",
"sass": "^1.53.0",
"sharp": "^0.30.7",
"styled-components": "^5.3.5",
"yarn": "^1.22.18",
"yarn": "^1.22.19",
"yup": "^0.32.11"
},
"devDependencies": {
Expand All @@ -91,18 +91,18 @@
"@types/yup": "^0.29.13",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"eslint": "^8.15.0",
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
"fast-check": "^2.25.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"fast-check": "^3.0.1",
"identity-obj-proxy": "^3.0.0",
"jest": "^28.1.0",
"prettier": "^2.6.2",
"jest": "^28.1.2",
"prettier": "^2.7.1",
"prisma": "^3.14.0",
"type-coverage": "^2.21.1",
"typescript": "^4.6.4",
"type-coverage": "^2.22.0",
"typescript": "^4.7.4",
"why-is-node-running": "^2.2.2"
}
}
1 change: 1 addition & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Head from 'next/head';
import React from 'react';
import { Col, Container, Row, SSRProvider } from 'react-bootstrap';
import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
import { ConfirmationServiceProvider } from '../hooks/useconfirmation';
import Footer from '../layouts/footer';
import Header from '../layouts/header';
Expand Down
14 changes: 8 additions & 6 deletions pages/admin/images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,12 @@ const Images = ({ speciesid, species }: Props): JSX.Element => {
} else {
throw new Error(await res.text());
}

setCurrentImage(undefined);
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const err: any = e;
console.error(err);
setError(err.toString());
const msg = `Error while trying to update image.\n${JSON.stringify(img)}\n${err}`;
console.error(msg);
setError(msg);
}
};

Expand All @@ -261,7 +260,7 @@ const Images = ({ speciesid, species }: Props): JSX.Element => {
variant: 'danger',
catchOnCancel: true,
title: 'Are you sure want to copy?',
message: `This will copy all of the metadata (source, source link, license, license link, creator, and attribution) from the original selected image to ${
message: `This will copy all of the metadata (source, source link, license, license link, creator, attribution, and caption) from the original selected image to ${
selectedForCopy.size > 1 ? `ALL ${selectedForCopy.size} of the other selected images` : `the other selected image`
}. Do you want to continue?`,
})
Expand All @@ -281,14 +280,17 @@ const Images = ({ speciesid, species }: Props): JSX.Element => {
attribution: copySource.attribution,
caption: copySource.caption,
}))
.map((i) => saveImage(i)),
.map((i) => {
saveImage(i);
}),
).catch((e: unknown) => setError(`Failed to save changes. ${e}.`));
})
.catch(() => {
setShowCopy(false);
Promise.resolve();
})
.finally(() => {
setCurrentImage(undefined);
setSelectedForCopy(new Set<number>());
setToggleCleared(!toggleCleared);
});
Expand Down
6 changes: 6 additions & 0 deletions pages/family/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ export const getStaticProps: GetStaticProps = async (context) => {

return {
props: {
// must add a key so that a navigation from the same route will re-render properly
key: pipe(
family,
O.map((f) => f.id),
O.getOrElse(constant(-1)),
),
family: family,
tree: tree,
},
Expand Down
34 changes: 21 additions & 13 deletions pages/gall/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,20 +263,28 @@ const Gall = ({ species, taxonomy, relatedGalls }: Props): JSX.Element => {

// Use static so that this stuff can be built once on the server-side and then cached.
export const getStaticProps: GetStaticProps = async (context) => {
const g = await getStaticPropsWithContext(context, gallById, 'gall');
const gall = g[0];
const sources = gall ? await linkSourceToGlossary(gall.speciessource) : null;
const fgs = gall ? await getStaticPropsWithContext(context, taxonomyForSpecies, 'taxonomy') : null;
const relatedGalls = gall ? await getStaticPropsWith<SimpleSpecies>(() => getRelatedGalls(gall), 'related galls') : null;
try {
const g = await getStaticPropsWithContext(context, gallById, 'gall');
if (!g[0]) throw '404';

return {
props: {
species: gall ? { ...gall, speciessource: sources } : null,
taxonomy: fgs,
relatedGalls: relatedGalls,
},
revalidate: 1,
};
const gall = g[0];
const sources = gall ? await linkSourceToGlossary(gall.speciessource) : null;
const fgs = gall ? await getStaticPropsWithContext(context, taxonomyForSpecies, 'taxonomy') : null;
const relatedGalls = gall ? await getStaticPropsWith<SimpleSpecies>(() => getRelatedGalls(gall), 'related galls') : null;

return {
props: {
// must add a key so that a navigation from the same route will re-render properly
key: gall.id,
species: gall ? { ...gall, speciessource: sources } : null,
taxonomy: fgs,
relatedGalls: relatedGalls,
},
revalidate: 1,
};
} catch (e) {
return { notFound: true };
}
};

export const getStaticPaths: GetStaticPaths = async () => getStaticPathsFromIds(allGallIds);
Expand Down
10 changes: 9 additions & 1 deletion pages/genus/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,17 @@ const Genus = ({ genus, species }: Props): JSX.Element => {

// Use static so that this stuff can be built once on the server-side and then cached.
export const getStaticProps: GetStaticProps = async (context) => {
const genus = await getStaticPropsWithContext(context, taxonomyEntryById, 'genus');

return {
props: {
genus: await getStaticPropsWithContext(context, taxonomyEntryById, 'genus'),
// must add a key so that a navigation from the same route will re-render properly
key: pipe(
genus,
O.map((g) => g.id),
O.getOrElse(constant(-1)),
),
genus: genus,
species: await getStaticPropsWithContext(context, getAllSpeciesForSectionOrGenus, 'species for genus', false, true),
},
revalidate: 1,
Expand Down
29 changes: 18 additions & 11 deletions pages/host/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,25 @@ const Host = ({ host, taxonomy }: Props): JSX.Element => {

// Use static so that this stuff can be built once on the server-side and then cached.
export const getStaticProps: GetStaticProps = async (context) => {
const h = await getStaticPropsWithContext(context, hostById, 'host');
const host = h[0];
const sources = host ? await linkSourceToGlossary(host.speciessource) : null;
const taxonomy = await getStaticPropsWithContext(context, taxonomyForSpecies, 'taxonomy');
try {
const h = await getStaticPropsWithContext(context, hostById, 'host');
if (!h[0]) throw '404';
const host = h[0];
const sources = host ? await linkSourceToGlossary(host.speciessource) : null;
const taxonomy = await getStaticPropsWithContext(context, taxonomyForSpecies, 'taxonomy');

return {
props: {
host: { ...host, speciessource: sources },
taxonomy: taxonomy,
},
revalidate: 1,
};
return {
props: {
// must add a key so that a navigation from the same route will re-render properly
key: host.id,
host: { ...host, speciessource: sources },
taxonomy: taxonomy,
},
revalidate: 1,
};
} catch (e) {
return { notFound: true };
}
};

export const getStaticPaths: GetStaticPaths = async () => getStaticPathsFromIds(allHostIds);
Expand Down
21 changes: 14 additions & 7 deletions pages/place/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,21 @@ const PlacePage = ({ place }: Props): JSX.Element => {

// Use static so that this stuff can be built once on the server-side and then cached.
export const getStaticProps: GetStaticProps = async (context) => {
const place = await getStaticPropsWithContext(context, placeById, 'place');
try {
const place = await getStaticPropsWithContext(context, placeById, 'place');
if (!place[0]) throw '404';

return {
props: {
place: place[0],
},
revalidate: 1,
};
return {
props: {
// must add a key so that a navigation from the same route will re-render properly
key: place[0]?.id,
place: place[0],
},
revalidate: 1,
};
} catch (e) {
return { notFound: true };
}
};

export const getStaticPaths: GetStaticPaths = async () => getStaticPathsFromIds(allPlaceIds);
Expand Down
Loading

0 comments on commit 0ff99b5

Please sign in to comment.