feat: notes de bord et sauvegarde position calendrier
- bouton crayon sur la fenêtre calendrier (MJ) → dialogue de note - date impériale + date réelle préremplies - append au journal configuré (créé auto si inexistant) - appendJournalNote : labels passés depuis l'ouverture (pas de re-lecture) - échappement XSS via jQuery .text().html() - onSave async + await + try/catch - sauvegarde position calendrier (client scope, debounced 300ms) - restore position au constructeur - close() async avec await settings.set - boutons note et config groupés dans .mgt2-cal-actions (flex row)
This commit is contained in:
+35
-2
@@ -22,10 +22,16 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
},
|
||||
};
|
||||
|
||||
constructor({ onConfig, onClose } = {}) {
|
||||
constructor({ onConfig, onClose, onNote } = {}) {
|
||||
super();
|
||||
const saved = game.settings?.get(MODULE_ID, 'calendarPosition');
|
||||
if (saved?.left != null && saved?.top != null) {
|
||||
this.position.left = saved.left;
|
||||
this.position.top = saved.top;
|
||||
}
|
||||
this._onConfig = onConfig;
|
||||
this._onClose = onClose;
|
||||
this._onNote = onNote;
|
||||
this._data = { year: 1116, day: 1, hour: 12, minute: 0, planet: '' };
|
||||
}
|
||||
|
||||
@@ -46,6 +52,26 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
html.find('.mgt2-cal-config-btn').on('click', () => {
|
||||
this._onConfig?.();
|
||||
});
|
||||
html.find('.mgt2-cal-note-btn').on('click', () => {
|
||||
this._onNote?.();
|
||||
});
|
||||
}
|
||||
|
||||
setPosition(position) {
|
||||
const result = super.setPosition(position);
|
||||
this._debouncedSavePos();
|
||||
return result;
|
||||
}
|
||||
|
||||
_debouncedSavePos() {
|
||||
if (this._savePosTimeout) clearTimeout(this._savePosTimeout);
|
||||
this._savePosTimeout = setTimeout(() => {
|
||||
game.settings?.set(MODULE_ID, 'calendarPosition', {
|
||||
left: this.position.left,
|
||||
top: this.position.top,
|
||||
});
|
||||
this._savePosTimeout = null;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
updateDisplay(data) {
|
||||
@@ -53,8 +79,15 @@ export class CalendarApp extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
this.render();
|
||||
}
|
||||
|
||||
close() {
|
||||
async close() {
|
||||
if (game.settings) {
|
||||
await game.settings.set(MODULE_ID, 'calendarPosition', {
|
||||
left: this.position.left,
|
||||
top: this.position.top,
|
||||
});
|
||||
}
|
||||
this._onConfig = null;
|
||||
this._onNote = null;
|
||||
this._onClose?.();
|
||||
this._onClose = null;
|
||||
return super.close();
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
|
||||
export class JournalNoteDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'mgt2-journal-note',
|
||||
classes: ['mgt2-calendar'],
|
||||
position: { width: 320, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fas fa-pen',
|
||||
title: 'Note de bord',
|
||||
resizable: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
main: {
|
||||
template: `modules/${MODULE_ID}/templates/journal-note-dialog.hbs`,
|
||||
root: true,
|
||||
},
|
||||
};
|
||||
|
||||
constructor({ imperialDate, realDate, onSave } = {}) {
|
||||
super();
|
||||
this._imperialDate = imperialDate;
|
||||
this._realDate = realDate;
|
||||
this._onSave = onSave;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'Note de bord';
|
||||
}
|
||||
|
||||
async _prepareContext() {
|
||||
return {
|
||||
imperialDate: this._imperialDate,
|
||||
realDate: this._realDate,
|
||||
};
|
||||
}
|
||||
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
const html = $(this.element);
|
||||
|
||||
html.find('[data-action="save"]').on('click', async () => {
|
||||
const content = html.find('[name="content"]').val()?.trim();
|
||||
try {
|
||||
await this._onSave?.(content);
|
||||
} catch (err) {
|
||||
console.error(`${MODULE_ID} | Erreur sauvegarde note:`, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this._onSave = null;
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@@ -93,3 +93,13 @@ export function formatImperialDate({ year, day, hour, minute, planet } = {}) {
|
||||
export function getDefaultCalendar() {
|
||||
return { year: 1116, day: 1, hour: 12, minute: 0, planet: '' };
|
||||
}
|
||||
|
||||
export function formatRealDate() {
|
||||
const d = new Date();
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const h = String(d.getHours()).padStart(2, '0');
|
||||
const min = String(d.getMinutes()).padStart(2, '0');
|
||||
return `${day}/${m}/${y} ${h}:${min}`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CalendarApp } from './CalendarApp.js';
|
||||
import { CalendarConfigDialog } from './CalendarConfigDialog.js';
|
||||
import { getDefaultCalendar } from './calendarDate.js';
|
||||
import { JournalNoteDialog } from './JournalNoteDialog.js';
|
||||
import { getDefaultCalendar, formatImperialDate, formatRealDate } from './calendarDate.js';
|
||||
|
||||
const MODULE_ID = 'mgt2-compendium-amiral-denisov';
|
||||
const ChatLogV2 = foundry.applications.sidebar.tabs.ChatLog;
|
||||
@@ -47,6 +48,7 @@ function openCalendarApp() {
|
||||
const data = readCalendar();
|
||||
_calendarApp = new CalendarApp({
|
||||
onConfig: () => openCalendarConfig(),
|
||||
onNote: () => openJournalNote(),
|
||||
onClose: () => { _calendarApp = null; },
|
||||
});
|
||||
_calendarApp.updateDisplay(data);
|
||||
@@ -67,6 +69,55 @@ function openCalendarConfig() {
|
||||
dialog.render({ force: true });
|
||||
}
|
||||
|
||||
async function appendJournalNote(content, { dateLabel, realLabel } = {}) {
|
||||
if (!content) return false;
|
||||
const journalName = game.settings.get(MODULE_ID, 'calendarJournalName') || 'Journal de bord';
|
||||
let journal = game.journal.getName(journalName);
|
||||
if (!journal) {
|
||||
journal = await JournalEntry.create({ name: journalName });
|
||||
if (!journal) {
|
||||
ui.notifications.error('Impossible de créer le journal');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!dateLabel || !realLabel) {
|
||||
const c = readCalendar();
|
||||
dateLabel = formatImperialDate(c);
|
||||
realLabel = formatRealDate();
|
||||
}
|
||||
const safeContent = $('<div>').text(content).html().replace(/\n/g, '<br>');
|
||||
const noteHtml = `<hr>
|
||||
<h3>${realLabel} — ${dateLabel}</h3>
|
||||
<p>${safeContent}</p>`;
|
||||
let page = journal.pages.contents[0];
|
||||
if (!page) {
|
||||
[page] = await journal.createEmbeddedDocuments('JournalEntryPage', [{ name: 'Notes', text: { content: '' } }]);
|
||||
}
|
||||
if (!page) {
|
||||
ui.notifications.error('Impossible de créer une page dans le journal');
|
||||
return false;
|
||||
}
|
||||
const existing = page.text?.content || '';
|
||||
await page.update({ 'text.content': existing + '\n' + noteHtml });
|
||||
ui.notifications.info(`Note ajoutée au journal « ${journalName} »`);
|
||||
return true;
|
||||
}
|
||||
|
||||
function openJournalNote() {
|
||||
const calendar = readCalendar();
|
||||
const dateLabel = formatImperialDate(calendar);
|
||||
const realLabel = formatRealDate();
|
||||
const dialog = new JournalNoteDialog({
|
||||
imperialDate: dateLabel,
|
||||
realDate: realLabel,
|
||||
onSave: async (content) => {
|
||||
const ok = await appendJournalNote(content, { dateLabel, realLabel });
|
||||
if (ok) dialog.close();
|
||||
},
|
||||
});
|
||||
dialog.render({ force: true });
|
||||
}
|
||||
|
||||
function registerCalCommand() {
|
||||
if (!ChatLogV2?.CHAT_COMMANDS) return;
|
||||
ChatLogV2.CHAT_COMMANDS.cal = {
|
||||
@@ -125,12 +176,27 @@ Hooks.once('init', () => {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarJournalName', {
|
||||
name: 'Journal de bord',
|
||||
hint: 'Nom du journal utilisé pour les notes de session. Créé automatiquement s\'il n\'existe pas.',
|
||||
scope: 'world',
|
||||
config: true,
|
||||
type: String,
|
||||
default: 'Journal de bord',
|
||||
});
|
||||
game.settings.register(MODULE_ID, 'calendarPosition', {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: Object,
|
||||
default: {},
|
||||
});
|
||||
|
||||
const loadTemplatesFn = foundry.applications?.handlebars?.loadTemplates || loadTemplates;
|
||||
if (loadTemplatesFn) {
|
||||
loadTemplatesFn([
|
||||
`modules/${MODULE_ID}/templates/calendar-app.hbs`,
|
||||
`modules/${MODULE_ID}/templates/calendar-config.hbs`,
|
||||
`modules/${MODULE_ID}/templates/journal-note-dialog.hbs`,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user