From 501c50e2598d90f601002165a587a2c6b9547834 Mon Sep 17 00:00:00 2001
From: James Cross <james@breakingwave.com>
Date: Mon, 28 Mar 2022 16:08:30 +0100
Subject: [PATCH] Adding image description to sidebar

---
 api/graphql/generated.go                      |  49 ++++++++++++++++++
 api/graphql/models/media_exif.go              |   1 +
 api/graphql/schema.graphql                    |   2 +
 api/scanner/exif/exif_parser_external.go      |   7 +++
 api/scanner/exif/exif_parser_internal.go      |   5 ++
 api/scanner/exif/exif_test.go                 |   1 +
 api/scanner/exif/test_data/bird.jpg           | Bin 37498 -> 37526 bytes
 ui/src/Pages/SharePage/AlbumSharePage.tsx     |   1 +
 ui/src/Pages/SharePage/SharePage.tsx          |   1 +
 .../SharePage/__generated__/SharePageToken.ts |   4 ++
 .../__generated__/shareAlbumQuery.ts          |   4 ++
 .../sidebar/MediaSidebar/MediaSidebar.tsx     |   1 +
 .../MediaSidebar/MediaSidebarExif.test.tsx    |   5 ++
 .../sidebar/MediaSidebar/MediaSidebarExif.tsx |   1 +
 .../__generated__/sidebarMediaQuery.ts        |   4 ++
 15 files changed, 86 insertions(+)

diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index 748d7fd..6a60718 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -130,6 +130,7 @@ type ComplexityRoot struct {
 		Camera          func(childComplexity int) int
 		Coordinates     func(childComplexity int) int
 		DateShot        func(childComplexity int) int
+		Description     func(childComplexity int) int
 		Exposure        func(childComplexity int) int
 		ExposureProgram func(childComplexity int) int
 		Flash           func(childComplexity int) int
@@ -739,6 +740,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 
 		return e.complexity.MediaEXIF.DateShot(childComplexity), true
 
+	case "MediaEXIF.description":
+		if e.complexity.MediaEXIF.Description == nil {
+			break
+		}
+
+		return e.complexity.MediaEXIF.Description(childComplexity), true
+
 	case "MediaEXIF.exposure":
 		if e.complexity.MediaEXIF.Exposure == nil {
 			break
@@ -2088,6 +2096,8 @@ type Media {
 type MediaEXIF {
   id: ID!
   media: Media!
+  "The description of the image"
+  description: String
   "The model name of the camera"
   camera: String
   "The maker of the camera"
@@ -4754,6 +4764,38 @@ func (ec *executionContext) _MediaEXIF_media(ctx context.Context, field graphql.
 	return ec.marshalNMedia2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMedia(ctx, field.Selections, res)
 }
 
+func (ec *executionContext) _MediaEXIF_description(ctx context.Context, field graphql.CollectedField, obj *models.MediaEXIF) (ret graphql.Marshaler) {
+	defer func() {
+		if r := recover(); r != nil {
+			ec.Error(ctx, ec.Recover(ctx, r))
+			ret = graphql.Null
+		}
+	}()
+	fc := &graphql.FieldContext{
+		Object:     "MediaEXIF",
+		Field:      field,
+		Args:       nil,
+		IsMethod:   false,
+		IsResolver: false,
+	}
+
+	ctx = graphql.WithFieldContext(ctx, fc)
+	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+		ctx = rctx // use context from middleware stack in children
+		return obj.Description, nil
+	})
+	if err != nil {
+		ec.Error(ctx, err)
+		return graphql.Null
+	}
+	if resTmp == nil {
+		return graphql.Null
+	}
+	res := resTmp.(*string)
+	fc.Result = res
+	return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
+}
+
 func (ec *executionContext) _MediaEXIF_camera(ctx context.Context, field graphql.CollectedField, obj *models.MediaEXIF) (ret graphql.Marshaler) {
 	defer func() {
 		if r := recover(); r != nil {
@@ -11377,6 +11419,13 @@ func (ec *executionContext) _MediaEXIF(ctx context.Context, sel ast.SelectionSet
 			if out.Values[i] == graphql.Null {
 				invalids++
 			}
+		case "description":
+			innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
+				return ec._MediaEXIF_description(ctx, field, obj)
+			}
+
+			out.Values[i] = innerFunc(ctx)
+
 		case "camera":
 			innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
 				return ec._MediaEXIF_camera(ctx, field, obj)
diff --git a/api/graphql/models/media_exif.go b/api/graphql/models/media_exif.go
index 37af1e4..c056ee8 100644
--- a/api/graphql/models/media_exif.go
+++ b/api/graphql/models/media_exif.go
@@ -6,6 +6,7 @@ import (
 
 type MediaEXIF struct {
 	Model
+	Description     *string
 	Camera          *string
 	Maker           *string
 	Lens            *string
diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql
index 8e4f420..0ece4fb 100644
--- a/api/graphql/schema.graphql
+++ b/api/graphql/schema.graphql
@@ -382,6 +382,8 @@ type Media {
 type MediaEXIF {
   id: ID!
   media: Media!
+  "The description of the image"
+  description: String
   "The model name of the camera"
   camera: String
   "The maker of the camera"
diff --git a/api/scanner/exif/exif_parser_external.go b/api/scanner/exif/exif_parser_external.go
index aef14eb..d6da190 100644
--- a/api/scanner/exif/exif_parser_external.go
+++ b/api/scanner/exif/exif_parser_external.go
@@ -81,6 +81,13 @@ func (p *externalExifParser) ParseExif(media_path string) (returnExif *models.Me
 	newExif := models.MediaEXIF{}
 	found_exif := false
 
+	// Get description
+	description, err := fileInfo.GetString("ImageDescription")
+	if err == nil {
+		found_exif = true
+		newExif.Description = &description
+	}
+
 	// Get camera model
 	model, err := fileInfo.GetString("Model")
 	if err == nil {
diff --git a/api/scanner/exif/exif_parser_internal.go b/api/scanner/exif/exif_parser_internal.go
index 11c8ef8..4244b86 100644
--- a/api/scanner/exif/exif_parser_internal.go
+++ b/api/scanner/exif/exif_parser_internal.go
@@ -44,6 +44,11 @@ func (p internalExifParser) ParseExif(media_path string) (returnExif *models.Med
 
 	newExif := models.MediaEXIF{}
 
+	description, err := p.readStringTag(exifTags, exif.ImageDescription, media_path)
+	if err == nil {
+		newExif.Description = description
+	}
+
 	model, err := p.readStringTag(exifTags, exif.Model, media_path)
 	if err == nil {
 		newExif.Camera = model
diff --git a/api/scanner/exif/exif_test.go b/api/scanner/exif/exif_test.go
index 9c2d32a..ca0ae43 100644
--- a/api/scanner/exif/exif_test.go
+++ b/api/scanner/exif/exif_test.go
@@ -48,6 +48,7 @@ func TestExifParsers(t *testing.T) {
 		{
 			path: "./test_data/bird.jpg",
 			assert: func(t *testing.T, exif *models.MediaEXIF) {
+				assert.EqualValues(t, *exif.Description, "Photo of a Bird")
 				assert.WithinDuration(t, *exif.DateShot, time.Unix(1336318784, 0).UTC(), time.Minute)
 				assert.EqualValues(t, *exif.Camera, "Canon EOS 600D")
 				assert.EqualValues(t, *exif.Maker, "Canon")
diff --git a/api/scanner/exif/test_data/bird.jpg b/api/scanner/exif/test_data/bird.jpg
index ea631f9ed3f7df68cf62ae26b7d2f03a9f2c3b5f..e717542888c80c4b05815cc2f5bb197731beb989 100644
GIT binary patch
diff --git a/ui/src/Pages/SharePage/AlbumSharePage.tsx b/ui/src/Pages/SharePage/AlbumSharePage.tsx
index 063a5a3..0e6f5da 100644
--- a/ui/src/Pages/SharePage/AlbumSharePage.tsx
+++ b/ui/src/Pages/SharePage/AlbumSharePage.tsx
@@ -68,6 +68,7 @@ export const SHARE_ALBUM_QUERY = gql`
         }
         exif {
           id
+          description
           camera
           maker
           lens
diff --git a/ui/src/Pages/SharePage/SharePage.tsx b/ui/src/Pages/SharePage/SharePage.tsx
index 4828e26..d49967c 100644
--- a/ui/src/Pages/SharePage/SharePage.tsx
+++ b/ui/src/Pages/SharePage/SharePage.tsx
@@ -49,6 +49,7 @@ export const SHARE_TOKEN_QUERY = gql`
         }
         exif {
           id
+          description
           camera
           maker
           lens
diff --git a/ui/src/Pages/SharePage/__generated__/SharePageToken.ts b/ui/src/Pages/SharePage/__generated__/SharePageToken.ts
index dd4f871..aff5a89 100644
--- a/ui/src/Pages/SharePage/__generated__/SharePageToken.ts
+++ b/ui/src/Pages/SharePage/__generated__/SharePageToken.ts
@@ -106,6 +106,10 @@ export interface SharePageToken_shareToken_media_exif_coordinates {
 export interface SharePageToken_shareToken_media_exif {
   __typename: 'MediaEXIF'
   id: string
+  /**
+   * The description of the image
+   */
+  description: string | null
   /**
    * The model name of the camera
    */
diff --git a/ui/src/Pages/SharePage/__generated__/shareAlbumQuery.ts b/ui/src/Pages/SharePage/__generated__/shareAlbumQuery.ts
index 26b476e..344e051 100644
--- a/ui/src/Pages/SharePage/__generated__/shareAlbumQuery.ts
+++ b/ui/src/Pages/SharePage/__generated__/shareAlbumQuery.ts
@@ -120,6 +120,10 @@ export interface shareAlbumQuery_album_media_exif_coordinates {
 export interface shareAlbumQuery_album_media_exif {
   __typename: 'MediaEXIF'
   id: string
+  /**
+   * The description of the image
+   */
+  description: string | null
   /**
    * The model name of the camera
    */
diff --git a/ui/src/components/sidebar/MediaSidebar/MediaSidebar.tsx b/ui/src/components/sidebar/MediaSidebar/MediaSidebar.tsx
index f5bce87..64aa963 100644
--- a/ui/src/components/sidebar/MediaSidebar/MediaSidebar.tsx
+++ b/ui/src/components/sidebar/MediaSidebar/MediaSidebar.tsx
@@ -65,6 +65,7 @@ export const SIDEBAR_MEDIA_QUERY = gql`
       }
       exif {
         id
+        description
         camera
         maker
         lens
diff --git a/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.test.tsx b/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.test.tsx
index 0e3b002..d0e1b83 100644
--- a/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.test.tsx
+++ b/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.test.tsx
@@ -12,6 +12,7 @@ describe('ExifDetails', () => {
       type: MediaType.Photo,
       exif: {
         id: '0',
+        description: null,
         camera: null,
         maker: null,
         lens: null,
@@ -30,6 +31,7 @@ describe('ExifDetails', () => {
 
     render(<ExifDetails media={media} />)
 
+    expect(screen.queryByText('Description')).not.toBeInTheDocument()
     expect(screen.queryByText('Camera')).not.toBeInTheDocument()
     expect(screen.queryByText('Maker')).not.toBeInTheDocument()
     expect(screen.queryByText('Lens')).not.toBeInTheDocument()
@@ -50,6 +52,7 @@ describe('ExifDetails', () => {
       type: MediaType.Photo,
       exif: {
         id: '1666',
+        description: "Media description",
         camera: 'Canon EOS R',
         maker: 'Canon',
         lens: 'TAMRON SP 24-70mm F/2.8',
@@ -72,6 +75,8 @@ describe('ExifDetails', () => {
 
     render(<ExifDetails media={media} />)
 
+    expect(screen.getByText('Description')).toBeInTheDocument()
+
     expect(screen.getByText('Camera')).toBeInTheDocument()
     expect(screen.getByText('Canon EOS R')).toBeInTheDocument()
 
diff --git a/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.tsx b/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.tsx
index 12adeaf..0c243f6 100644
--- a/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.tsx
+++ b/ui/src/components/sidebar/MediaSidebar/MediaSidebarExif.tsx
@@ -120,6 +120,7 @@ const ExifDetails = ({ media }: ExifDetailsProps) => {
 }
 
 const exifNameLookup = (t: TranslationFn): { [key: string]: string } => ({
+  description: t('sidebar.media.exif.description', 'Description'),
   camera: t('sidebar.media.exif.name.camera', 'Camera'),
   maker: t('sidebar.media.exif.name.maker', 'Maker'),
   lens: t('sidebar.media.exif.name.lens', 'Lens'),
diff --git a/ui/src/components/sidebar/MediaSidebar/__generated__/sidebarMediaQuery.ts b/ui/src/components/sidebar/MediaSidebar/__generated__/sidebarMediaQuery.ts
index 0bd4ce2..8cadd6e 100644
--- a/ui/src/components/sidebar/MediaSidebar/__generated__/sidebarMediaQuery.ts
+++ b/ui/src/components/sidebar/MediaSidebar/__generated__/sidebarMediaQuery.ts
@@ -85,6 +85,10 @@ export interface sidebarMediaQuery_media_exif_coordinates {
 export interface sidebarMediaQuery_media_exif {
   __typename: 'MediaEXIF'
   id: string
+  /**
+   * The description of the image
+   */
+  description: string | null
   /**
    * The model name of the camera
    */
