WIP working on sorting recently published by capture date when published in batches
This commit is contained in:
		| @@ -10,6 +10,10 @@ import sharp from "sharp"; | |||||||
| import { Palette } from "node-vibrant/lib/color"; | import { Palette } from "node-vibrant/lib/color"; | ||||||
| import { performance } from "perf_hooks"; | 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 path = require("path"); | ||||||
| // const Vibrant = require("node-vibrant"); | // const Vibrant = require("node-vibrant"); | ||||||
| // const chroma = require("chroma-js"); | // const chroma = require("chroma-js"); | ||||||
| @@ -114,7 +118,8 @@ function transformMetaToNodeData( | |||||||
|   metaData: Record<string, unknown>, |   metaData: Record<string, unknown>, | ||||||
|   vibrantData: Palette, |   vibrantData: Palette, | ||||||
|   imagePath: string, |   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 vibrant = vibrantData ? processColors(vibrantData, imagePath) : null; | ||||||
|   const vibrantHue = vibrantData.Vibrant!.getHsl()[0] * 360; |   const vibrantHue = vibrantData.Vibrant!.getHsl()[0] * 360; | ||||||
| @@ -131,6 +136,7 @@ function transformMetaToNodeData( | |||||||
|   } |   } | ||||||
|   return { |   return { | ||||||
|     dateTaken: metaData.DateTimeOriginal, |     dateTaken: metaData.DateTimeOriginal, | ||||||
|  |     datePublished, | ||||||
|     meta: { |     meta: { | ||||||
|       Make: metaData.Make, |       Make: metaData.Make, | ||||||
|       Model: metaData.Model, |       Model: metaData.Model, | ||||||
| @@ -174,6 +180,11 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({ | |||||||
|   const { createNodeField } = actions; |   const { createNodeField } = actions; | ||||||
|  |  | ||||||
|   if (node.internal.type === "File" && node.sourceInstanceName === "gallery") { |   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, { |     const metaData = await exifr.parse(node.absolutePath as string, { | ||||||
|       iptc: true, |       iptc: true, | ||||||
|       xmp: true, |       xmp: true, | ||||||
| @@ -201,7 +212,8 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({ | |||||||
|         metaData, |         metaData, | ||||||
|         vibrantData, |         vibrantData, | ||||||
|         node.absolutePath as string, |         node.absolutePath as string, | ||||||
|         dominant |         dominant, | ||||||
|  |         datePublished | ||||||
|       ), |       ), | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										193
									
								
								scratchpad.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								scratchpad.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
| @@ -30,7 +30,7 @@ interface MasonryGalleryProps { | |||||||
| } | } | ||||||
|  |  | ||||||
| const MasonryGallery = ({ | const MasonryGallery = ({ | ||||||
|   images, |   images: _images, | ||||||
|   aspectsByBreakpoint: aspectTargetsByBreakpoint, |   aspectsByBreakpoint: aspectTargetsByBreakpoint, | ||||||
|   debugHue, |   debugHue, | ||||||
|   debugRating, |   debugRating, | ||||||
| @@ -59,8 +59,8 @@ const MasonryGallery = ({ | |||||||
|   }px)`; |   }px)`; | ||||||
|  |  | ||||||
|   const aspectRatios = React.useMemo( |   const aspectRatios = React.useMemo( | ||||||
|     () => R.map(getAspectRatio, images).filter(Boolean), |     () => R.map(getAspectRatio, _images).filter(Boolean), | ||||||
|     [images] |     [_images] | ||||||
|   ) as number[]; |   ) as number[]; | ||||||
|  |  | ||||||
|   const targetAspect = aspectTargetsByBreakpoint[breakpoint]; |   const targetAspect = aspectTargetsByBreakpoint[breakpoint]; | ||||||
| @@ -87,10 +87,7 @@ const MasonryGallery = ({ | |||||||
|             } |             } | ||||||
|             // no-op instead of starting a new row |             // no-op instead of starting a new row | ||||||
|             if (singleRow) { |             if (singleRow) { | ||||||
|               return [ |               return [currentRow]; | ||||||
|                 ...acc, |  | ||||||
|                 currentRow, |  | ||||||
|               ] |  | ||||||
|             } |             } | ||||||
|             // start a new row |             // start a new row | ||||||
|             return [ |             return [ | ||||||
| @@ -107,14 +104,16 @@ const MasonryGallery = ({ | |||||||
|         ), |         ), | ||||||
|         R.indexBy(R.prop("startIndex")) |         R.indexBy(R.prop("startIndex")) | ||||||
|       )(aspectRatios), |       )(aspectRatios), | ||||||
|     [aspectRatios, targetAspect] |     [aspectRatios, targetAspect, singleRow] | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const sortedImageList = React.useMemo( |   const sortedImageList = React.useMemo( | ||||||
|     () => images.map((image) => image.base), |     () => _images.map((image) => image.base), | ||||||
|     [images] |     [_images] | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   const images = singleRow ? _images.slice(0, rows[0].images) : _images; | ||||||
|  |   | ||||||
|   let cursor = 0; |   let cursor = 0; | ||||||
|   return ( |   return ( | ||||||
|     <div |     <div | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ export function Select<T extends object>(props: AriaSelectProps<T>) { | |||||||
|       <button |       <button | ||||||
|         {...mergeProps(buttonProps, focusProps)} |         {...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 ${ |         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"}`} |         } ${state.isOpen ? "bg-gray-100" : "bg-white"}`} | ||||||
|         ref={ref} |         ref={ref} | ||||||
|       > |       > | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								src/gatsby-types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								src/gatsby-types.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -571,6 +571,7 @@ type FileFieldsFilterInput = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| type FileFieldsImageMeta = { | type FileFieldsImageMeta = { | ||||||
|  |   readonly datePublished: Maybe<Scalars['String']>; | ||||||
|   readonly dateTaken: Maybe<Scalars['Date']>; |   readonly dateTaken: Maybe<Scalars['Date']>; | ||||||
|   readonly dominantHue: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>; |   readonly dominantHue: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>; | ||||||
|   readonly meta: Maybe<FileFieldsImageMetaMeta>; |   readonly meta: Maybe<FileFieldsImageMetaMeta>; | ||||||
| @@ -587,6 +588,7 @@ type FileFieldsImageMeta_dateTakenArgs = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| type FileFieldsImageMetaFieldSelector = { | type FileFieldsImageMetaFieldSelector = { | ||||||
|  |   readonly datePublished: InputMaybe<FieldSelectorEnum>; | ||||||
|   readonly dateTaken: InputMaybe<FieldSelectorEnum>; |   readonly dateTaken: InputMaybe<FieldSelectorEnum>; | ||||||
|   readonly dominantHue: InputMaybe<FieldSelectorEnum>; |   readonly dominantHue: InputMaybe<FieldSelectorEnum>; | ||||||
|   readonly meta: InputMaybe<FileFieldsImageMetaMetaFieldSelector>; |   readonly meta: InputMaybe<FileFieldsImageMetaMetaFieldSelector>; | ||||||
| @@ -595,6 +597,7 @@ type FileFieldsImageMetaFieldSelector = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| type FileFieldsImageMetaFilterInput = { | type FileFieldsImageMetaFilterInput = { | ||||||
|  |   readonly datePublished: InputMaybe<StringQueryOperatorInput>; | ||||||
|   readonly dateTaken: InputMaybe<DateQueryOperatorInput>; |   readonly dateTaken: InputMaybe<DateQueryOperatorInput>; | ||||||
|   readonly dominantHue: InputMaybe<FloatQueryOperatorInput>; |   readonly dominantHue: InputMaybe<FloatQueryOperatorInput>; | ||||||
|   readonly meta: InputMaybe<FileFieldsImageMetaMetaFilterInput>; |   readonly meta: InputMaybe<FileFieldsImageMetaMetaFilterInput>; | ||||||
| @@ -715,6 +718,7 @@ type FileFieldsImageMetaMetaSortInput = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| type FileFieldsImageMetaSortInput = { | type FileFieldsImageMetaSortInput = { | ||||||
|  |   readonly datePublished: InputMaybe<SortOrderEnum>; | ||||||
|   readonly dateTaken: InputMaybe<SortOrderEnum>; |   readonly dateTaken: InputMaybe<SortOrderEnum>; | ||||||
|   readonly dominantHue: InputMaybe<SortOrderEnum>; |   readonly dominantHue: InputMaybe<SortOrderEnum>; | ||||||
|   readonly meta: InputMaybe<FileFieldsImageMetaMetaSortInput>; |   readonly meta: InputMaybe<FileFieldsImageMetaMetaSortInput>; | ||||||
| @@ -2559,12 +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 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 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 GalleryPageQueryQueryVariables = Exact<{ [key: string]: never; }>; | ||||||
|  |  | ||||||
|  |  | ||||||
| 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 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 }; | type GatsbyImageSharpFixedFragment = { readonly base64: string | null, readonly width: number, readonly height: number, readonly src: string, readonly srcSet: string }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import { Helmet } from "react-helmet"; | |||||||
| import MasonryGallery from "../components/MasonryGallery"; | import MasonryGallery from "../components/MasonryGallery"; | ||||||
| import KeywordsPicker from "../components/KeywordsPicker"; | import KeywordsPicker from "../components/KeywordsPicker"; | ||||||
| import { | import { | ||||||
|  |   compareDates, | ||||||
|   getGalleryPageUrl, |   getGalleryPageUrl, | ||||||
|   getHelmetSafeBodyStyle, |   getHelmetSafeBodyStyle, | ||||||
|   getVibrantStyle, |   getVibrantStyle, | ||||||
| @@ -19,14 +20,25 @@ import ColorPalette from "@spectrum-icons/workflow/ColorPalette"; | |||||||
| const SORT_KEYS = { | 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], | ||||||
|  |   hue_debug: ["fields", "imageMeta", "dominantHue", "0"], | ||||||
|   date: ["fields", "imageMeta", "dateTaken"], |   date: ["fields", "imageMeta", "dateTaken"], | ||||||
|   modified: ["fields", "imageMeta", "meta", "ModifyDate"] |   modified: ["fields", "imageMeta", "datePublished"], | ||||||
| } as const; | } as const; | ||||||
|  |  | ||||||
| export type GalleryImage = | export type GalleryImage = | ||||||
|   Queries.GalleryPageQueryQuery["all"]["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 GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | ||||||
|   const hash = |   const hash = | ||||||
|     typeof window !== "undefined" ? window.location.hash.replace("#", "") : ""; |     typeof window !== "undefined" ? window.location.hash.replace("#", "") : ""; | ||||||
| @@ -130,15 +142,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|   const images: GalleryImage[] = React.useMemo(() => { |   const images: GalleryImage[] = React.useMemo(() => { | ||||||
|     const sort = |     const sort = | ||||||
|       sortKey === "date" || sortKey === "modified" |       sortKey === "date" || sortKey === "modified" | ||||||
|         ? R.sort((node1: typeof data["all"]["nodes"][number], node2) => { |         ? R.sort((node1: typeof data["all"]["nodes"][number], node2) => smartCompareDates(sortKey, node1, node2)) | ||||||
|             const date1 = new Date( |  | ||||||
|               R.pathOr("", SORT_KEYS[sortKey], node1) |  | ||||||
|             ); |  | ||||||
|             const date2 = new Date( |  | ||||||
|               R.pathOr("", SORT_KEYS[sortKey], node2) |  | ||||||
|             ); |  | ||||||
|             return -1 * (date1.getTime() - date2.getTime()); |  | ||||||
|           }) |  | ||||||
|         : R.sort( |         : R.sort( | ||||||
|             // @ts-ignore |             // @ts-ignore | ||||||
|             R.descend(R.path<GalleryImage>(SORT_KEYS[sortKey])) |             R.descend(R.path<GalleryImage>(SORT_KEYS[sortKey])) | ||||||
| @@ -166,6 +170,10 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|     } |     } | ||||||
|   }, [data, sortKey, filterKeyword]); |   }, [data, sortKey, filterKeyword]); | ||||||
|  |  | ||||||
|  |   const recents = React.useMemo(() => { | ||||||
|  |     return R.sort((left, right) => smartCompareDates('modified', left, right), data.recents.nodes) | ||||||
|  |   }, [data, 'hi']) | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       {/* @ts-ignore */} |       {/* @ts-ignore */} | ||||||
| @@ -198,7 +206,9 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|         <div className="px-4 md:px-8"> |         <div className="px-4 md:px-8"> | ||||||
|           <h3 id="recently" className="mx-2 font-bold">Recently updated</h3> |           <h3 id="recently" className="mx-2 font-bold"> | ||||||
|  |             Recently published | ||||||
|  |           </h3> | ||||||
|         </div> |         </div> | ||||||
|         <MasonryGallery |         <MasonryGallery | ||||||
|           aspectsByBreakpoint={{ |           aspectsByBreakpoint={{ | ||||||
| @@ -210,11 +220,13 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|             "2xl": 6.1, |             "2xl": 6.1, | ||||||
|             "3xl": 8, |             "3xl": 8, | ||||||
|           }} |           }} | ||||||
|           images={data.recents.nodes} |           images={recents} | ||||||
|           singleRow |           singleRow | ||||||
|         /> |         /> | ||||||
|         <div className="px-4 md:px-8 mt-4 pt-2 border-t"> |         <div className="px-4 md:px-8 mt-4 pt-2 border-t"> | ||||||
|           <h3 id="all" className="mx-2 font-bold">All images</h3> |           <h3 id="all" className="mx-2 font-bold"> | ||||||
|  |             All images | ||||||
|  |           </h3> | ||||||
|         </div> |         </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 | ||||||
| @@ -258,14 +270,14 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|               selectedKey={sortKey} |               selectedKey={sortKey} | ||||||
|             > |             > | ||||||
|               <Item key="rating">Curated</Item> |               <Item key="rating">Curated</Item> | ||||||
|               <Item key="modified">Date Updated</Item> |               <Item key="modified">Date published</Item> | ||||||
|               <Item key="date">Date taken</Item> |               <Item key="date">Date taken</Item> | ||||||
|               <Item key="hue">Hue</Item> |               <Item key="hue">Hue</Item> | ||||||
|             </Select> |             </Select> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <MasonryGallery |       {/* <MasonryGallery | ||||||
|         aspectsByBreakpoint={{ |         aspectsByBreakpoint={{ | ||||||
|           xs: 2, |           xs: 2, | ||||||
|           sm: 2, |           sm: 2, | ||||||
| @@ -283,7 +295,7 @@ const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => { | |||||||
|           filterKeyword, |           filterKeyword, | ||||||
|         }} |         }} | ||||||
|         showPalette={showPalette} |         showPalette={showPalette} | ||||||
|       /> |       /> */} | ||||||
|     </> |     </> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| @@ -292,7 +304,7 @@ export const query = graphql` | |||||||
|   query GalleryPageQuery { |   query GalleryPageQuery { | ||||||
|     recents: allFile( |     recents: allFile( | ||||||
|       filter: { sourceInstanceName: { eq: "gallery" } } |       filter: { sourceInstanceName: { eq: "gallery" } } | ||||||
|       sort: { fields: { imageMeta: { meta: { ModifyDate: DESC } } } } |       sort: { fields: { imageMeta: { datePublished: DESC } } } | ||||||
|       limit: 7 |       limit: 7 | ||||||
|     ) { |     ) { | ||||||
|       ...GalleryImageFile |       ...GalleryImageFile | ||||||
| @@ -307,7 +319,6 @@ export const query = graphql` | |||||||
|  |  | ||||||
|   fragment GalleryImageFile on FileConnection { |   fragment GalleryImageFile on FileConnection { | ||||||
|     nodes { |     nodes { | ||||||
|       relativePath |  | ||||||
|       base |       base | ||||||
|       childImageSharp { |       childImageSharp { | ||||||
|         fluid { |         fluid { | ||||||
| @@ -324,6 +335,7 @@ export const query = graphql` | |||||||
|           vibrantHue |           vibrantHue | ||||||
|           dominantHue |           dominantHue | ||||||
|           dateTaken |           dateTaken | ||||||
|  |           datePublished | ||||||
|           meta { |           meta { | ||||||
|             Keywords |             Keywords | ||||||
|             Rating |             Rating | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								src/utils.ts
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								src/utils.ts
									
									
									
									
									
								
							| @@ -1,18 +1,23 @@ | |||||||
|  | import React from "react"; | ||||||
|  |  | ||||||
|  | import { pathOr } from "ramda"; | ||||||
| // import kebabCase from 'lodash/kebabCase'; | // import kebabCase from 'lodash/kebabCase'; | ||||||
|  |  | ||||||
| import React from "react"; |  | ||||||
| import { HomepageImage } from "./pages"; | import { HomepageImage } from "./pages"; | ||||||
| import { GalleryImage } from "./pages/photogallery"; | 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) => | 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 | // 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 => | export const getAspectRatio = (image: GalleryImage | HomepageImage): number => | ||||||
|   image.childImageSharp?.fluid?.aspectRatio ?? 1; |   image.childImageSharp?.fluid?.aspectRatio ?? 1; | ||||||
| @@ -25,7 +30,10 @@ export const getCanonicalSize = (image: GalleryImage) => ({ | |||||||
| export const getRgba = (palette: string[], alpha: number) => | export const getRgba = (palette: string[], alpha: number) => | ||||||
|   `rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`; |   `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, |   "--muted": vibrant.Muted, | ||||||
|   "--dark-muted": vibrant.DarkMuted, |   "--dark-muted": vibrant.DarkMuted, | ||||||
|   "--light-muted": vibrant.LightMuted, |   "--light-muted": vibrant.LightMuted, | ||||||
| @@ -40,10 +48,12 @@ export const getHelmetSafeBodyStyle = (style: React.CSSProperties) => { | |||||||
|   if (typeof window === "undefined") { |   if (typeof window === "undefined") { | ||||||
|     return style; |     return style; | ||||||
|   } |   } | ||||||
|   return Object.keys(style) |   return ( | ||||||
|   // @ts-ignore |     Object.keys(style) | ||||||
|     .map((key) => `${key}: ${style[key]};`) |       // @ts-ignore | ||||||
|     .join(""); |       .map((key) => `${key}: ${style[key]};`) | ||||||
|  |       .join("") | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const gcd = (a: number, b: number): number => { | const gcd = (a: number, b: number): number => { | ||||||
| @@ -94,7 +104,10 @@ interface galleryPageUrlProps { | |||||||
|   sortKey: string; |   sortKey: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| export const getGalleryPageUrl = ({ keyword, sortKey }: galleryPageUrlProps, hash: string) => { | export const getGalleryPageUrl = ( | ||||||
|  |   { keyword, sortKey }: galleryPageUrlProps, | ||||||
|  |   hash: string | ||||||
|  | ) => { | ||||||
|   const url = new URL( |   const url = new URL( | ||||||
|     `${ |     `${ | ||||||
|       typeof window !== "undefined" |       typeof window !== "undefined" | ||||||
| @@ -121,3 +134,18 @@ export const getGalleryPageUrl = ({ keyword, sortKey }: galleryPageUrlProps, has | |||||||
|   } |   } | ||||||
|   return url.href.toString().replace(url.origin, ""); |   return url.href.toString().replace(url.origin, ""); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | export function compareDates<T>( | ||||||
|  |   date_path: readonly string[], | ||||||
|  |   left: T, | ||||||
|  |   right: T | ||||||
|  | ): number { | ||||||
|  |   const d1 = pathOr("", date_path, left); | ||||||
|  |   console.log("🚀 ~ file: utils.ts:129 ~ d1:", d1); | ||||||
|  |   const date1 = new Date(d1); | ||||||
|  |   console.log("🚀 ~ file: utils.ts:146 ~ new Date(d1):", new Date(d1)) | ||||||
|  |   console.log("🚀 ~ file: utils.ts:133 ~ date1:", date1); | ||||||
|  |   const date2 = new Date(pathOr("", date_path, right)); | ||||||
|  |   const diff = -1 * (date1.getTime() - date2.getTime()); | ||||||
|  |   return diff; | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user