Compare commits
6 Commits
main
...
recent-images
| Author | SHA1 | Date | |
|---|---|---|---|
| 534ee35451 | |||
| fa50031f0a | |||
| 1f9fa7b204 | |||
| 496185cc88 | |||
| e046649be1 | |||
| 6214944649 |
@@ -0,0 +1,2 @@
|
||||
[lfs]
|
||||
url = "https://git.chuckdries.com/chuckdries/Personal-Website.git/info/lfs/"
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+15
-2
@@ -10,6 +10,10 @@ import sharp from "sharp";
|
||||
import { Palette } from "node-vibrant/lib/color";
|
||||
import { performance } from "perf_hooks";
|
||||
|
||||
import util from 'node:util';
|
||||
import { exec as _exec } from "child_process";
|
||||
const exec = util.promisify(_exec)
|
||||
|
||||
// const path = require("path");
|
||||
// const Vibrant = require("node-vibrant");
|
||||
// const chroma = require("chroma-js");
|
||||
@@ -114,7 +118,8 @@ function transformMetaToNodeData(
|
||||
metaData: Record<string, unknown>,
|
||||
vibrantData: Palette,
|
||||
imagePath: string,
|
||||
{ r, g, b }: { r: number; b: number; g: number }
|
||||
{ r, g, b }: { r: number; b: number; g: number },
|
||||
datePublished: string,
|
||||
) {
|
||||
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
|
||||
const vibrantHue = vibrantData.Vibrant!.getHsl()[0] * 360;
|
||||
@@ -131,6 +136,7 @@ function transformMetaToNodeData(
|
||||
}
|
||||
return {
|
||||
dateTaken: metaData.DateTimeOriginal,
|
||||
datePublished,
|
||||
meta: {
|
||||
Make: metaData.Make,
|
||||
Model: metaData.Model,
|
||||
@@ -139,6 +145,7 @@ function transformMetaToNodeData(
|
||||
ISO: metaData.ISO,
|
||||
DateTimeOriginal: metaData.DateTimeOriginal,
|
||||
CreateDate: metaData.CreateDate,
|
||||
ModifyDate: metaData.ModifyDate,
|
||||
ShutterSpeedValue: metaData.ShutterSpeedValue,
|
||||
ApertureValue: metaData.ApertureValue,
|
||||
FocalLength: metaData.FocalLength,
|
||||
@@ -173,6 +180,11 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({
|
||||
const { createNodeField } = actions;
|
||||
|
||||
if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
|
||||
const { stdout: datePublished, stderr } = await exec(`git log --diff-filter=A --follow --format=%aI -1 -- ${node.absolutePath}`)
|
||||
if (stderr.length) {
|
||||
console.error('something went wrong checking publish date: ', stderr);
|
||||
}
|
||||
|
||||
const metaData = await exifr.parse(node.absolutePath as string, {
|
||||
iptc: true,
|
||||
xmp: true,
|
||||
@@ -200,7 +212,8 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({
|
||||
metaData,
|
||||
vibrantData,
|
||||
node.absolutePath as string,
|
||||
dominant
|
||||
dominant,
|
||||
datePublished
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
a = {
|
||||
ApplicationRecordVersion: '\x00\x04',
|
||||
DateCreated: '2015-10-18T17:59:38',
|
||||
TimeCreated: '175938',
|
||||
DigitalCreationDate: '20151018',
|
||||
DigitalCreationTime: '175938',
|
||||
format: 'image/jpeg',
|
||||
CreatorTool: 'Adobe Lightroom 5.4 (Macintosh)',
|
||||
ModifyDate: '2022-07-09T21:16:39.000Z',
|
||||
CreateDate: '2015-10-19T00:59:38.000Z',
|
||||
MetadataDate: '2022-07-09T14:16:39-07:00',
|
||||
Rating: 5,
|
||||
LensInfo: [ 50, 50, 1.8, 1.8 ],
|
||||
Lens: 'DT 50mm F1.8 SAM',
|
||||
DocumentID: 'xmp.did:27795bf1-8bb2-49a0-807a-cf6150bd505b',
|
||||
OriginalDocumentID: 'CE2B8F240393ADFDF9F092CF395C6632',
|
||||
InstanceID: 'xmp.iid:27795bf1-8bb2-49a0-807a-cf6150bd505b',
|
||||
History: [
|
||||
{ action: 'derived', parameters: 'saved to new location' },
|
||||
{
|
||||
action: 'saved',
|
||||
instanceID: 'xmp.iid:27795bf1-8bb2-49a0-807a-cf6150bd505b',
|
||||
when: '2022-07-09T14:16:39-07:00',
|
||||
softwareAgent: 'Adobe Lightroom 5.4 (Macintosh)',
|
||||
changed: '/'
|
||||
}
|
||||
],
|
||||
DerivedFrom: {
|
||||
documentID: 'CE2B8F240393ADFDF9F092CF395C6632',
|
||||
originalDocumentID: 'CE2B8F240393ADFDF9F092CF395C6632'
|
||||
},
|
||||
good: true,
|
||||
Version: 14.4,
|
||||
ProcessVersion: 6.7,
|
||||
WhiteBalance: 'Manual',
|
||||
IncrementalTemperature: -11,
|
||||
IncrementalTint: 23,
|
||||
Exposure2012: 0,
|
||||
Contrast2012: 6,
|
||||
Highlights2012: 0,
|
||||
Shadows2012: 0,
|
||||
Whites2012: 35,
|
||||
Blacks2012: -22,
|
||||
Texture: 0,
|
||||
Clarity2012: 0,
|
||||
Dehaze: 0,
|
||||
Vibrance: 0,
|
||||
Saturation: 'Normal',
|
||||
ParametricShadows: 0,
|
||||
ParametricDarks: 0,
|
||||
ParametricLights: 0,
|
||||
ParametricHighlights: 0,
|
||||
ParametricShadowSplit: 25,
|
||||
ParametricMidtoneSplit: 50,
|
||||
ParametricHighlightSplit: 75,
|
||||
Sharpness: 'Normal',
|
||||
LuminanceSmoothing: 0,
|
||||
ColorNoiseReduction: 0,
|
||||
HueAdjustmentRed: 0,
|
||||
HueAdjustmentOrange: 0,
|
||||
HueAdjustmentYellow: 0,
|
||||
HueAdjustmentGreen: 0,
|
||||
HueAdjustmentAqua: 0,
|
||||
HueAdjustmentBlue: 0,
|
||||
HueAdjustmentPurple: 0,
|
||||
HueAdjustmentMagenta: 0,
|
||||
SaturationAdjustmentRed: 0,
|
||||
SaturationAdjustmentOrange: 0,
|
||||
SaturationAdjustmentYellow: 0,
|
||||
SaturationAdjustmentGreen: 0,
|
||||
SaturationAdjustmentAqua: 0,
|
||||
SaturationAdjustmentBlue: 0,
|
||||
SaturationAdjustmentPurple: 0,
|
||||
SaturationAdjustmentMagenta: 0,
|
||||
LuminanceAdjustmentRed: 0,
|
||||
LuminanceAdjustmentOrange: 0,
|
||||
LuminanceAdjustmentYellow: 0,
|
||||
LuminanceAdjustmentGreen: 0,
|
||||
LuminanceAdjustmentAqua: 0,
|
||||
LuminanceAdjustmentBlue: 0,
|
||||
LuminanceAdjustmentPurple: 0,
|
||||
LuminanceAdjustmentMagenta: 0,
|
||||
SplitToningShadowHue: 0,
|
||||
SplitToningShadowSaturation: 0,
|
||||
SplitToningHighlightHue: 0,
|
||||
SplitToningHighlightSaturation: 0,
|
||||
SplitToningBalance: 0,
|
||||
ColorGradeMidtoneHue: 0,
|
||||
ColorGradeMidtoneSat: 0,
|
||||
ColorGradeShadowLum: 0,
|
||||
ColorGradeMidtoneLum: 0,
|
||||
ColorGradeHighlightLum: 0,
|
||||
ColorGradeBlending: 50,
|
||||
ColorGradeGlobalHue: 0,
|
||||
ColorGradeGlobalSat: 0,
|
||||
ColorGradeGlobalLum: 0,
|
||||
AutoLateralCA: 0,
|
||||
LensProfileEnable: 0,
|
||||
LensManualDistortionAmount: 0,
|
||||
VignetteAmount: 0,
|
||||
DefringePurpleAmount: 0,
|
||||
DefringePurpleHueLo: 30,
|
||||
DefringePurpleHueHi: 70,
|
||||
DefringeGreenAmount: 0,
|
||||
DefringeGreenHueLo: 40,
|
||||
DefringeGreenHueHi: 60,
|
||||
PerspectiveUpright: 0,
|
||||
PerspectiveVertical: 0,
|
||||
PerspectiveHorizontal: 0,
|
||||
PerspectiveRotate: 0,
|
||||
PerspectiveAspect: 0,
|
||||
PerspectiveScale: 100,
|
||||
PerspectiveX: 0,
|
||||
PerspectiveY: 0,
|
||||
GrainAmount: 0,
|
||||
PostCropVignetteAmount: 0,
|
||||
ShadowTint: 0,
|
||||
RedHue: 0,
|
||||
RedSaturation: 0,
|
||||
GreenHue: 0,
|
||||
GreenSaturation: 0,
|
||||
BlueHue: 0,
|
||||
BlueSaturation: 0,
|
||||
ConvertToGrayscale: false,
|
||||
OverrideLookVignette: false,
|
||||
ToneCurveName2012: 'Linear',
|
||||
CameraProfile: 'Embedded',
|
||||
CameraProfileDigest: '54650A341B5B5CCAE8442D0B43A92BCE',
|
||||
HasSettings: true,
|
||||
CropTop: 0,
|
||||
CropLeft: 0,
|
||||
CropBottom: 1,
|
||||
CropRight: 1,
|
||||
CropAngle: 0,
|
||||
CropConstrainToWarp: 0,
|
||||
HasCrop: false,
|
||||
AlreadyApplied: true,
|
||||
ToneCurvePV2012: [ '0, 0', '255, 255' ],
|
||||
ToneCurvePV2012Red: [ '0, 0', '255, 255' ],
|
||||
ToneCurvePV2012Green: [ '0, 0', '255, 255' ],
|
||||
ToneCurvePV2012Blue: [ '0, 0', '255, 255' ],
|
||||
Make: 'SONY',
|
||||
Model: 'SLT-A55V',
|
||||
XResolution: 240,
|
||||
YResolution: 240,
|
||||
ResolutionUnit: 'inches',
|
||||
Software: 'Adobe Lightroom 5.4 (Macintosh)',
|
||||
ExposureTime: 0.01,
|
||||
FNumber: 1.8,
|
||||
ExposureProgram: 'Manual',
|
||||
ISO: 200,
|
||||
SensitivityType: 2,
|
||||
RecommendedExposureIndex: 200,
|
||||
ExifVersion: '2.3.1',
|
||||
DateTimeOriginal: '2015-10-19T00:59:38.000Z',
|
||||
OffsetTime: '-07:00',
|
||||
ShutterSpeedValue: 6.643856,
|
||||
ApertureValue: 1.695994,
|
||||
BrightnessValue: 3,
|
||||
ExposureCompensation: 0,
|
||||
MaxApertureValue: 1.7,
|
||||
MeteringMode: 'Pattern',
|
||||
LightSource: 'Cloudy weather',
|
||||
Flash: 'Flash did not fire, compulsory flash mode',
|
||||
FocalLength: 50,
|
||||
ColorSpace: 1,
|
||||
FileSource: 'Digital Camera',
|
||||
SceneType: 'Directly photographed',
|
||||
CustomRendered: 'Normal',
|
||||
ExposureMode: 'Manual',
|
||||
FocalLengthIn35mmFormat: 75,
|
||||
SceneCaptureType: 'Standard',
|
||||
Contrast: 'Normal',
|
||||
LensModel: 'DT 50mm F1.8 SAM',
|
||||
GPSVersionID: '2.2.0.0',
|
||||
GPSLatitudeRef: 'N',
|
||||
GPSLatitude: [ 33, 25.7121, 0 ],
|
||||
GPSLongitudeRef: 'W',
|
||||
GPSLongitude: [ 111, 55.57122, 0 ],
|
||||
GPSAltitude: 354.7,
|
||||
GPSTimeStamp: '19:2:7',
|
||||
GPSStatus: 'V',
|
||||
GPSMeasureMode: '3',
|
||||
GPSSpeedRef: 'K',
|
||||
GPSSpeed: 33.8,
|
||||
GPSTrackRef: 'T',
|
||||
GPSTrack: 0.36,
|
||||
GPSMapDatum: 'WGS-84',
|
||||
GPSDateStamp: '2015:10:09',
|
||||
GPSDifferential: 0,
|
||||
latitude: 33.428535,
|
||||
longitude: -111.926187
|
||||
}
|
||||
@@ -91,7 +91,7 @@ function Option({ item, state }: OptionProps) {
|
||||
return (
|
||||
<li
|
||||
{...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" : ""
|
||||
} ${isSelected ? "font-bold" : ""}`}
|
||||
ref={ref}
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Row {
|
||||
}
|
||||
|
||||
interface MasonryGalleryProps {
|
||||
images: GalleryImage[];
|
||||
images: readonly GalleryImage[];
|
||||
aspectsByBreakpoint: {
|
||||
[breakpoint: string]: number;
|
||||
};
|
||||
@@ -26,15 +26,17 @@ interface MasonryGalleryProps {
|
||||
debugRating?: boolean;
|
||||
linkState?: object;
|
||||
showPalette?: boolean;
|
||||
singleRow?: boolean;
|
||||
}
|
||||
|
||||
const MasonryGallery = ({
|
||||
images,
|
||||
images: _images,
|
||||
aspectsByBreakpoint: aspectTargetsByBreakpoint,
|
||||
debugHue,
|
||||
debugRating,
|
||||
linkState,
|
||||
showPalette,
|
||||
singleRow,
|
||||
}: MasonryGalleryProps) => {
|
||||
const [isClient, setIsClient] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
@@ -57,8 +59,8 @@ const MasonryGallery = ({
|
||||
}px)`;
|
||||
|
||||
const aspectRatios = React.useMemo(
|
||||
() => R.map(getAspectRatio, images).filter(Boolean),
|
||||
[images]
|
||||
() => R.map(getAspectRatio, _images).filter(Boolean),
|
||||
[_images]
|
||||
) as number[];
|
||||
|
||||
const targetAspect = aspectTargetsByBreakpoint[breakpoint];
|
||||
@@ -83,6 +85,11 @@ const MasonryGallery = ({
|
||||
},
|
||||
];
|
||||
}
|
||||
// no-op instead of starting a new row
|
||||
if (singleRow) {
|
||||
return [currentRow];
|
||||
}
|
||||
// start a new row
|
||||
return [
|
||||
...acc,
|
||||
currentRow,
|
||||
@@ -97,14 +104,16 @@ const MasonryGallery = ({
|
||||
),
|
||||
R.indexBy(R.prop("startIndex"))
|
||||
)(aspectRatios),
|
||||
[aspectRatios, targetAspect]
|
||||
[aspectRatios, targetAspect, singleRow]
|
||||
);
|
||||
|
||||
const sortedImageList = React.useMemo(
|
||||
() => images.map((image) => image.base),
|
||||
[images]
|
||||
() => _images.map((image) => image.base),
|
||||
[_images]
|
||||
);
|
||||
|
||||
const images = singleRow ? _images.slice(0, rows[0].images) : _images;
|
||||
|
||||
let cursor = 0;
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -49,7 +49,7 @@ export function Select<T extends object>(props: AriaSelectProps<T>) {
|
||||
<button
|
||||
{...mergeProps(buttonProps, focusProps)}
|
||||
className={`py-[5px] px-3 w-[150px] flex flex-row items-center justify-between overflow-hidden cursor-default rounded border hover:bg-transparentblack ${
|
||||
isFocusVisible ? "border-green-500" : "border-black"
|
||||
isFocusVisible ? "border-green-700" : "border-black"
|
||||
} ${state.isOpen ? "bg-gray-100" : "bg-white"}`}
|
||||
ref={ref}
|
||||
>
|
||||
|
||||
Vendored
+19
-1
@@ -571,6 +571,7 @@ type FileFieldsFilterInput = {
|
||||
};
|
||||
|
||||
type FileFieldsImageMeta = {
|
||||
readonly datePublished: Maybe<Scalars['String']>;
|
||||
readonly dateTaken: Maybe<Scalars['Date']>;
|
||||
readonly dominantHue: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
|
||||
readonly meta: Maybe<FileFieldsImageMetaMeta>;
|
||||
@@ -587,6 +588,7 @@ type FileFieldsImageMeta_dateTakenArgs = {
|
||||
};
|
||||
|
||||
type FileFieldsImageMetaFieldSelector = {
|
||||
readonly datePublished: InputMaybe<FieldSelectorEnum>;
|
||||
readonly dateTaken: InputMaybe<FieldSelectorEnum>;
|
||||
readonly dominantHue: InputMaybe<FieldSelectorEnum>;
|
||||
readonly meta: InputMaybe<FileFieldsImageMetaMetaFieldSelector>;
|
||||
@@ -595,6 +597,7 @@ type FileFieldsImageMetaFieldSelector = {
|
||||
};
|
||||
|
||||
type FileFieldsImageMetaFilterInput = {
|
||||
readonly datePublished: InputMaybe<StringQueryOperatorInput>;
|
||||
readonly dateTaken: InputMaybe<DateQueryOperatorInput>;
|
||||
readonly dominantHue: InputMaybe<FloatQueryOperatorInput>;
|
||||
readonly meta: InputMaybe<FileFieldsImageMetaMetaFilterInput>;
|
||||
@@ -617,6 +620,7 @@ type FileFieldsImageMetaMeta = {
|
||||
readonly Location: Maybe<Scalars['String']>;
|
||||
readonly Make: Maybe<Scalars['String']>;
|
||||
readonly Model: Maybe<Scalars['String']>;
|
||||
readonly ModifyDate: Maybe<Scalars['Date']>;
|
||||
readonly ObjectName: Maybe<Scalars['String']>;
|
||||
readonly Rating: Maybe<Scalars['Int']>;
|
||||
readonly ShutterSpeedValue: Maybe<Scalars['Float']>;
|
||||
@@ -639,6 +643,14 @@ type FileFieldsImageMetaMeta_DateTimeOriginalArgs = {
|
||||
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 = {
|
||||
readonly ApertureValue: InputMaybe<FieldSelectorEnum>;
|
||||
readonly Caption: InputMaybe<FieldSelectorEnum>;
|
||||
@@ -654,6 +666,7 @@ type FileFieldsImageMetaMetaFieldSelector = {
|
||||
readonly Location: InputMaybe<FieldSelectorEnum>;
|
||||
readonly Make: InputMaybe<FieldSelectorEnum>;
|
||||
readonly Model: InputMaybe<FieldSelectorEnum>;
|
||||
readonly ModifyDate: InputMaybe<FieldSelectorEnum>;
|
||||
readonly ObjectName: InputMaybe<FieldSelectorEnum>;
|
||||
readonly Rating: InputMaybe<FieldSelectorEnum>;
|
||||
readonly ShutterSpeedValue: InputMaybe<FieldSelectorEnum>;
|
||||
@@ -675,6 +688,7 @@ type FileFieldsImageMetaMetaFilterInput = {
|
||||
readonly Location: InputMaybe<StringQueryOperatorInput>;
|
||||
readonly Make: InputMaybe<StringQueryOperatorInput>;
|
||||
readonly Model: InputMaybe<StringQueryOperatorInput>;
|
||||
readonly ModifyDate: InputMaybe<DateQueryOperatorInput>;
|
||||
readonly ObjectName: InputMaybe<StringQueryOperatorInput>;
|
||||
readonly Rating: InputMaybe<IntQueryOperatorInput>;
|
||||
readonly ShutterSpeedValue: InputMaybe<FloatQueryOperatorInput>;
|
||||
@@ -696,6 +710,7 @@ type FileFieldsImageMetaMetaSortInput = {
|
||||
readonly Location: InputMaybe<SortOrderEnum>;
|
||||
readonly Make: InputMaybe<SortOrderEnum>;
|
||||
readonly Model: InputMaybe<SortOrderEnum>;
|
||||
readonly ModifyDate: InputMaybe<SortOrderEnum>;
|
||||
readonly ObjectName: InputMaybe<SortOrderEnum>;
|
||||
readonly Rating: InputMaybe<SortOrderEnum>;
|
||||
readonly ShutterSpeedValue: InputMaybe<SortOrderEnum>;
|
||||
@@ -703,6 +718,7 @@ type FileFieldsImageMetaMetaSortInput = {
|
||||
};
|
||||
|
||||
type FileFieldsImageMetaSortInput = {
|
||||
readonly datePublished: InputMaybe<SortOrderEnum>;
|
||||
readonly dateTaken: InputMaybe<SortOrderEnum>;
|
||||
readonly dominantHue: InputMaybe<SortOrderEnum>;
|
||||
readonly meta: InputMaybe<FileFieldsImageMetaMetaSortInput>;
|
||||
@@ -2547,10 +2563,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 GalleryImageFileFragment = { readonly nodes: ReadonlyArray<{ 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 datePublished: 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 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 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 datePublished: 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 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 datePublished: 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 };
|
||||
|
||||
|
||||
+65
-19
@@ -7,6 +7,7 @@ import { Helmet } from "react-helmet";
|
||||
import MasonryGallery from "../components/MasonryGallery";
|
||||
import KeywordsPicker from "../components/KeywordsPicker";
|
||||
import {
|
||||
compareDates,
|
||||
getGalleryPageUrl,
|
||||
getHelmetSafeBodyStyle,
|
||||
getVibrantStyle,
|
||||
@@ -19,12 +20,24 @@ import ColorPalette from "@spectrum-icons/workflow/ColorPalette";
|
||||
const SORT_KEYS = {
|
||||
hue: ["fields", "imageMeta", "vibrantHue"],
|
||||
rating: ["fields", "imageMeta", "meta", "Rating"],
|
||||
hue_debug: ["fields", "imageMeta", "dominantHue", 0],
|
||||
date: [],
|
||||
// hue_debug: ["fields", "imageMeta", "dominantHue", 0],
|
||||
hue_debug: ["fields", "imageMeta", "dominantHue", "0"],
|
||||
date: ["fields", "imageMeta", "dateTaken"],
|
||||
modified: ["fields", "imageMeta", "datePublished"],
|
||||
} as const;
|
||||
|
||||
export type GalleryImage =
|
||||
Queries.GalleryPageQueryQuery["allFile"]["nodes"][number];
|
||||
Queries.GalleryPageQueryQuery["all"]["nodes"][number];
|
||||
|
||||
function smartCompareDates(key: keyof typeof SORT_KEYS, left: GalleryImage, right: GalleryImage) {
|
||||
let diff = compareDates(SORT_KEYS[key], left, right);
|
||||
console.log("🚀 ~ file: photogallery.tsx:34 ~ smartCompareDates ~ diff:", diff)
|
||||
if (diff !== 0) {
|
||||
return diff;
|
||||
}
|
||||
console.log('falling back to date')
|
||||
return compareDates(SORT_KEYS.date, left, right);
|
||||
}
|
||||
|
||||
const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
const hash =
|
||||
@@ -128,16 +141,8 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
|
||||
const images: GalleryImage[] = React.useMemo(() => {
|
||||
const sort =
|
||||
sortKey === "date"
|
||||
? R.sort((node1: typeof data["allFile"]["nodes"][number], node2) => {
|
||||
const date1 = new Date(
|
||||
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node1)
|
||||
);
|
||||
const date2 = new Date(
|
||||
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node2)
|
||||
);
|
||||
return -1 * (date1.getTime() - date2.getTime());
|
||||
})
|
||||
sortKey === "date" || sortKey === "modified"
|
||||
? R.sort((node1: typeof data["all"]["nodes"][number], node2) => smartCompareDates(sortKey, node1, node2))
|
||||
: R.sort(
|
||||
// @ts-ignore
|
||||
R.descend(R.path<GalleryImage>(SORT_KEYS[sortKey]))
|
||||
@@ -157,7 +162,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
// @ts-ignore
|
||||
sort,
|
||||
filter
|
||||
)(data.allFile.nodes) as any;
|
||||
)(data.all.nodes) as any;
|
||||
return ret;
|
||||
} catch (e) {
|
||||
console.log("caught images!", e);
|
||||
@@ -165,6 +170,10 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
}
|
||||
}, [data, sortKey, filterKeyword]);
|
||||
|
||||
const recents = React.useMemo(() => {
|
||||
return R.sort((left, right) => smartCompareDates('modified', left, right), data.recents.nodes)
|
||||
}, [data, 'hi'])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
@@ -174,6 +183,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
className="bg-white transition-color"
|
||||
// @ts-ignore
|
||||
style={getHelmetSafeBodyStyle(
|
||||
// @ts-ignore shrug
|
||||
getVibrantStyle({
|
||||
Muted: [0, 0, 0],
|
||||
LightMuted: [0, 0, 0],
|
||||
@@ -195,6 +205,29 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="px-4 md:px-8">
|
||||
<h3 id="recently" className="mx-2 font-bold">
|
||||
Recently published
|
||||
</h3>
|
||||
</div>
|
||||
<MasonryGallery
|
||||
aspectsByBreakpoint={{
|
||||
xs: 2,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 4,
|
||||
xl: 5,
|
||||
"2xl": 6.1,
|
||||
"3xl": 8,
|
||||
}}
|
||||
images={recents}
|
||||
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">
|
||||
<KeywordsPicker
|
||||
keywords={[
|
||||
@@ -237,7 +270,8 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
selectedKey={sortKey}
|
||||
>
|
||||
<Item key="rating">Curated</Item>
|
||||
<Item key="date">Date</Item>
|
||||
<Item key="modified">Date published</Item>
|
||||
<Item key="date">Date taken</Item>
|
||||
<Item key="hue">Hue</Item>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -268,12 +302,23 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
|
||||
|
||||
export const query = graphql`
|
||||
query GalleryPageQuery {
|
||||
allFile(
|
||||
recents: allFile(
|
||||
filter: { sourceInstanceName: { eq: "gallery" } }
|
||||
sort: { fields: { imageMeta: { datePublished: DESC } } }
|
||||
limit: 7
|
||||
) {
|
||||
...GalleryImageFile
|
||||
}
|
||||
all: allFile(
|
||||
filter: { sourceInstanceName: { eq: "gallery" } }
|
||||
sort: { fields: { imageMeta: { dateTaken: DESC } } }
|
||||
) {
|
||||
...GalleryImageFile
|
||||
}
|
||||
}
|
||||
|
||||
fragment GalleryImageFile on FileConnection {
|
||||
nodes {
|
||||
relativePath
|
||||
base
|
||||
childImageSharp {
|
||||
fluid {
|
||||
@@ -290,20 +335,21 @@ export const query = graphql`
|
||||
vibrantHue
|
||||
dominantHue
|
||||
dateTaken
|
||||
datePublished
|
||||
meta {
|
||||
Keywords
|
||||
Rating
|
||||
ObjectName
|
||||
CreateDate
|
||||
ModifyDate
|
||||
}
|
||||
vibrant {
|
||||
# Vibrant
|
||||
...VibrantColors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default GalleryPage;
|
||||
|
||||
+37
-9
@@ -1,18 +1,23 @@
|
||||
import React from "react";
|
||||
|
||||
import { pathOr } from "ramda";
|
||||
// import kebabCase from 'lodash/kebabCase';
|
||||
|
||||
import React from "react";
|
||||
import { HomepageImage } from "./pages";
|
||||
import { GalleryImage } from "./pages/photogallery";
|
||||
|
||||
export const getMeta = <T extends GalleryImage | HomepageImage>(image: T) => image.fields?.imageMeta;
|
||||
export const getMeta = <T extends GalleryImage | HomepageImage>(image: T) =>
|
||||
image.fields?.imageMeta;
|
||||
|
||||
export const getName = (image: GalleryImage) =>
|
||||
image.fields?.imageMeta?.meta?.ObjectName || image.base;
|
||||
image.fields?.imageMeta?.meta?.ObjectName || image.base;
|
||||
|
||||
// some pleasing default colors for SSR and initial hydration
|
||||
export const getVibrant = (image: GalleryImage | HomepageImage) => getMeta(image)?.vibrant;
|
||||
export const getVibrant = (image: GalleryImage | HomepageImage) =>
|
||||
getMeta(image)?.vibrant;
|
||||
|
||||
export const hasName = (image: GalleryImage) => Boolean(image.fields?.imageMeta?.meta?.ObjectName);
|
||||
export const hasName = (image: GalleryImage) =>
|
||||
Boolean(image.fields?.imageMeta?.meta?.ObjectName);
|
||||
|
||||
export const getAspectRatio = (image: GalleryImage | HomepageImage): number =>
|
||||
image.childImageSharp?.fluid?.aspectRatio ?? 1;
|
||||
@@ -25,7 +30,10 @@ export const getCanonicalSize = (image: GalleryImage) => ({
|
||||
export const getRgba = (palette: string[], alpha: number) =>
|
||||
`rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`;
|
||||
|
||||
export const getVibrantStyle = (vibrant: Queries.FileFieldsImageMetaVibrant, screenHeight?: number) => ({
|
||||
export const getVibrantStyle = (
|
||||
vibrant: Queries.FileFieldsImageMetaVibrant,
|
||||
screenHeight?: number
|
||||
) => ({
|
||||
"--muted": vibrant.Muted,
|
||||
"--dark-muted": vibrant.DarkMuted,
|
||||
"--light-muted": vibrant.LightMuted,
|
||||
@@ -40,10 +48,12 @@ export const getHelmetSafeBodyStyle = (style: React.CSSProperties) => {
|
||||
if (typeof window === "undefined") {
|
||||
return style;
|
||||
}
|
||||
return Object.keys(style)
|
||||
return (
|
||||
Object.keys(style)
|
||||
// @ts-ignore
|
||||
.map((key) => `${key}: ${style[key]};`)
|
||||
.join("");
|
||||
.join("")
|
||||
);
|
||||
};
|
||||
|
||||
const gcd = (a: number, b: number): number => {
|
||||
@@ -55,6 +65,9 @@ const gcd = (a: number, b: number): number => {
|
||||
};
|
||||
|
||||
export const getShutterFractionFromExposureTime = (exposureTime: number) => {
|
||||
if (exposureTime === 0.3333333333333333) {
|
||||
return "1/3";
|
||||
}
|
||||
if (exposureTime === 0.03333333333333333) {
|
||||
return "1/30";
|
||||
}
|
||||
@@ -91,7 +104,10 @@ interface galleryPageUrlProps {
|
||||
sortKey: string;
|
||||
}
|
||||
|
||||
export const getGalleryPageUrl = ({ keyword, sortKey }: galleryPageUrlProps, hash: string) => {
|
||||
export const getGalleryPageUrl = (
|
||||
{ keyword, sortKey }: galleryPageUrlProps,
|
||||
hash: string
|
||||
) => {
|
||||
const url = new URL(
|
||||
`${
|
||||
typeof window !== "undefined"
|
||||
@@ -118,3 +134,15 @@ export const getGalleryPageUrl = ({ keyword, sortKey }: galleryPageUrlProps, has
|
||||
}
|
||||
return url.href.toString().replace(url.origin, "");
|
||||
};
|
||||
|
||||
export function compareDates<T>(
|
||||
date_path: readonly string[],
|
||||
left: T,
|
||||
right: T
|
||||
): number {
|
||||
// why tf do my dates have newlines in them?!?!
|
||||
const date1 = new Date(pathOr("", date_path, left).replace(/\s/g, ''));
|
||||
const date2 = new Date(pathOr("", date_path, right).replace(/\s/g, ''));
|
||||
const diff = -1 * (date1.getTime() - date2.getTime());
|
||||
return diff;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user