diff --git a/ddb.js b/ddb.js index 5badd70..494a7b3 100644 --- a/ddb.js +++ b/ddb.js @@ -1,4 +1,4 @@ -const { net, session, app, dialog, BrowserWindow, ipcMain } = require('electron') +const { net, session, app, dialog, BrowserWindow, ipcMain, Notification } = require('electron') const qs = require('querystring') const ProgressBar = require('electron-progressbar') const Slugify = require('slugify') @@ -43,11 +43,17 @@ function sanitize(text,rulesdata=null) { } } if (elem?.attribs?.href) { - elem.attribs.href=elem.attribs.href.replace(/^\/(monsters|spells|armor|weapons|adventuring-gear|magic-items)\//,(_,p1)=>{ + elem.attribs.href=elem.attribs.href.replace(/^\/(monsters|spells|armor|weapons|adventuring-gear|magic-items)\/(?:([0-9]+)(?:-.*)?)?/,(_,p1,p2)=>{ switch(p1) { - case "monsters": return "/monster/"; break; - case "spells": return "/spell/"; break; - default: return "/item/"; break; + case "monsters": + return `/monster/${(p2)?uuid5(`ddb://${p1}/${p2}`,uuid5.URL):''}`; + break; + case "spells": + return `/spell/${(p2)?uuid5(`ddb://${p1}/${p2}`,uuid5.URL):''}`; + break; + default: + return `/item/${(p2)?uuid5(`ddb://${p1}/${p2}`,uuid5.URL):''}`; + break; } }) } @@ -679,6 +685,10 @@ class DDB { zip.writeZip(filename) prog.detail = `Saved campaign` setTimeout(()=>prog.setCompleted(),1000) + if (Notification.isSupported()) { + const notification = new Notification({title: "Export Complete", body: `Encounters exported to ${filename}`}) + notification.show() + } } return campaign } @@ -898,6 +908,10 @@ class DDB { zip.writeZip(filename) prog.detail = `Saved compendium` setTimeout(()=>prog.setCompleted(),1000) + if (Notification.isSupported()) { + const notification = new Notification({title: "Export Complete", body: `Compendium exported to ${filename}`}) + notification.show() + } } return compendium } @@ -924,7 +938,7 @@ class DDB { { code: "$", names: [ "wealth","gemstone" ] }, ] const apiurl = "https://character-service.dndbeyond.com/character/v4/game-data/items" - const params = qs.stringify({ 'sharingSetting': 2 }) + const params = (this.gameId)? qs.stringify({ 'sharingSetting': 2, 'campaignId': this.gameId }) : qs.stringify({ 'sharingSetting': 2 }) await this.getCobaltAuth() const response = await this.getRequest(`${apiurl}?${params}`,true).catch((e)=>console.log(`Error getting items: ${e}`)) if (response?.data) { @@ -1102,6 +1116,10 @@ class DDB { prog.detail = `Saved compendium` setTimeout(()=>prog.setCompleted(),1000) console.log("Wrote compendium") + if (Notification.isSupported()) { + const notification = new Notification({title: "Export Complete", body: `Compendium exported to ${filename}`}) + notification.show() + } } return compendium } @@ -1207,6 +1225,13 @@ class DDB { prog.detail = `Retrieved ${monsters.length}/${count}...` } monsters = monsters.sort((a,b)=>a.name.normalize().localeCompare(b.name.normalize())) + console.log(monsters.length,this.legacy) + if (this.legacy == "uselegacy") { + monsters = monsters.filter(m=>m.isLegacy||!monsters.find(up=>up.isLegacy&&up.name==m.name)) + } else if (this.legacy == "useupdated") { + monsters = monsters.filter(m=>!m.isLegacy||!monsters.find(up=>!up.isLegacy&&up.name==m.name)) + } + console.log(monsters.length) for (const monster of monsters) { if (!monster.isReleased&&!monster.isHomebrew) { prog.value += (!filename)? (15*(1/count)) : 1 @@ -1243,6 +1268,10 @@ class DDB { prog.detail = `Saved compendium` console.log("Saved compendium.") setTimeout(()=>prog.setCompleted(),1000) + if (Notification.isSupported()) { + const notification = new Notification({title: "Export Complete", body: `Compendium exported to ${filename}`}) + notification.show() + } } return compendium } @@ -1252,8 +1281,8 @@ class DDB { _name: "monster", _attrs: { id: uuid5(`ddb://monsters/${monster.id}`,uuid5.URL) }, _content: [ - {name: monster.name}, - {slug: slugify(monster.name)}, + {name: (monster.isLegacy&&this.legacy=='mark')? `${monster.name} [Legacy]` : monster.name}, + {slug: slugify((monster.isLegacy&&this.legacy=='mark')? `${monster.name} [Legacy]` : monster.name)}, {size: this.ruledata.creatureSizes.find(s=>s.id===monster.sizeId).name.charAt(0).toUpperCase()}, {alignment: this.ruledata.alignments.find(s=>s.id===monster.alignmentId)?.name||monster.alignmentId}, {ac: `${monster.armorClass} ${monster.armorClassDescription}`}, @@ -3199,6 +3228,10 @@ function doSearch(el,resId) { zip.writeZip() prog.detail = "Module saved." setTimeout(()=>prog.setCompleted(),1000) + if (Notification.isSupported()) { + const notification = new Notification({title: "Export Complete", body: `Module exported to ${filename}`}) + notification.show() + } }) }) console.log("Closing database") diff --git a/index.js b/index.js index 128d86d..a878dba 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,4 @@ -const {app, session, BrowserWindow, ipcMain, net, Menu, MenuItem, dialog, shell} = require('electron') +const {app, session, BrowserWindow, ipcMain, net, Menu, MenuItem, dialog, shell, Notification} = require('electron') const ElectronPreferences = require('electron-preferences') const WebSocket = require('ws') const path = require('path') @@ -61,6 +61,7 @@ const preferences = new ElectronPreferences({ }, 'export': { 'art': [ 'artwork', 'tokens' ], + 'legacy': 'mark', 'maps': 'nomaps', 'mapsloc': 'group', } @@ -124,6 +125,16 @@ const preferences = new ElectronPreferences({ { 'label': 'Tokens', 'value': 'tokens' }, ] }, + { + 'label': "Legacy Monsters", + 'key': 'legacy', + 'type': 'radio', + 'options': [ + { 'label': 'Prefer Legacy Monsters', 'value': 'uselegacy' }, + { 'label': 'Prefer Updated Monsters', 'value': 'useupdated' }, + { 'label': 'Keep Both and Mark Legacy Monsters', 'value': 'mark' }, + ] + }, { 'label': "Attempt to create maps", 'key': 'maps', @@ -253,6 +264,7 @@ app.on('ready', () => { ddb.art = preferences.value('export.art'); ddb.maps = preferences.value('export.maps') ?? "nomaps"; ddb.mapsloc = preferences.value('export.mapsloc') ?? "group"; + ddb.legacy = preferences.value('export.legacy') ?? "mark"; console.log(ddb.art,ddb.maps) preferences.on('save', (preferences) => { @@ -262,6 +274,7 @@ app.on('ready', () => { ddb.art = preferences.export.art ddb.maps = preferences.export.maps ddb.mapsloc = preferences.export.mapsloc + ddb.legacy = preferences.export.legacy }) var menu = Menu.buildFromTemplate([ { @@ -284,6 +297,11 @@ app.on('ready', () => { { label: `${app.getName()} v${app.getVersion()}`, enabled: false }, { label: "About", click: () => shell.openExternal("https://github.com/rrgeorge/EncounterLog") }, { label: "Support this project", click: () => shell.openExternal("https://github.com/sponsors/rrgeorge") }, + { label: "Refresh menus", + click: ()=>{ + populateCampaignMenu() + populateCompendiumMenu() + }}, { 'click': function() { fs.rm(path.join(app.getPath("cache"),app.getName(),"imagecache"),{recursive: true},(e)=>{ @@ -334,8 +352,10 @@ app.on('ready', () => { (() => new Promise(resolve => setTimeout(resolve, 500)))().then(()=> ddb.getUserData() .then(()=>{ - updateManifest().then(()=> - populateCompendiumMenu() + updateManifest().then(()=>{ + populateCampaignMenu() + populateCompendiumMenu() + } ) }) .catch(e=>{ @@ -403,9 +423,7 @@ app.on('ready', () => { _ws.close(1001,"Going away") } if (win.webContents.getURL().match(/dndbeyond.com\/my-campaigns/)) { - (() => new Promise(resolve => setTimeout(resolve, 500)))().then(()=> - ddb.userId && populateCampaignMenu() - ) + ddb.userId && populateCampaignMenu() } }) } @@ -425,6 +443,7 @@ app.on('ready', () => { console.log(`EncounterPlus URL: ${encounterhost}`) } win.loadURL('https://www.dndbeyond.com/my-campaigns',{httpReferrer: "https://www.dndbeyond.com"}) + if (Notification.isSupported()) new Notification() }); function displayError(e) { _win.loadURL("http://www.dndbeyond.com/my-campaigns",{httpReferrer: "https://www.dndbeyond.com"}) diff --git a/package.json b/package.json index 347508f..a998120 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "encounterlog", "productName": "EncounterLog", - "version": "2.6.2", + "version": "2.6.3", "repository": { "type": "git", "url": "https://github.com/rrgeorge/encounterlog"