refactor: improve typing and doc
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/803/head
parent
e75aa4006f
commit
b1ab26e44a
|
@ -171,8 +171,12 @@ class VideoController extends GenericApiController
|
||||||
|
|
||||||
// Requested only JSON info
|
// Requested only JSON info
|
||||||
if ('json' === $format) {
|
if ('json' === $format) {
|
||||||
|
// IPhoto object for the live video
|
||||||
return new JSONResponse([
|
return new JSONResponse([
|
||||||
'fileid' => $liveFile->getId(),
|
'fileid' => $liveFile->getId(),
|
||||||
|
'etag' => $liveFile->getEtag(),
|
||||||
|
'basename' => $liveFile->getName(),
|
||||||
|
'mimetype' => $liveFile->getMimeType(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ import * as utils from '../services/utils';
|
||||||
import * as dav from '../services/dav';
|
import * as dav from '../services/dav';
|
||||||
import { API } from '../services/API';
|
import { API } from '../services/API';
|
||||||
|
|
||||||
import type { IAlbum, IFace, IImageInfo, IPhoto } from '../types';
|
import type { IAlbum, IFace, IImageInfo, IPhoto, IExif } from '../types';
|
||||||
|
|
||||||
interface TopField {
|
interface TopField {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -117,7 +117,7 @@ export default defineComponent({
|
||||||
data: () => ({
|
data: () => ({
|
||||||
fileid: null as number | null,
|
fileid: null as number | null,
|
||||||
filename: '',
|
filename: '',
|
||||||
exif: {} as NonNullable<IImageInfo['exif']>,
|
exif: {} as IExif,
|
||||||
baseInfo: {} as IImageInfo,
|
baseInfo: {} as IImageInfo,
|
||||||
|
|
||||||
loading: 0,
|
loading: 0,
|
||||||
|
@ -198,12 +198,12 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Title EXIF value */
|
/** Title EXIF value */
|
||||||
title(): string | null {
|
title(): string | null {
|
||||||
return this.exif['Title'] || null;
|
return this.exif.Title || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Description EXIF value */
|
/** Description EXIF value */
|
||||||
description(): string | null {
|
description(): string | null {
|
||||||
return this.exif['Description'] || null;
|
return this.exif.Description || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Date taken info */
|
/** Date taken info */
|
||||||
|
@ -252,7 +252,7 @@ export default defineComponent({
|
||||||
dateOriginalTime(): string[] | null {
|
dateOriginalTime(): string[] | null {
|
||||||
if (!this.dateOriginal) return null;
|
if (!this.dateOriginal) return null;
|
||||||
|
|
||||||
const fields = ['OffsetTimeOriginal', 'OffsetTime', 'LocationTZID'];
|
const fields: (keyof IExif)[] = ['OffsetTimeOriginal', 'OffsetTime', 'LocationTZID'];
|
||||||
const hasTz = fields.some((key) => this.exif[key]);
|
const hasTz = fields.some((key) => this.exif[key]);
|
||||||
|
|
||||||
const format = 't' + (hasTz ? ' ZZ' : '');
|
const format = 't' + (hasTz ? ' ZZ' : '');
|
||||||
|
@ -262,18 +262,18 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Camera make and model info */
|
/** Camera make and model info */
|
||||||
camera(): string | null {
|
camera(): string | null {
|
||||||
const make = this.exif['Make'];
|
const make = this.exif.Make;
|
||||||
const model = this.exif['Model'];
|
const model = this.exif.Model;
|
||||||
if (!make || !model) return null;
|
if (!make || !model) return null;
|
||||||
if (model.startsWith(make)) return model;
|
if (model.startsWith(make)) return model;
|
||||||
return `${make} ${model}`;
|
return `${make} ${model}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
cameraSub(): string[] {
|
cameraSub(): string[] {
|
||||||
const f = this.exif['FNumber'] || this.exif['Aperture'];
|
const f = this.exif.FNumber || this.exif.Aperture;
|
||||||
const s = this.shutterSpeed;
|
const s = this.shutterSpeed;
|
||||||
const len = this.exif['FocalLength'];
|
const len = this.exif.FocalLength;
|
||||||
const iso = this.exif['ISO'];
|
const iso = this.exif.ISO;
|
||||||
|
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
if (f) parts.push(`f/${f}`);
|
if (f) parts.push(`f/${f}`);
|
||||||
|
@ -285,7 +285,7 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Convert shutter speed decimal to 1/x format */
|
/** Convert shutter speed decimal to 1/x format */
|
||||||
shutterSpeed(): string | null {
|
shutterSpeed(): string | null {
|
||||||
const speed = Number(this.exif['ShutterSpeedValue'] || this.exif['ShutterSpeed'] || this.exif['ExposureTime']);
|
const speed = Number(this.exif.ShutterSpeedValue || this.exif.ShutterSpeed || this.exif.ExposureTime);
|
||||||
if (!speed) return null;
|
if (!speed) return null;
|
||||||
|
|
||||||
if (speed < 1) {
|
if (speed < 1) {
|
||||||
|
@ -311,7 +311,7 @@ export default defineComponent({
|
||||||
|
|
||||||
imageInfoSub(): string[] {
|
imageInfoSub(): string[] {
|
||||||
let parts: string[] = [];
|
let parts: string[] = [];
|
||||||
let mp = Number(this.exif['Megapixels']);
|
let mp = Number(this.exif.Megapixels);
|
||||||
|
|
||||||
if (this.baseInfo.w && this.baseInfo.h) {
|
if (this.baseInfo.w && this.baseInfo.h) {
|
||||||
parts.push(`${this.baseInfo.w}x${this.baseInfo.h}`);
|
parts.push(`${this.baseInfo.w}x${this.baseInfo.h}`);
|
||||||
|
@ -341,11 +341,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
lat(): number {
|
lat(): number {
|
||||||
return Number(this.exif['GPSLatitude']);
|
return Number(this.exif.GPSLatitude);
|
||||||
},
|
},
|
||||||
|
|
||||||
lon(): number {
|
lon(): number {
|
||||||
return Number(this.exif['GPSLongitude']);
|
return Number(this.exif.GPSLongitude);
|
||||||
},
|
},
|
||||||
|
|
||||||
tagNames(): string[] {
|
tagNames(): string[] {
|
||||||
|
@ -452,7 +452,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleFileUpdated({ fileid }: { fileid: number }) {
|
handleFileUpdated({ fileid }: utils.BusEvent['files:file:updated']) {
|
||||||
if (fileid && this.fileid === fileid) {
|
if (fileid && this.fileid === fileid) {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onScroll({ current, previous }: { current: number; previous: number }) {
|
onScroll({ current, previous }: utils.BusEvent['memories.recycler.scroll']) {
|
||||||
this.isScrollDown = (this.isScrollDown && previous - current < 40) || current - previous > 40; // momentum scroll
|
this.isScrollDown = (this.isScrollDown && previous - current < 40) || current - previous > 40; // momentum scroll
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -476,7 +476,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Handle mouse leave */
|
/** Handle mouse leave */
|
||||||
mouseleave() {
|
mouseleave(event: MouseEvent) {
|
||||||
this.interactend();
|
this.interactend();
|
||||||
this.moveHoverCursor(this.cursorY);
|
this.moveHoverCursor(this.cursorY);
|
||||||
},
|
},
|
||||||
|
@ -539,7 +539,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Handle touch */
|
/** Handle touch */
|
||||||
touchmove(event: any) {
|
touchmove(event: TouchEvent) {
|
||||||
if (!this.scrollerRect) return;
|
if (!this.scrollerRect) return;
|
||||||
let y = event.targetTouches[0].pageY - this.scrollerRect.top;
|
let y = event.targetTouches[0].pageY - this.scrollerRect.top;
|
||||||
y = Math.max(this.topPadding, y + MOBILE_CURSOR_HH); // middle of touch finger
|
y = Math.max(this.topPadding, y + MOBILE_CURSOR_HH); // middle of touch finger
|
||||||
|
|
|
@ -21,7 +21,7 @@ const pendingUrls = new Map<string, BlobCallback[]>();
|
||||||
|
|
||||||
// Cache for preview images
|
// Cache for preview images
|
||||||
const cacheName = 'memories-images';
|
const cacheName = 'memories-images';
|
||||||
let imageCache: Cache;
|
let imageCache: Cache | undefined;
|
||||||
(async function openCache() {
|
(async function openCache() {
|
||||||
try {
|
try {
|
||||||
imageCache = await self.caches?.open(cacheName);
|
imageCache = await self.caches?.open(cacheName);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
@click="click($event, album)"
|
@click="click($event, album)"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<XImg v-if="album.last_added_photo !== -1" class="album__image" :src="toCoverUrl(album.last_added_photo)" />
|
<XImg v-if="album.last_added_photo !== -1" class="album__image" :src="toCoverUrl(album)" />
|
||||||
<div v-else class="album__image album__image--placeholder">
|
<div v-else class="album__image album__image--placeholder">
|
||||||
<ImageMultipleIcon :size="32" />
|
<ImageMultipleIcon :size="32" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,10 +81,10 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
toCoverUrl(fileId: string | number) {
|
toCoverUrl(album: IAlbum) {
|
||||||
return getPreviewUrl({
|
return getPreviewUrl({
|
||||||
photo: {
|
photo: {
|
||||||
fileid: Number(fileId),
|
fileid: Number(album.last_added_photo),
|
||||||
} as IPhoto,
|
} as IPhoto,
|
||||||
sqsize: 256,
|
sqsize: 256,
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,7 +115,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
isMobile(): boolean {
|
isMobile(): boolean {
|
||||||
return globalThis.windowInnerWidth <= 768;
|
return utils.isMobile();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshSidebar() {
|
refreshSidebar() {
|
||||||
if (this.isMobile) return;
|
if (utils.isMobile()) return;
|
||||||
globalThis.mSidebar.close();
|
globalThis.mSidebar.close();
|
||||||
globalThis.mSidebar.open(0, this.filename, true);
|
globalThis.mSidebar.open(0, this.filename, true);
|
||||||
},
|
},
|
||||||
|
|
|
@ -149,7 +149,7 @@ export default defineComponent({
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isAlbumList(): boolean {
|
isAlbumList(): boolean {
|
||||||
return !Boolean(this.$route.params.name);
|
return !this.$route.params.name;
|
||||||
},
|
},
|
||||||
|
|
||||||
canEditAlbum(): boolean {
|
canEditAlbum(): boolean {
|
||||||
|
@ -158,7 +158,7 @@ export default defineComponent({
|
||||||
|
|
||||||
name(): string {
|
name(): string {
|
||||||
// Album name is displayed in the dynamic top matter (timeline)
|
// Album name is displayed in the dynamic top matter (timeline)
|
||||||
return this.$route.params.name ? '' : this.t('memories', 'Albums');
|
return this.isAlbumList ? this.t('memories', 'Albums') : String();
|
||||||
},
|
},
|
||||||
|
|
||||||
isMobile(): boolean {
|
isMobile(): boolean {
|
||||||
|
|
|
@ -10,7 +10,7 @@ if (title) {
|
||||||
let isHidden = false; // cache state to avoid unnecessary DOM updates
|
let isHidden = false; // cache state to avoid unnecessary DOM updates
|
||||||
|
|
||||||
// Hide header when recycler is scrolled down
|
// Hide header when recycler is scrolled down
|
||||||
utils.bus.on('memories.recycler.scroll', ({ dynTopMatterVisible }: { dynTopMatterVisible: boolean }) => {
|
utils.bus.on('memories.recycler.scroll', ({ dynTopMatterVisible }) => {
|
||||||
if (dynTopMatterVisible === isHidden) return;
|
if (dynTopMatterVisible === isHidden) return;
|
||||||
header.classList.toggle('hidden', (isHidden = dynTopMatterVisible));
|
header.classList.toggle('hidden', (isHidden = dynTopMatterVisible));
|
||||||
});
|
});
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onRecyclerScroll({ dynTopMatterVisible }: { dynTopMatterVisible: boolean }) {
|
onRecyclerScroll({ dynTopMatterVisible }: utils.BusEvent['memories.recycler.scroll']) {
|
||||||
this.dynamicVisible = dynTopMatterVisible;
|
this.dynamicVisible = dynTopMatterVisible;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -163,7 +163,7 @@ class VideoContentSetup {
|
||||||
// Create video element
|
// Create video element
|
||||||
content.videoElement = document.createElement('video');
|
content.videoElement = document.createElement('video');
|
||||||
content.videoElement.className = 'video-js';
|
content.videoElement.className = 'video-js';
|
||||||
content.videoElement.setAttribute('poster', content.data.msrc);
|
content.videoElement.setAttribute('poster', content.data.msrc!);
|
||||||
content.videoElement.setAttribute('preload', 'none');
|
content.videoElement.setAttribute('preload', 'none');
|
||||||
content.videoElement.setAttribute('controls', '');
|
content.videoElement.setAttribute('controls', '');
|
||||||
content.videoElement.setAttribute('playsinline', '');
|
content.videoElement.setAttribute('playsinline', '');
|
||||||
|
|
|
@ -468,9 +468,8 @@ export default defineComponent({
|
||||||
arrowNextTitle: this.t('memories', 'Next'),
|
arrowNextTitle: this.t('memories', 'Next'),
|
||||||
getViewportSizeFn: () => {
|
getViewportSizeFn: () => {
|
||||||
// Ignore the sidebar if mobile or fullscreen
|
// Ignore the sidebar if mobile or fullscreen
|
||||||
const isMobile = globalThis.windowInnerWidth < 768;
|
|
||||||
const isFullscreen = Boolean(document.fullscreenElement);
|
const isFullscreen = Boolean(document.fullscreenElement);
|
||||||
const use = this.sidebarOpen && !isMobile && !isFullscreen;
|
const use = this.sidebarOpen && !utils.isMobile() && !isFullscreen;
|
||||||
|
|
||||||
// Calculate the sidebar width to use and outer width
|
// Calculate the sidebar width to use and outer width
|
||||||
const sidebarWidth = use ? globalThis.mSidebar.getWidth() : 0;
|
const sidebarWidth = use ? globalThis.mSidebar.getWidth() : 0;
|
||||||
|
@ -780,7 +779,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Get base data object */
|
/** Get base data object */
|
||||||
getItemData(photo: IPhoto) {
|
getItemData(photo: IPhoto): PsContent['data'] {
|
||||||
let previewUrl = utils.getPreviewUrl({ photo, size: 'screen' });
|
let previewUrl = utils.getPreviewUrl({ photo, size: 'screen' });
|
||||||
const isvideo = photo.flag & this.c.FLAG_IS_VIDEO;
|
const isvideo = photo.flag & this.c.FLAG_IS_VIDEO;
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,16 @@ import { IPhoto } from '../../types';
|
||||||
|
|
||||||
type PsAugment = {
|
type PsAugment = {
|
||||||
data: _SlideData & {
|
data: _SlideData & {
|
||||||
|
/** The original source of the image.*/
|
||||||
src: string;
|
src: string;
|
||||||
msrc: string;
|
/** The original photo object. */
|
||||||
photo: IPhoto;
|
photo: IPhoto;
|
||||||
|
/** The source of the high resolution image. */
|
||||||
|
highSrc: string | null;
|
||||||
|
/** The condition for loading the high resolution image. */
|
||||||
|
highSrcCond: 'always' | 'zoom' | 'never';
|
||||||
|
/** The type of content. */
|
||||||
|
type: 'image' | 'video';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type PsSlide = Slide &
|
export type PsSlide = Slide &
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as utils from '../services/utils';
|
||||||
import { IConfig } from '../types';
|
import { IConfig } from '../types';
|
||||||
import staticConfig from '../services/static-config';
|
import staticConfig from '../services/static-config';
|
||||||
|
|
||||||
const eventName = 'memories:user-config-changed';
|
const eventName: keyof utils.BusEvent = 'memories:user-config-changed';
|
||||||
|
|
||||||
const localSettings: (keyof IConfig)[] = [
|
const localSettings: (keyof IConfig)[] = [
|
||||||
'square_thumbs',
|
'square_thumbs',
|
||||||
|
|
|
@ -231,13 +231,9 @@ async function extendWithLivePhotos(photos: IPhoto[]) {
|
||||||
photos
|
photos
|
||||||
.filter((p) => p.liveid && !p.liveid.startsWith('self__'))
|
.filter((p) => p.liveid && !p.liveid.startsWith('self__'))
|
||||||
.map(async (p) => {
|
.map(async (p) => {
|
||||||
const url = API.Q(utils.getLivePhotoVideoUrl(p, false), { format: 'json' });
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(url);
|
const url = API.Q(utils.getLivePhotoVideoUrl(p, false), { format: 'json' });
|
||||||
const data = response.data;
|
return (await axios.get<IPhoto>(url)).data;
|
||||||
return {
|
|
||||||
fileid: data.fileid,
|
|
||||||
} as IPhoto;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -67,8 +67,8 @@ function parseIdFromLocation(url: string): number {
|
||||||
url = url.substring(0, queryPos);
|
url = url.substring(0, queryPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = url.split('/');
|
const parts: string[] = url.split('/');
|
||||||
let result;
|
let result: string | undefined;
|
||||||
do {
|
do {
|
||||||
result = parts[parts.length - 1];
|
result = parts[parts.length - 1];
|
||||||
parts.pop();
|
parts.pop();
|
||||||
|
|
|
@ -66,11 +66,11 @@ export function randomChoice<T>(arr: T[]): T {
|
||||||
*/
|
*/
|
||||||
export function randomSubarray<T>(arr: T[], size: number): T[] {
|
export function randomSubarray<T>(arr: T[], size: number): T[] {
|
||||||
if (arr.length <= size) return arr;
|
if (arr.length <= size) return arr;
|
||||||
var shuffled = arr.slice(0),
|
let shuffled: T[] = arr.slice(0),
|
||||||
i = arr.length,
|
i: number = arr.length,
|
||||||
min = i - size,
|
min: number = i - size,
|
||||||
temp,
|
temp: T,
|
||||||
index;
|
index: number;
|
||||||
while (i-- > min) {
|
while (i-- > min) {
|
||||||
index = Math.floor((i + 1) * Math.random());
|
index = Math.floor((i + 1) * Math.random());
|
||||||
temp = shuffled[index];
|
temp = shuffled[index];
|
||||||
|
|
|
@ -17,7 +17,7 @@ export function dateToDayId(date: Date) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get month name from number */
|
/** Get month name from number */
|
||||||
export function getShortDateStr(date: Date) {
|
export function getShortDateStr(date: Date): string {
|
||||||
const dayId = dateToDayId(date);
|
const dayId = dateToDayId(date);
|
||||||
if (!shortDateStrMemo.has(dayId)) {
|
if (!shortDateStrMemo.has(dayId)) {
|
||||||
shortDateStrMemo.set(
|
shortDateStrMemo.set(
|
||||||
|
@ -29,7 +29,7 @@ export function getShortDateStr(date: Date) {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return shortDateStrMemo.get(dayId);
|
return shortDateStrMemo.get(dayId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get long date string with optional year if same as current */
|
/** Get long date string with optional year if same as current */
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
|
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus';
|
||||||
import { IConfig, IPhoto } from '../../types';
|
import { IConfig, IPhoto } from '../../types';
|
||||||
|
|
||||||
type BusEvent = {
|
export type BusEvent = {
|
||||||
/** Open/close the navigation drawer */
|
/** Open/close the navigation drawer */
|
||||||
'toggle-navigation': { open: boolean };
|
'toggle-navigation': { open: boolean };
|
||||||
/** File was created */
|
/** File was created */
|
||||||
|
@ -26,7 +26,10 @@ type BusEvent = {
|
||||||
value: IConfig[keyof IConfig];
|
value: IConfig[keyof IConfig];
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
/** Delete these photos from the timeline */
|
/**
|
||||||
|
* Remove these photos from the timeline.
|
||||||
|
* Each photo object is required to have the `d` (day) property.
|
||||||
|
*/
|
||||||
'memories:timeline:deleted': IPhoto[];
|
'memories:timeline:deleted': IPhoto[];
|
||||||
/** Viewer has requested fetching day */
|
/** Viewer has requested fetching day */
|
||||||
'memories:timeline:fetch-day': number;
|
'memories:timeline:fetch-day': number;
|
||||||
|
@ -46,17 +49,32 @@ type BusEvent = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit an event on the Nextcloud event bus.
|
* Wrapper around Nextcloud's event bus.
|
||||||
* @param name Name of event
|
|
||||||
* @param data arguments
|
|
||||||
*/
|
*/
|
||||||
export const bus = {
|
export const bus = {
|
||||||
|
/**
|
||||||
|
* Emit an event on the Nextcloud event bus.
|
||||||
|
* @param name Name of event
|
||||||
|
* @param data arguments
|
||||||
|
*/
|
||||||
emit<T extends keyof BusEvent>(name: T, data: BusEvent[T]): void {
|
emit<T extends keyof BusEvent>(name: T, data: BusEvent[T]): void {
|
||||||
emit(name, data as any);
|
emit(name, data as any);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to an event on the Nextcloud event bus.
|
||||||
|
* @param name Name of event
|
||||||
|
* @param callback Callback to be called when the event is emitted
|
||||||
|
*/
|
||||||
on<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
|
on<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
|
||||||
subscribe(name, callback);
|
subscribe(name, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from an event on the Nextcloud event bus.
|
||||||
|
* @param name Name of event
|
||||||
|
* @param callback Same callback that was passed to `on`
|
||||||
|
*/
|
||||||
off<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
|
off<T extends keyof BusEvent>(name: T, callback: (data: BusEvent[T]) => void): void {
|
||||||
unsubscribe(name, callback);
|
unsubscribe(name, callback);
|
||||||
},
|
},
|
||||||
|
|
50
src/types.ts
50
src/types.ts
|
@ -106,24 +106,7 @@ export interface IImageInfo {
|
||||||
address?: string;
|
address?: string;
|
||||||
tags?: { [id: string]: string };
|
tags?: { [id: string]: string };
|
||||||
|
|
||||||
exif?: {
|
exif?: IExif;
|
||||||
Rotation?: number;
|
|
||||||
Orientation?: number;
|
|
||||||
ImageWidth?: number;
|
|
||||||
ImageHeight?: number;
|
|
||||||
|
|
||||||
Title?: string;
|
|
||||||
Description?: string;
|
|
||||||
Make?: string;
|
|
||||||
Model?: string;
|
|
||||||
|
|
||||||
DateTimeEpoch?: number;
|
|
||||||
OffsetTimeOriginal?: string;
|
|
||||||
OffsetTime?: string;
|
|
||||||
LocationTZID?: string;
|
|
||||||
|
|
||||||
[other: string]: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
clusters?: {
|
clusters?: {
|
||||||
albums?: IAlbum[];
|
albums?: IAlbum[];
|
||||||
|
@ -132,6 +115,37 @@ export interface IImageInfo {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IExif {
|
||||||
|
Rotation?: number;
|
||||||
|
Orientation?: number;
|
||||||
|
ImageWidth?: number;
|
||||||
|
ImageHeight?: number;
|
||||||
|
Megapixels?: number;
|
||||||
|
|
||||||
|
Title?: string;
|
||||||
|
Description?: string;
|
||||||
|
Make?: string;
|
||||||
|
Model?: string;
|
||||||
|
|
||||||
|
DateTimeEpoch?: number;
|
||||||
|
OffsetTimeOriginal?: string;
|
||||||
|
OffsetTime?: string;
|
||||||
|
LocationTZID?: string;
|
||||||
|
|
||||||
|
ExposureTime?: number;
|
||||||
|
ShutterSpeed?: number;
|
||||||
|
ShutterSpeedValue?: number;
|
||||||
|
Aperture?: number;
|
||||||
|
ApertureValue?: number;
|
||||||
|
ISO?: number;
|
||||||
|
FNumber?: number;
|
||||||
|
FocalLength?: number;
|
||||||
|
|
||||||
|
GPSAltitude?: number;
|
||||||
|
GPSLatitude?: number;
|
||||||
|
GPSLongitude?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFolder extends IPhoto {
|
export interface IFolder extends IPhoto {
|
||||||
/** Path to folder */
|
/** Path to folder */
|
||||||
path: string;
|
path: string;
|
||||||
|
|
Loading…
Reference in New Issue