Compare commits

...

4 Commits

14 changed files with 134 additions and 49 deletions

2
.lfsconfig Normal file
View File

@@ -0,0 +1,2 @@
[lfs]
url = "https://git.chuckdries.com/chuckdries/Personal-Website.git/info/lfs/"

BIN
data/gallery/DSC00003.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC00159-2.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC06719.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC06803.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC08263.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC09447.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC09454.jpg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -139,6 +139,7 @@ function transformMetaToNodeData(
ISO: metaData.ISO, ISO: metaData.ISO,
DateTimeOriginal: metaData.DateTimeOriginal, DateTimeOriginal: metaData.DateTimeOriginal,
CreateDate: metaData.CreateDate, CreateDate: metaData.CreateDate,
ModifyDate: metaData.ModifyDate,
ShutterSpeedValue: metaData.ShutterSpeedValue, ShutterSpeedValue: metaData.ShutterSpeedValue,
ApertureValue: metaData.ApertureValue, ApertureValue: metaData.ApertureValue,
FocalLength: metaData.FocalLength, FocalLength: metaData.FocalLength,

View File

@@ -91,7 +91,7 @@ function Option({ item, state }: OptionProps) {
return ( return (
<li <li
{...optionProps} {...optionProps}
className={`p-3 outline-none cursor-default flex items-center justify-between ${text} text-sm ${ className={`p-2 outline-none cursor-default flex items-center justify-between ${text} text-sm ${
isFocused ? "bg-transparentblack" : "" isFocused ? "bg-transparentblack" : ""
} ${isSelected ? "font-bold" : ""}`} } ${isSelected ? "font-bold" : ""}`}
ref={ref} ref={ref}

View File

@@ -18,7 +18,7 @@ interface Row {
} }
interface MasonryGalleryProps { interface MasonryGalleryProps {
images: GalleryImage[]; images: readonly GalleryImage[];
aspectsByBreakpoint: { aspectsByBreakpoint: {
[breakpoint: string]: number; [breakpoint: string]: number;
}; };
@@ -26,6 +26,7 @@ interface MasonryGalleryProps {
debugRating?: boolean; debugRating?: boolean;
linkState?: object; linkState?: object;
showPalette?: boolean; showPalette?: boolean;
singleRow?: boolean;
} }
const MasonryGallery = ({ const MasonryGallery = ({
@@ -35,6 +36,7 @@ const MasonryGallery = ({
debugRating, debugRating,
linkState, linkState,
showPalette, showPalette,
singleRow,
}: MasonryGalleryProps) => { }: MasonryGalleryProps) => {
const [isClient, setIsClient] = React.useState(false); const [isClient, setIsClient] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
@@ -83,6 +85,14 @@ const MasonryGallery = ({
}, },
]; ];
} }
// no-op instead of starting a new row
if (singleRow) {
return [
...acc,
currentRow,
]
}
// start a new row
return [ return [
...acc, ...acc,
currentRow, currentRow,

16
src/gatsby-types.d.ts vendored
View File

@@ -617,6 +617,7 @@ type FileFieldsImageMetaMeta = {
readonly Location: Maybe<Scalars['String']>; readonly Location: Maybe<Scalars['String']>;
readonly Make: Maybe<Scalars['String']>; readonly Make: Maybe<Scalars['String']>;
readonly Model: Maybe<Scalars['String']>; readonly Model: Maybe<Scalars['String']>;
readonly ModifyDate: Maybe<Scalars['Date']>;
readonly ObjectName: Maybe<Scalars['String']>; readonly ObjectName: Maybe<Scalars['String']>;
readonly Rating: Maybe<Scalars['Int']>; readonly Rating: Maybe<Scalars['Int']>;
readonly ShutterSpeedValue: Maybe<Scalars['Float']>; readonly ShutterSpeedValue: Maybe<Scalars['Float']>;
@@ -639,6 +640,14 @@ type FileFieldsImageMetaMeta_DateTimeOriginalArgs = {
locale: InputMaybe<Scalars['String']>; locale: InputMaybe<Scalars['String']>;
}; };
type FileFieldsImageMetaMeta_ModifyDateArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type FileFieldsImageMetaMetaFieldSelector = { type FileFieldsImageMetaMetaFieldSelector = {
readonly ApertureValue: InputMaybe<FieldSelectorEnum>; readonly ApertureValue: InputMaybe<FieldSelectorEnum>;
readonly Caption: InputMaybe<FieldSelectorEnum>; readonly Caption: InputMaybe<FieldSelectorEnum>;
@@ -654,6 +663,7 @@ type FileFieldsImageMetaMetaFieldSelector = {
readonly Location: InputMaybe<FieldSelectorEnum>; readonly Location: InputMaybe<FieldSelectorEnum>;
readonly Make: InputMaybe<FieldSelectorEnum>; readonly Make: InputMaybe<FieldSelectorEnum>;
readonly Model: InputMaybe<FieldSelectorEnum>; readonly Model: InputMaybe<FieldSelectorEnum>;
readonly ModifyDate: InputMaybe<FieldSelectorEnum>;
readonly ObjectName: InputMaybe<FieldSelectorEnum>; readonly ObjectName: InputMaybe<FieldSelectorEnum>;
readonly Rating: InputMaybe<FieldSelectorEnum>; readonly Rating: InputMaybe<FieldSelectorEnum>;
readonly ShutterSpeedValue: InputMaybe<FieldSelectorEnum>; readonly ShutterSpeedValue: InputMaybe<FieldSelectorEnum>;
@@ -675,6 +685,7 @@ type FileFieldsImageMetaMetaFilterInput = {
readonly Location: InputMaybe<StringQueryOperatorInput>; readonly Location: InputMaybe<StringQueryOperatorInput>;
readonly Make: InputMaybe<StringQueryOperatorInput>; readonly Make: InputMaybe<StringQueryOperatorInput>;
readonly Model: InputMaybe<StringQueryOperatorInput>; readonly Model: InputMaybe<StringQueryOperatorInput>;
readonly ModifyDate: InputMaybe<DateQueryOperatorInput>;
readonly ObjectName: InputMaybe<StringQueryOperatorInput>; readonly ObjectName: InputMaybe<StringQueryOperatorInput>;
readonly Rating: InputMaybe<IntQueryOperatorInput>; readonly Rating: InputMaybe<IntQueryOperatorInput>;
readonly ShutterSpeedValue: InputMaybe<FloatQueryOperatorInput>; readonly ShutterSpeedValue: InputMaybe<FloatQueryOperatorInput>;
@@ -696,6 +707,7 @@ type FileFieldsImageMetaMetaSortInput = {
readonly Location: InputMaybe<SortOrderEnum>; readonly Location: InputMaybe<SortOrderEnum>;
readonly Make: InputMaybe<SortOrderEnum>; readonly Make: InputMaybe<SortOrderEnum>;
readonly Model: InputMaybe<SortOrderEnum>; readonly Model: InputMaybe<SortOrderEnum>;
readonly ModifyDate: InputMaybe<SortOrderEnum>;
readonly ObjectName: InputMaybe<SortOrderEnum>; readonly ObjectName: InputMaybe<SortOrderEnum>;
readonly Rating: InputMaybe<SortOrderEnum>; readonly Rating: InputMaybe<SortOrderEnum>;
readonly ShutterSpeedValue: InputMaybe<SortOrderEnum>; readonly ShutterSpeedValue: InputMaybe<SortOrderEnum>;
@@ -2547,10 +2559,12 @@ type GalleryImageQueryVariables = Exact<{
type GalleryImageQuery = { readonly file: { readonly base: string, readonly publicURL: string | null, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly dateTaken: string | null, readonly meta: { readonly Make: string | null, readonly Model: string | null, readonly ExposureTime: number | null, readonly FNumber: number | null, readonly ISO: number | null, readonly DateTimeOriginal: string | null, readonly CreateDate: string | null, readonly ShutterSpeedValue: number | null, readonly ApertureValue: number | null, readonly FocalLength: number | null, readonly LensModel: string | null, readonly ObjectName: string | null, readonly Caption: string | null, readonly Location: string | null, readonly City: string | null, readonly State: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null } | null }; type GalleryImageQuery = { readonly file: { readonly base: string, readonly publicURL: string | null, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly dateTaken: string | null, readonly meta: { readonly Make: string | null, readonly Model: string | null, readonly ExposureTime: number | null, readonly FNumber: number | null, readonly ISO: number | null, readonly DateTimeOriginal: string | null, readonly CreateDate: string | null, readonly ShutterSpeedValue: number | null, readonly ApertureValue: number | null, readonly FocalLength: number | null, readonly LensModel: string | null, readonly ObjectName: string | null, readonly Caption: string | null, readonly Location: string | null, readonly City: string | null, readonly State: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null } | null };
type GalleryImageFileFragment = { readonly nodes: ReadonlyArray<{ readonly relativePath: string, readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> };
type GalleryPageQueryQueryVariables = Exact<{ [key: string]: never; }>; type GalleryPageQueryQueryVariables = Exact<{ [key: string]: never; }>;
type GalleryPageQueryQuery = { readonly allFile: { readonly nodes: ReadonlyArray<{ readonly relativePath: string, readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> } }; type GalleryPageQueryQuery = { readonly recents: { readonly nodes: ReadonlyArray<{ readonly relativePath: string, readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> }, readonly all: { readonly nodes: ReadonlyArray<{ readonly relativePath: string, readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> } };
type GatsbyImageSharpFixedFragment = { readonly base64: string | null, readonly width: number, readonly height: number, readonly src: string, readonly srcSet: string }; type GatsbyImageSharpFixedFragment = { readonly base64: string | null, readonly width: number, readonly height: number, readonly src: string, readonly srcSet: string };

View File

@@ -20,11 +20,12 @@ const SORT_KEYS = {
hue: ["fields", "imageMeta", "vibrantHue"], hue: ["fields", "imageMeta", "vibrantHue"],
rating: ["fields", "imageMeta", "meta", "Rating"], rating: ["fields", "imageMeta", "meta", "Rating"],
hue_debug: ["fields", "imageMeta", "dominantHue", 0], hue_debug: ["fields", "imageMeta", "dominantHue", 0],
date: [], date: ["fields", "imageMeta", "dateTaken"],
modified: ["fields", "imageMeta", "meta", "ModifyDate"]
} as const; } as const;
export type GalleryImage = export type GalleryImage =
Queries.GalleryPageQueryQuery["allFile"]["nodes"][number]; Queries.GalleryPageQueryQuery["all"]["nodes"][number];
const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
const hash = const hash =
@@ -128,13 +129,13 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
const images: GalleryImage[] = React.useMemo(() => { const images: GalleryImage[] = React.useMemo(() => {
const sort = const sort =
sortKey === "date" sortKey === "date" || sortKey === "modified"
? R.sort((node1: typeof data["allFile"]["nodes"][number], node2) => { ? R.sort((node1: typeof data["all"]["nodes"][number], node2) => {
const date1 = new Date( const date1 = new Date(
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node1) R.pathOr("", SORT_KEYS[sortKey], node1)
); );
const date2 = new Date( const date2 = new Date(
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node2) R.pathOr("", SORT_KEYS[sortKey], node2)
); );
return -1 * (date1.getTime() - date2.getTime()); return -1 * (date1.getTime() - date2.getTime());
}) })
@@ -157,7 +158,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
// @ts-ignore // @ts-ignore
sort, sort,
filter filter
)(data.allFile.nodes) as any; )(data.all.nodes) as any;
return ret; return ret;
} catch (e) { } catch (e) {
console.log("caught images!", e); console.log("caught images!", e);
@@ -174,6 +175,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
className="bg-white transition-color" className="bg-white transition-color"
// @ts-ignore // @ts-ignore
style={getHelmetSafeBodyStyle( style={getHelmetSafeBodyStyle(
// @ts-ignore shrug
getVibrantStyle({ getVibrantStyle({
Muted: [0, 0, 0], Muted: [0, 0, 0],
LightMuted: [0, 0, 0], LightMuted: [0, 0, 0],
@@ -195,6 +197,25 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
]} ]}
/> />
</div> </div>
<div className="px-4 md:px-8">
<h3 id="recently" className="mx-2 font-bold">Recently updated</h3>
</div>
<MasonryGallery
aspectsByBreakpoint={{
xs: 2,
sm: 2,
md: 3,
lg: 4,
xl: 5,
"2xl": 6.1,
"3xl": 8,
}}
images={data.recents.nodes}
singleRow
/>
<div className="px-4 md:px-8 mt-4 pt-2 border-t">
<h3 id="all" className="mx-2 font-bold">All images</h3>
</div>
<div className="flex flex-col lg:flex-row lg:items-end justify-between px-4 md:px-8 sm:mx-auto"> <div className="flex flex-col lg:flex-row lg:items-end justify-between px-4 md:px-8 sm:mx-auto">
<KeywordsPicker <KeywordsPicker
keywords={[ keywords={[
@@ -218,17 +239,17 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
/> />
<div className="m-2 flex flex-row items-end"> <div className="m-2 flex flex-row items-end">
<div className="border border-black rounded mr-2"> <div className="border border-black rounded mr-2">
<Switch <Switch
isSelected={showPalette} isSelected={showPalette}
onChange={(val) => setShowPalette(val)} onChange={(val) => setShowPalette(val)}
> >
<ColorPalette <ColorPalette
UNSAFE_style={{ UNSAFE_style={{
width: "24px", width: "24px",
margin: "0 4px", margin: "0 4px",
}} }}
/> />
</Switch> </Switch>
</div> </div>
<Select <Select
label="Sort by..." label="Sort by..."
@@ -237,7 +258,8 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
selectedKey={sortKey} selectedKey={sortKey}
> >
<Item key="rating">Curated</Item> <Item key="rating">Curated</Item>
<Item key="date">Date</Item> <Item key="modified">Date Updated</Item>
<Item key="date">Date taken</Item>
<Item key="hue">Hue</Item> <Item key="hue">Hue</Item>
</Select> </Select>
</div> </div>
@@ -268,37 +290,49 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
export const query = graphql` export const query = graphql`
query GalleryPageQuery { query GalleryPageQuery {
allFile( recents: allFile(
filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: { imageMeta: { meta: { ModifyDate: DESC } } } }
limit: 7
) {
...GalleryImageFile
}
all: allFile(
filter: { sourceInstanceName: { eq: "gallery" } } filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: { imageMeta: { dateTaken: DESC } } } sort: { fields: { imageMeta: { dateTaken: DESC } } }
) { ) {
nodes { ...GalleryImageFile
relativePath }
base }
childImageSharp {
fluid { fragment GalleryImageFile on FileConnection {
aspectRatio nodes {
} relativePath
gatsbyImageData( base
layout: CONSTRAINED childImageSharp {
height: 550 fluid {
placeholder: DOMINANT_COLOR aspectRatio
)
} }
fields { gatsbyImageData(
imageMeta { layout: CONSTRAINED
vibrantHue height: 550
dominantHue placeholder: DOMINANT_COLOR
dateTaken )
meta { }
Keywords fields {
Rating imageMeta {
ObjectName vibrantHue
} dominantHue
vibrant { dateTaken
# Vibrant meta {
...VibrantColors Keywords
} Rating
ObjectName
CreateDate
ModifyDate
}
vibrant {
...VibrantColors
} }
} }
} }

View File

@@ -55,6 +55,9 @@ const gcd = (a: number, b: number): number => {
}; };
export const getShutterFractionFromExposureTime = (exposureTime: number) => { export const getShutterFractionFromExposureTime = (exposureTime: number) => {
if (exposureTime === 0.3333333333333333) {
return "1/3";
}
if (exposureTime === 0.03333333333333333) { if (exposureTime === 0.03333333333333333) {
return "1/30"; return "1/30";
} }