Skip to content

Commit

Permalink
feat: Add override metadata (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhea-so committed May 8, 2024
2 parents 3b2ccb5 + 94419c7 commit d97517f
Show file tree
Hide file tree
Showing 19 changed files with 370 additions and 42 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.4.81",
"version": "0.4.82",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
8 changes: 8 additions & 0 deletions web/src/core/exif-metadata/override-exif-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const overrideExifMetadata = () => {
const overridableMetadata: { [key: string]: string }[] = JSON.parse(localStorage.getItem('overridableMetadata') || '[]');
const overrideMetadataIndex: number | null = JSON.parse(localStorage.getItem('overrideMetadataIndex') || 'null');
const metadata = overrideMetadataIndex == null ? null : overridableMetadata.length > overrideMetadataIndex ? overridableMetadata[overrideMetadataIndex] : null;
return metadata;
};

export default overrideExifMetadata;
23 changes: 13 additions & 10 deletions web/src/core/photo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { load } from 'exifreader';
import ExifMetadata from './exif-metadata/exif-metadata';
import thumbnail from './drawing/thumbnail';
import overrideExifMetadata from './exif-metadata/override-exif-metadata';

/**
* Represents a photo.
Expand Down Expand Up @@ -39,7 +40,7 @@ class Photo {
*/
public get make(): string {
if (localStorage.getItem('showCameraMaker') === 'false') return '';
return localStorage.getItem('overrideCameraMaker') || this.metadata.make || '';
return overrideExifMetadata()?.make || this.metadata.make || '';
}

/**
Expand All @@ -48,7 +49,7 @@ class Photo {
*/
public get model(): string {
if (localStorage.getItem('showCameraModel') === 'false') return '';
return localStorage.getItem('overrideCameraModel') || this.metadata.model || '';
return overrideExifMetadata()?.model || this.metadata.model || '';
}

/**
Expand All @@ -57,7 +58,7 @@ class Photo {
*/
public get lensModel(): string {
if (localStorage.getItem('showLensModel') === 'false') return '';
return localStorage.getItem('overrideLensModel') || this.metadata.lensModel || '';
return overrideExifMetadata()?.lensModel || this.metadata.lensModel || '';
}

/**
Expand All @@ -66,44 +67,46 @@ class Photo {
*/
public get focalLength(): string {
if (localStorage.getItem('focalLengthRatioMode') === 'true') {
const focalLength = parseFloat(this.metadata?.focalLength?.replace(' mm', '') || '0');
const focalLength = parseFloat(overrideExifMetadata()?.focalLength?.replace(' mm', '') || this.metadata?.focalLength?.replace(' mm', '') || '0');
return (focalLength * parseFloat(localStorage.getItem('focalLengthRatio') || '1')).toFixed(0) + 'mm';
}
return localStorage.getItem('focalLength35mmMode') === 'false' ? this.metadata.focalLength || '' : this.metadata.focalLengthIn35mm || '';
return localStorage.getItem('focalLength35mmMode') === 'false'
? overrideExifMetadata()?.focalLength || this.metadata.focalLength || ''
: overrideExifMetadata()?.focalLengthIn35mm || this.metadata.focalLengthIn35mm || '';
}

/**
* Returns the F number of the camera that took the photo.
* @example 'F4'
*/
public get fNumber(): string {
return this.metadata.fNumber || '';
return overrideExifMetadata()?.fNumber || this.metadata.fNumber || '';
}

/**
* Returns the ISO of the camera that took the photo.
* @example 'ISO100'
*/
public get iso(): string {
return this.metadata.iso || '';
return overrideExifMetadata()?.iso || this.metadata.iso || '';
}

/**
* Returns the exposure time of the camera that took the photo.
* @example '1/100s'
*/
public get exposureTime(): string {
return this.metadata.exposureTime || '';
return overrideExifMetadata()?.exposureTime || this.metadata.exposureTime || '';
}

/**
* Returns the date the photo was taken.
* @example '2021-01-01T00:00:00.000+09:00'
*/
public get takenAt(): string {
if (!this.metadata.takenAt) return '';
if (!overrideExifMetadata()?.takenAt && !this.metadata.takenAt) return '';

const takenAt = new Date(this.metadata.takenAt);
const takenAt = new Date(overrideExifMetadata()?.takenAt || this.metadata.takenAt!);
switch (localStorage.getItem('dateNotation') || '2001/01/01 01:01:01') {
case '2001/01/01 01:01:01':
return `${takenAt.getFullYear()}/${(takenAt.getMonth() + 1).toString().padStart(2, '0')}/${takenAt.getDate().toString().padStart(2, '0')} ${takenAt
Expand Down
12 changes: 12 additions & 0 deletions web/src/icons/pencil.icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Icon } from 'konsta/react';
import { LuPencilLine } from 'react-icons/lu';

interface PencilIconProps {
size?: number;
}

const PencilIcon = ({ size }: PencilIconProps) => {
return <Icon ios={<LuPencilLine size={size} />} />;
};

export default PencilIcon;
17 changes: 10 additions & 7 deletions web/src/locales/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@
"ja": "日本語",
"ko": "한국어",

"make": "Camera Maker",
"model": "Camera Model",
"lens": "Lens Model",
"focal": "Focal Length",
"focal-35mm": "Focal Length 35mm",
"name": "Name",
"make": "Camera maker",
"model": "Camera model",
"lens": "Lens model",
"focal": "Focal length",
"focal-35mm": "Focal length 35mm",
"aperture": "Aperture",
"shutter": "Shutter Speed",
"shutter": "Shutter speed",
"iso": "ISO",
"taken-at": "Taken At",
"taken-at": "Taken at",

"drag-and-drop-photos-here": "Drag and drop photos here",

Expand Down Expand Up @@ -74,6 +75,8 @@
"root.settings.override-camera-maker": "Override Camera Maker",
"root.settings.override-camera-model": "Override Camera Model",
"root.settings.override-lens-model": "Override Lens Model",
"root.settings.select-overridable-metadata": "Select Overridable Metadata",
"root.settings.create-metadata": "Create Metadata",

"privacy-policy": "Privacy Policy",
"term-and-conditions": "Term and Conditions",
Expand Down
3 changes: 3 additions & 0 deletions web/src/locales/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"ja": "日本語",
"ko": "한국어",

"name": "名前",
"make": "カメラメーカー",
"model": "カメラモデル",
"lens": "レンズモデル",
Expand Down Expand Up @@ -74,6 +75,8 @@
"root.settings.override-camera-maker": "カメラメーカーを上書き",
"root.settings.override-camera-model": "カメラモデルを上書き",
"root.settings.override-lens-model": "レンズモデルを上書き",
"root.settings.select-overridable-metadata": "上書き可能なメタデータを選択",
"root.settings.create-metadata": "メタデータを作成",

"privacy-policy": "プライバシーポリシー",
"term-and-conditions": "利用規約",
Expand Down
3 changes: 3 additions & 0 deletions web/src/locales/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"ja": "日本語",
"ko": "한국어",

"name": "이름",
"make": "카메라 제조사",
"model": "카메라 모델",
"lens": "렌즈 모델",
Expand Down Expand Up @@ -75,6 +76,8 @@
"root.settings.override-camera-maker": "카메라 제조사 전역 변경",
"root.settings.override-camera-model": "카메라 모델 전역 변경",
"root.settings.override-lens-model": "렌즈 모델 전역 변경",
"root.settings.select-overridable-metadata": "전역 변경할 메타데이터 선택",
"root.settings.create-metadata": "메타데이터 생성",

"privacy-policy": "개인정보 처리방침",
"term-and-conditions": "이용약관",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ListButton } from 'konsta/react';
import { useTranslation } from 'react-i18next';
import { useStore } from '../../../store';
import AddIcon from '../../../icons/add.icon';

const AddOverridableMetadataButton = () => {
const { t } = useTranslation();
const { setAddOverridableMetadataPopup } = useStore();

return (
<ListButton onClick={() => setAddOverridableMetadataPopup(true)}>
<AddIcon size={18} />
<div style={{ width: 4 }} />
{t('root.settings.create-metadata')}
</ListButton>
);
};

export default AddOverridableMetadataButton;
85 changes: 85 additions & 0 deletions web/src/pages/metadata/components/add-override-metadata.popup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Link, List, ListButton, ListInput, Navbar, Page, Popup } from 'konsta/react';
import { useStore } from '../../../store';
import { useTranslation } from 'react-i18next';
import { useEffect, useState } from 'react';

const AddOverrideMetadataPopup = () => {
const { t } = useTranslation();
const { addOverridableMetadataPopup, setAddOverridableMetadataPopup, setOverridableMetadata, overridableMetadata } = useStore();

const [name, setName] = useState('');
const [make, setMake] = useState('');
const [model, setModel] = useState('');
const [lens, setLens] = useState('');
const [focalLength, setFocalLength] = useState('');
const [focalLengthIn35mm, setFocalLengthIn35mm] = useState('');
const [aperture, setAperture] = useState('');
const [iso, setIso] = useState('');
const [shutter, setShutter] = useState('');
const [takenAt, setTakenAt] = useState('');

useEffect(() => {
setName('');
setMake('');
setModel('');
setLens('');
setFocalLength('');
setFocalLengthIn35mm('');
setAperture('');
setIso('');
setShutter('');
setTakenAt('');
}, [addOverridableMetadataPopup]);

const onCreateMetadata = () => {
if (!name) return;

setOverridableMetadata([
...overridableMetadata,
{
name,
make,
model,
lensModel: lens,
focalLength,
focalLengthIn35mm,
fNumber: aperture,
iso,
exposureTime: shutter,
takenAt,
},
]);

setAddOverridableMetadataPopup(false);
};

return (
<Popup opened={addOverridableMetadataPopup} onBackdropClick={() => setAddOverridableMetadataPopup(false)}>
<Page>
<Navbar
title={t('root.settings.create-metadata')}
right={
<Link navbar onClick={() => setAddOverridableMetadataPopup(false)}>
{t('close')}
</Link>
}
/>
<List strongIos inset>
<ListInput label={t('name') + '*'} type="text" value={name} onChange={(e) => setName(e.target.value)} />
<ListInput label={t('make')} type="text" value={make} onChange={(e) => setMake(e.target.value)} />
<ListInput label={t('model')} type="text" value={model} onChange={(e) => setModel(e.target.value)} />
<ListInput label={t('lens')} type="text" value={lens} onChange={(e) => setLens(e.target.value)} />
<ListInput label={t('focal') + ' (ex. 35mm)'} type="text" value={focalLength} onChange={(e) => setFocalLength(e.target.value)} />
<ListInput label={t('focal-35mm') + ' (ex. 50mm)'} type="text" value={focalLengthIn35mm} onChange={(e) => setFocalLengthIn35mm(e.target.value)} />
<ListInput label={t('aperture') + ' (ex. F2.8)'} type="text" value={aperture} onChange={(e) => setAperture(e.target.value)} />
<ListInput label={t('iso') + ' (ex. ISO1200)'} type="text" value={iso} onChange={(e) => setIso(e.target.value)} />
<ListInput label={t('shutter') + ' (ex. 1/200s)'} type="text" value={shutter} onChange={(e) => setShutter(e.target.value)} />
<ListInput label={t('taken-at') + ' (ex. 2024-05-06T08:35:38.223+09:00)'} type="text" value={takenAt} onChange={(e) => setTakenAt(e.target.value)} />
<ListButton onClick={onCreateMetadata}>{t('root.settings.create-metadata')}</ListButton>
</List>
</Page>
</Popup>
);
};

export default AddOverrideMetadataPopup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ListItem } from 'konsta/react';
import { useStore } from '../../../store';
import RemoveOneMetadataButton from './remove-one-metadata.button';

const OverridableMetadataList = () => {
const { overridableMetadata } = useStore();

return (
<>
{overridableMetadata.map((metadata, index) => (
<ListItem
key={index}
title={metadata.name}
text={`${metadata.make} ${metadata.model} ${metadata.lensModel} ${metadata.focalLength} ${metadata.focalLengthIn35mm} ${metadata.fNumber} ${metadata.iso} ${metadata.exposureTime} ${metadata.takenAt}`}
footer={
<div className="flex space-x-1 mt-1">
<RemoveOneMetadataButton index={index} />
</div>
}
/>
))}
</>
);
};

export default OverridableMetadataList;
31 changes: 31 additions & 0 deletions web/src/pages/metadata/components/remove-one-metadata.button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IoTrashOutline } from 'react-icons/io5';
import { useStore } from '../../../store';
import { Button, Icon } from 'konsta/react';

interface RemoveOneMetadataButtonProps {
index: number;
}

const RemoveOneMetadataButton: React.FC<RemoveOneMetadataButtonProps> = ({ index }) => {
const { overridableMetadata, setOverridableMetadata, overrideMetadataIndex, setOverrideMetadataIndex } = useStore();

return (
<div className="w-10">
<Button
className="k-color-brand-red"
onClick={() => {
const newMetadata = overridableMetadata.filter((_, i) => i !== index);
setOverridableMetadata(newMetadata);

if (overrideMetadataIndex === index) {
setOverrideMetadataIndex(null);
}
}}
>
<Icon ios={<IoTrashOutline className="w-5 h-5" />} />
</Button>
</div>
);
};

export default RemoveOneMetadataButton;
28 changes: 28 additions & 0 deletions web/src/pages/metadata/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { List, Navbar, NavbarBackLink, Page } from 'konsta/react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import OverridableMetadataList from './components/overridable-metadata.list-item';
import AddOverridableMetadataButton from './components/add-overridable-metadata.button';
import AddOverrideMetadataPopup from './components/add-override-metadata.popup';

const MetadataPage = () => {
const navigator = useNavigate();
const { t } = useTranslation();

return (
<>
<Page>
<Navbar title={t('root.settings.create-metadata')} left={<NavbarBackLink text={t('back')} onClick={() => navigator(-1)} />} />

<List strong inset>
<OverridableMetadataList />
<AddOverridableMetadataButton />
</List>

<AddOverrideMetadataPopup />
</Page>
</>
);
};

export default MetadataPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ListItem } from 'konsta/react';
import { useTranslation } from 'react-i18next';
import PencilIcon from '../../../icons/pencil.icon';
import { useNavigate } from 'react-router-dom';

const CreateOverrideMetadataListItem = () => {
const navigate = useNavigate();
const { t } = useTranslation();

return <ListItem media={<PencilIcon size={26} />} title={t('root.settings.create-metadata')} link onClick={() => navigate('/metadata')} />;
};

export default CreateOverrideMetadataListItem;
Loading

0 comments on commit d97517f

Please sign in to comment.