From 0514ce5eb8d1561e04453a70a526cb4ba455a789 Mon Sep 17 00:00:00 2001 From: SvenPf Date: Mon, 24 Jul 2023 01:12:38 +0200 Subject: [PATCH 1/3] add feature to customize date formatting --- src/api/apis/MALAPI.ts | 9 ++--- src/api/apis/OMDbAPI.ts | 7 ++-- src/api/apis/SteamAPI.ts | 3 +- src/api/apis/WikipediaAPI.ts | 3 +- src/main.ts | 5 +++ src/settings/Settings.ts | 20 +++++++++++ src/utils/DateFormatter.ts | 70 ++++++++++++++++++++++++++++++++++++ src/utils/Utils.ts | 2 ++ 8 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/utils/DateFormatter.ts diff --git a/src/api/apis/MALAPI.ts b/src/api/apis/MALAPI.ts index a728316..50566cc 100644 --- a/src/api/apis/MALAPI.ts +++ b/src/api/apis/MALAPI.ts @@ -8,6 +8,7 @@ import { MediaType } from '../../utils/MediaType'; export class MALAPI extends APIModel { plugin: MediaDbPlugin; typeMappings: Map; + apiDateFormat: string = 'YYYY-MM-DDTHH:mm:ssZ'; // ISO constructor(plugin: MediaDbPlugin) { super(); @@ -115,7 +116,7 @@ export class MALAPI extends APIModel { image: result.images?.jpg?.image_url ?? '', released: true, - premiere: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown', + premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown', streamingServices: result.streaming?.map((x: any) => x.name) ?? [], userData: { @@ -146,7 +147,7 @@ export class MALAPI extends APIModel { image: result.images?.jpg?.image_url ?? '', released: true, - premiere: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown', + premiere: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown', streamingServices: result.streaming?.map((x: any) => x.name) ?? [], userData: { @@ -176,8 +177,8 @@ export class MALAPI extends APIModel { image: result.images?.jpg?.image_url ?? '', released: true, - airedFrom: new Date(result.aired?.from).toLocaleDateString() ?? 'unknown', - airedTo: new Date(result.aired?.to).toLocaleDateString() ?? 'unknown', + airedFrom: this.plugin.dateFormatter.format(result.aired?.from, this.apiDateFormat) ?? 'unknown', + airedTo: this.plugin.dateFormatter.format(result.aired?.to, this.apiDateFormat) ?? 'unknown', airing: result.airing, userData: { diff --git a/src/api/apis/OMDbAPI.ts b/src/api/apis/OMDbAPI.ts index 9876291..ad3fb45 100644 --- a/src/api/apis/OMDbAPI.ts +++ b/src/api/apis/OMDbAPI.ts @@ -9,6 +9,7 @@ import { MediaType } from '../../utils/MediaType'; export class OMDbAPI extends APIModel { plugin: MediaDbPlugin; typeMappings: Map; + apiDateFormat: string = 'DD MMM YYYY'; constructor(plugin: MediaDbPlugin) { super(); @@ -142,7 +143,7 @@ export class OMDbAPI extends APIModel { released: true, streamingServices: [], - premiere: new Date(result.Released).toLocaleDateString() ?? 'unknown', + premiere: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown', userData: { watched: false, @@ -173,7 +174,7 @@ export class OMDbAPI extends APIModel { released: true, streamingServices: [], airing: false, - airedFrom: new Date(result.Released).toLocaleDateString() ?? 'unknown', + airedFrom: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown', airedTo: 'unknown', userData: { @@ -199,7 +200,7 @@ export class OMDbAPI extends APIModel { image: result.Poster ?? '', released: true, - releaseDate: new Date(result.Released).toLocaleDateString() ?? 'unknown', + releaseDate: this.plugin.dateFormatter.format(result.Released, this.apiDateFormat) ?? 'unknown', userData: { played: false, diff --git a/src/api/apis/SteamAPI.ts b/src/api/apis/SteamAPI.ts index ae36d3a..fda9dcb 100644 --- a/src/api/apis/SteamAPI.ts +++ b/src/api/apis/SteamAPI.ts @@ -8,6 +8,7 @@ import { MediaType } from '../../utils/MediaType'; export class SteamAPI extends APIModel { plugin: MediaDbPlugin; typeMappings: Map; + apiDateFormat: string = 'DD MMM, YYYY'; constructor(plugin: MediaDbPlugin) { super(); @@ -109,7 +110,7 @@ export class SteamAPI extends APIModel { image: result.header_image ?? '', released: !result.release_date?.comming_soon, - releaseDate: new Date(result.release_date?.date).toLocaleDateString() ?? 'unknown', + releaseDate: this.plugin.dateFormatter.format(result.release_date?.date, this.apiDateFormat) ?? 'unknown', userData: { played: false, diff --git a/src/api/apis/WikipediaAPI.ts b/src/api/apis/WikipediaAPI.ts index 5193bb4..72571d8 100644 --- a/src/api/apis/WikipediaAPI.ts +++ b/src/api/apis/WikipediaAPI.ts @@ -6,6 +6,7 @@ import { MediaType } from '../../utils/MediaType'; export class WikipediaAPI extends APIModel { plugin: MediaDbPlugin; + apiDateFormat: string = 'YYYY-MM-DDTHH:mm:ssZ'; // ISO constructor(plugin: MediaDbPlugin) { super(); @@ -72,7 +73,7 @@ export class WikipediaAPI extends APIModel { id: result.pageid, wikiUrl: result.fullurl, - lastUpdated: new Date(result.touched).toLocaleDateString() ?? 'unknown', + lastUpdated: this.plugin.dateFormatter.format(result.touched, this.apiDateFormat), length: result.length, userData: {}, diff --git a/src/main.ts b/src/main.ts index 76abff4..a62e99c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,6 +15,7 @@ import { YAMLConverter } from './utils/YAMLConverter'; import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal'; import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping'; import { ModalHelper, ModalResultCode, SearchModalOptions } from './utils/ModalHelper'; +import { DateFormatter } from './utils/DateFormatter'; export default class MediaDbPlugin extends Plugin { settings: MediaDbPluginSettings; @@ -22,6 +23,7 @@ export default class MediaDbPlugin extends Plugin { mediaTypeManager: MediaTypeManager; modelPropertyMapper: PropertyMapper; modalHelper: ModalHelper; + dateFormatter: DateFormatter; frontMatterRexExpPattern: string = '^(---)\\n[\\s\\S]*?\\n---'; @@ -39,6 +41,7 @@ export default class MediaDbPlugin extends Plugin { this.mediaTypeManager = new MediaTypeManager(); this.modelPropertyMapper = new PropertyMapper(this); this.modalHelper = new ModalHelper(this); + this.dateFormatter = new DateFormatter(); await this.loadSettings(); // register the settings tab @@ -46,6 +49,7 @@ export default class MediaDbPlugin extends Plugin { this.mediaTypeManager.updateTemplates(this.settings); this.mediaTypeManager.updateFolders(this.settings); + this.dateFormatter.setFormat(this.settings.customDateFormat); // add icon to the left ribbon const ribbonIconEl = this.addRibbonIcon('database', 'Add new Media DB entry', () => this.createEntryWithAdvancedSearchModal()); @@ -563,6 +567,7 @@ export default class MediaDbPlugin extends Plugin { async saveSettings(): Promise { this.mediaTypeManager.updateTemplates(this.settings); this.mediaTypeManager.updateFolders(this.settings); + this.dateFormatter.setFormat(this.settings.customDateFormat); await this.saveData(this.settings); } diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 4c37060..c53a46e 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -7,12 +7,14 @@ import PropertyMappingModelsComponent from './PropertyMappingModelsComponent.sve import { PropertyMapping, PropertyMappingModel, PropertyMappingOption } from './PropertyMapping'; import { MEDIA_TYPES } from '../utils/MediaTypeManager'; import { MediaTypeModel } from '../models/MediaTypeModel'; +import { fragWithHTML } from '../utils/Utils'; export interface MediaDbPluginSettings { OMDbKey: string; sfwFilter: boolean; useCustomYamlStringifier: boolean; templates: boolean; + customDateFormat: string; movieTemplate: string; seriesTemplate: string; @@ -50,6 +52,7 @@ const DEFAULT_SETTINGS: MediaDbPluginSettings = { sfwFilter: true, useCustomYamlStringifier: true, templates: true, + customDateFormat: 'L', movieTemplate: '', seriesTemplate: '', @@ -165,6 +168,23 @@ export class MediaDbSettingTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName('Date format') + .setDesc(fragWithHTML("Your custom date format. Use \'YYYY-MM-DD\' for example.
" + + "For more syntax, refer to format reference.
" + + "Your current syntax looks like this: " + + this.plugin.dateFormatter.getPreview() + "")) + .addText(cb => { + cb.setPlaceholder(DEFAULT_SETTINGS.customDateFormat) + .setValue(this.plugin.settings.customDateFormat === DEFAULT_SETTINGS.customDateFormat ? '' : this.plugin.settings.customDateFormat) + .onChange(data => { + const newDateFormat = data ? data : DEFAULT_SETTINGS.customDateFormat; + this.plugin.settings.customDateFormat = newDateFormat; + document.getElementById('dateformat-preview').innerHTML = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview + this.plugin.saveSettings(); + }); + }); + containerEl.createEl('h3', { text: 'New File Location' }); // region new file location new Setting(containerEl) diff --git a/src/utils/DateFormatter.ts b/src/utils/DateFormatter.ts new file mode 100644 index 0000000..eac1761 --- /dev/null +++ b/src/utils/DateFormatter.ts @@ -0,0 +1,70 @@ +// obsidian already uses moment, so no need to package it twice! +// import { moment } from 'obsidian'; // doesn't work for release build +// obsidian uses a namespace-style import for moment, which ES6 doesn't allow anymore +const obsidian = require('obsidian'); +const moment = obsidian.moment; + +export class DateFormatter { + toFormat: string; + localeString: string; + + constructor() { + this.toFormat = 'YYYY-MM-DD'; + // get locale string (e.g. en, en-gb, de, fr, etc.) + this.localeString = new Intl.DateTimeFormat().resolvedOptions().locale; + } + + setFormat(format: string): void { + this.toFormat = format; + } + + getPreview(format?: string): string { + const today = moment(); + today.locale(this.localeString); + + if (!format) { + format = this.toFormat; + } + + return today.format(format); + } + + /** + * Tries to format a given date string with the currently set date format. + * You can set a date format by calling `setFormat()`. + * + * @param dateString the date string to be formatted + * @param dateFormat the current format of `dateString`. When this is `null` and the actual format of the + * given date string is not `C2822` or `ISO` format, this function will try to guess the format by using the native `Date` module. + * @returns formatted date string or null if `dateString` is not a valid date + */ + format(dateString: string, dateFormat?: string): string | null { + if (!dateString) { + return null; + } + + let date: moment.Moment; + + if (!dateFormat) { + // reading date formats other then C2822 or ISO with moment is deprecated + if (this.hasMomentFormat(dateString)) { + // expect C2822 or ISO format + date = moment(dateString); + } else { + // try to read date string with native Date + date = moment(new Date(dateString)); + } + date.locale(this.localeString); // set local locale definition for moment + } else { + date = moment(dateString, dateFormat, this.localeString); + } + + // format date (if it is valid) + return date.isValid() ? date.format(this.toFormat) : null; + } + + private hasMomentFormat(dateString: string): boolean { + const date = moment(dateString, true); // strict mode + return date.isValid(); + } +} diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 2d2fb71..4aebac1 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -135,6 +135,8 @@ export function markdownTable(content: string[][]): string { return table; } +export const fragWithHTML = (html: string) => createFragment(frag => (frag.createDiv().innerHTML = html)); + export function dateToString(date: Date): string { return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`; } From 52309dda5994762a92e89ed162989845c253f9f3 Mon Sep 17 00:00:00 2001 From: SvenPf Date: Wed, 26 Jul 2023 18:05:47 +0200 Subject: [PATCH 2/3] fix issue where date formatting was wrong if machine's locale is anything other then 'en' or 'gb' --- src/utils/DateFormatter.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/utils/DateFormatter.ts b/src/utils/DateFormatter.ts index eac1761..6f85656 100644 --- a/src/utils/DateFormatter.ts +++ b/src/utils/DateFormatter.ts @@ -6,12 +6,12 @@ const moment = obsidian.moment; export class DateFormatter { toFormat: string; - localeString: string; + locale: string; constructor() { this.toFormat = 'YYYY-MM-DD'; - // get locale string (e.g. en, en-gb, de, fr, etc.) - this.localeString = new Intl.DateTimeFormat().resolvedOptions().locale; + // get locale of this machine (e.g. en, en-gb, de, fr, etc.) + this.locale = new Intl.DateTimeFormat().resolvedOptions().locale; } setFormat(format: string): void { @@ -20,13 +20,12 @@ export class DateFormatter { getPreview(format?: string): string { const today = moment(); - today.locale(this.localeString); if (!format) { format = this.toFormat; } - return today.format(format); + return today.locale(this.locale).format(format); } /** @@ -36,9 +35,11 @@ export class DateFormatter { * @param dateString the date string to be formatted * @param dateFormat the current format of `dateString`. When this is `null` and the actual format of the * given date string is not `C2822` or `ISO` format, this function will try to guess the format by using the native `Date` module. + * @param locale the locale of `dateString`. This is needed when `dateString` includes a month or day name and its locale format differs + * from the locale of this machine. * @returns formatted date string or null if `dateString` is not a valid date */ - format(dateString: string, dateFormat?: string): string | null { + format(dateString: string, dateFormat?: string, locale: string = 'en'): string | null { if (!dateString) { return null; } @@ -47,6 +48,7 @@ export class DateFormatter { if (!dateFormat) { // reading date formats other then C2822 or ISO with moment is deprecated + // see https://momentjs.com/docs/#/parsing/string/ if (this.hasMomentFormat(dateString)) { // expect C2822 or ISO format date = moment(dateString); @@ -54,13 +56,12 @@ export class DateFormatter { // try to read date string with native Date date = moment(new Date(dateString)); } - date.locale(this.localeString); // set local locale definition for moment } else { - date = moment(dateString, dateFormat, this.localeString); + date = moment(dateString, dateFormat, locale); } // format date (if it is valid) - return date.isValid() ? date.format(this.toFormat) : null; + return date.isValid() ? date.locale(this.locale).format(this.toFormat) : null; } private hasMomentFormat(dateString: string): boolean { From 78c7dd8b7d3bb89b178e2873189cb1d6a0b315d8 Mon Sep 17 00:00:00 2001 From: spozer Date: Mon, 28 Aug 2023 17:38:24 +0200 Subject: [PATCH 3/3] fix for requested changes - uses textContent instead of innterHTML to display date preview in settings panel, also prefixed element id with media-db --- src/settings/Settings.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index c53a46e..34b2a7b 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -172,7 +172,7 @@ export class MediaDbSettingTab extends PluginSettingTab { .setName('Date format') .setDesc(fragWithHTML("Your custom date format. Use \'YYYY-MM-DD\' for example.
" + "For more syntax, refer to format reference.
" + - "Your current syntax looks like this: " + + "Your current syntax looks like this: " + this.plugin.dateFormatter.getPreview() + "")) .addText(cb => { cb.setPlaceholder(DEFAULT_SETTINGS.customDateFormat) @@ -180,7 +180,7 @@ export class MediaDbSettingTab extends PluginSettingTab { .onChange(data => { const newDateFormat = data ? data : DEFAULT_SETTINGS.customDateFormat; this.plugin.settings.customDateFormat = newDateFormat; - document.getElementById('dateformat-preview').innerHTML = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview + document.getElementById('media-db-dateformat-preview').textContent = this.plugin.dateFormatter.getPreview(newDateFormat); // update preview this.plugin.saveSettings(); }); });