Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(journal): add setting to create folders based on kanka tree stru… #24

Merged
merged 7 commits into from
Nov 26, 2020
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ This module should warn you starting a week before it expires.
- **Campaign**: After entering a valid Access Token you should receive a list of all Kanka campaigns you have access
to. You must select the campaign you would like to import data from. You can always change the campaign later to import
entries from another campaign without loosing what you have already imported.
- **Create folder tree**: Most entries in Kanka can be organized hierarchically. If this option is selected the module
will create folders to replicate this hierarchy. Foundry has a limit of 3 folders levels, thus everything on a lower
level will be flattened to this 3rd level instead.
- **Include image in text**: When importing an entry from Kanka it will use the entries main image as the journal
entries image. With this setting the image will additionally be displayed in the journal entry next to its text
(see screenshot bellow).
Expand All @@ -36,8 +39,10 @@ be refreshed from Kanka until this setting has been enabled again.
- **Basic metadata import**: This setting allows you to define which *basic* metadata should be imported. *Basic*
metadata includes properties like the *type* given to most entries in Kanka, *age* for characters and other basic
properties.
- **Attributes import**: The module will import attributes on any Kanka entry as metadata. This settings allows you to
- **Attributes import**: The module will import attributes on any Kanka entry as metadata. This setting allows you to
broadly define which attributes to import this way.
- **Inventory import**: The module will import inventory for all entries as metadata. This setting allows you to control
which inventory entries will be imported.
- **Character trait import**: Characters in Kanka have appearance and personality traits. This setting allows you to
select which of those traits you would like to import.
- **Quest reference import**: Quests can contain references to Characters, Locations and other entries. This setting
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/renderJournalSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default async function renderJournalSheet(
// Workaround for missing locale in links
const parsedContent = contentElement.html().replace(
'https://kanka.io/campaign',
`https://kanka.io/${campaign.locale}/campaign`,
`https://kanka.io/${campaign.locale ?? 'en'}/campaign`,
);

contentElement.html(parsedContent);
Expand Down
8 changes: 8 additions & 0 deletions src/kanka/entities/Ability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export default class Ability extends PrimaryEntity<AbilityData, Campaign> {
return EntityType.ability;
}

get treeParentId(): number | undefined {
return this.data.ability_id;
}

async treeParent(): Promise<Ability | undefined> {
return this.findReference(this.parent.abilities(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
16 changes: 16 additions & 0 deletions src/kanka/entities/Campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export default class Campaign extends PrimaryEntity<CampaignData> {
return EntityType.campaign;
}

public abilities(): EntityCollection<Ability> {
return this.#abilities;
}

public characters(): EntityCollection<Character> {
return this.#characters;
}
Expand All @@ -39,6 +43,10 @@ export default class Campaign extends PrimaryEntity<CampaignData> {
return this.#items;
}

public journals(): EntityCollection<Journal> {
return this.#journals;
}

public locations(): EntityCollection<Location> {
return this.#locations;
}
Expand All @@ -55,6 +63,14 @@ export default class Campaign extends PrimaryEntity<CampaignData> {
return this.#families;
}

public quests(): EntityCollection<Quest> {
return this.#quests;
}

public notes(): EntityCollection<Note> {
return this.#notes;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public getByType(type: string): EntityCollection<any> | undefined {
switch (type) {
Expand Down
8 changes: 8 additions & 0 deletions src/kanka/entities/Family.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ export default class Family extends PrimaryEntity<FamilyData, Campaign> {
return EntityType.family;
}

get treeParentId(): number | undefined {
return this.data.family_id;
}

async treeParent(): Promise<Family | undefined> {
return this.findReference(this.parent.families(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
5 changes: 5 additions & 0 deletions src/kanka/entities/InventoryItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default class InventoryItem extends EntityBase<KankaInventory, PrimaryEnt
return this.data.position;
}

get description(): string | undefined {
return this.data.description;
}

get amount(): number | undefined {
return this.data.amount;
}
Expand All @@ -25,6 +29,7 @@ export default class InventoryItem extends EntityBase<KankaInventory, PrimaryEnt
}

async item(): Promise<Item | undefined> {
if (!this.data.item_id) return undefined;
return this.findReference(this.parent.parent.items(), this.data.item_id);
}
}
8 changes: 8 additions & 0 deletions src/kanka/entities/Journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export default class Journal extends PrimaryEntity<JournalData, Campaign> {
return EntityType.journal;
}

get treeParentId(): number | undefined {
return this.data.journal_id;
}

async treeParent(): Promise<Journal | undefined> {
return this.findReference(this.parent.journals(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
8 changes: 8 additions & 0 deletions src/kanka/entities/Location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ export default class Location extends PrimaryEntity<LocationData, Campaign> {
return this.data.type;
}

get treeParentId(): number | undefined {
return this.data.parent_location_id;
}

async treeParent(): Promise<Location | undefined> {
return this.findReference(this.parent.locations(), this.treeParentId);
}

protected async buildMetaData(): Promise<void> {
await super.buildMetaData();
this.addMetaData({ label: 'type', value: this.type });
Expand Down
8 changes: 8 additions & 0 deletions src/kanka/entities/Note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export default class Note extends PrimaryEntity<NoteData, Campaign> {
return EntityType.note;
}

get treeParentId(): number | undefined {
return this.data.note_id;
}

async treeParent(): Promise<Note | undefined> {
return this.findReference(this.parent.notes(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
8 changes: 8 additions & 0 deletions src/kanka/entities/Organisation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ export default class Organisation extends PrimaryEntity<OrganisationData, Campai
return EntityType.organisation;
}

get treeParentId(): number | undefined {
return this.data.organisation_id;
}

async treeParent(): Promise<Organisation | undefined> {
return this.findReference(this.parent.organisations(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
29 changes: 25 additions & 4 deletions src/kanka/entities/PrimaryEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ export default abstract class PrimaryEntity<

abstract get entityType(): EntityType;

get treeParentId(): number | undefined {
return undefined;
}

async treeParent(): Promise<PrimaryEntity<T, P> | undefined> {
return undefined;
}

async treeAncestors(): Promise<PrimaryEntity<T, P>[]> {
const parent = await this.treeParent();
if (!parent) return [];
const path = await parent.treeAncestors();
return [...path, parent];
}

public get attributes(): EntityAttribute[] {
return this.#attributes;
}
Expand All @@ -42,7 +57,7 @@ export default abstract class PrimaryEntity<

public get image(): string | undefined {
if (this.data.has_custom_image === false) return undefined;
return `https://kanka-user-assets.s3.eu-central-1.amazonaws.com/${this.data.image}`;
return this.data.image_full;
}

public get entry(): string {
Expand Down Expand Up @@ -94,14 +109,20 @@ export default abstract class PrimaryEntity<

this.inventory
.forEach((inventory, index) => {
const label = `${inventory.amount} &times; ${items[index]?.name ?? inventory.name}`;
const value: string[] = [];

if (inventory.isEquipped) value.push('<i class="fas fa-check-circle"></i>');
if (inventory.description) value.push(`<em>${inventory.description}</em>`);

this.addMetaData({
label,
type: MetaDataType.inventory,
section: inventory.position || 'inventory',
label: items[index]?.name,
value: `${inventory.amount ? `${inventory.amount} &times; ` : ''}${inventory.name}${inventory.isEquipped ? '*' : ''}`,
value: value.join(' '),
originalData: inventory,
linkTo: items[index],
});
}, true);
});
}

Expand Down
14 changes: 11 additions & 3 deletions src/kanka/entities/Quest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export default class Quest extends PrimaryEntity<QuestData, Campaign> {
return EntityType.quest;
}

get treeParentId(): number | undefined {
return this.data.quest_id;
}

async treeParent(): Promise<Quest | undefined> {
return this.findReference(this.parent.quests(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down Expand Up @@ -71,8 +79,8 @@ export default class Quest extends PrimaryEntity<QuestData, Campaign> {
await Promise.all([
this.addQuestReferenceMetaData(this.#characters.all(), 'characters'),
this.addQuestReferenceMetaData(this.#locations.all(), 'locations'),
// this.addQuestReferenceMetaData(this.#items.all(), 'items'),
// this.addQuestReferenceMetaData(this.#organisations.all(), 'organisations'),
this.addQuestReferenceMetaData(this.#items.all(), 'items'),
this.addQuestReferenceMetaData(this.#organisations.all(), 'organisations'),
]);
}

Expand All @@ -90,6 +98,6 @@ export default class Quest extends PrimaryEntity<QuestData, Campaign> {
type: MetaDataType.questReference,
originalData: reference,
linkTo: entities[index],
}));
}, true));
}
}
8 changes: 8 additions & 0 deletions src/kanka/entities/Race.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export default class Race extends PrimaryEntity<RaceData, Campaign> {
return EntityType.race;
}

get treeParentId(): number | undefined {
return this.data.race_id;
}

async treeParent(): Promise<Race | undefined> {
return this.findReference(this.parent.races(), this.treeParentId);
}

public get type(): string | undefined {
return this.data.type;
}
Expand Down
4 changes: 4 additions & 0 deletions src/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,17 @@
"KANKA.SettingsMetaDataInventoryVisibility.value.none": "Keine Inventareinträge",
"KANKA.SettingsImageInText.label": "Bild neben Text",
"KANKA.SettingsImageInText.hint": "Soll das Bild eines Eintrags neben dem Text angezeigt werden?",
"KANKA.SettingsKeepTreeStructure.label": "Ordnerstruktur nachbilden",
"KANKA.SettingsKeepTreeStructure.hint": "Soll Kankas Baumstruktur über Ordner nachgestellt werden? Die maximale Tiefe beträgt 3 Ordner.",
"KANKA.SettingsEntityTypeVisibility.hint": "Soll diese Kategorie geladen werden? Eine reduzierte Anzahl von Kategorien reduziert auch die Anzahl der Anfragen an Kanka und kann somit die Ladezeiten verbessern.",
"KANKA.BrowserLinkFolder": "Alle verbinden",
"KANKA.BrowserRefreshFolder": "Verbundene aktualisieren",
"KANKA.BrowserNotificationSyncedFolder": "Alle Kanka-Einträge vom Typ {type} wurden aktualisert.",
"KANKA.BrowserNotificationLinkedFolder": "Alle Kanka-Einträge vom Typ {type} wurden verbunden.",
"KANKA.BrowserNotificationSynced": "Eintrag {name} wurde verbunden.",
"KANKA.BrowserNotificationRefreshed": "Eintrag {name} wurde aktualisiert.",
"KANKA.BrowserSyncError": "Unerwarteter Fehler beim aktualisieren.",
"KANKA.JournalFolder.root": "Kanka",
"KANKA.EntityType.ability": "Fähigkeiten",
"KANKA.EntityType.character": "Charaktere",
"KANKA.EntityType.family": "Familien",
Expand Down
5 changes: 4 additions & 1 deletion src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,22 @@
"KANKA.SettingsMetaDataQuestReferenceVisibility.value.public": "All public quest references",
"KANKA.SettingsMetaDataQuestReferenceVisibility.value.none": "No quest references",
"KANKA.SettingsMetaDataInventoryVisibility.label": "Inventory import",
"KANKA.SettingsMetaDataInventoryVisibility.hint": "Which inventory entries should be imported as metadata.",
"KANKA.SettingsMetaDataInventoryVisibility.value.all": "All inventory entries",
"KANKA.SettingsMetaDataInventoryVisibility.value.public": "All public inventory entries",
"KANKA.SettingsMetaDataInventoryVisibility.value.none": "No inventory entries",
"KANKA.SettingsImageInText.label": "Include image in text",
"KANKA.SettingsImageInText.hint": "Should the image be include next to the text in addition to the regular journal entry image?",
"KANKA.SettingsKeepTreeStructure.label": "Create folder tree",
"KANKA.SettingsKeepTreeStructure.hint": "Should Kankas tree structure be replicated via folders? The maximum depth is 3 folders.",
"KANKA.SettingsEntityTypeVisibility.hint": "Should this type of entity be loaded? Reducing the types of entity also reduces the number of requests made to Kanka and thus improves the performance.",
"KANKA.BrowserLinkFolder": "Link all",
"KANKA.BrowserRefreshFolder": "Refresh linked",
"KANKA.BrowserNotificationSyncedFolder": "Refreshed all Kanka entities of type {type}.",
"KANKA.BrowserNotificationLinkedFolder": "Linked all Kanka entities of type {type}.",
"KANKA.BrowserNotificationSynced": "Linked entity {name}.",
"KANKA.BrowserNotificationRefreshed": "Updated entity {name}.",
"KANKA.BrowserSyncError": "Unexpected error while syncing.",
"KANKA.JournalFolder.root": "Kanka",
"KANKA.EntityType.ability": "Abilities",
"KANKA.EntityType.character": "Characters",
"KANKA.EntityType.family": "Families",
Expand Down
21 changes: 13 additions & 8 deletions src/module/KankaBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import moduleConfig from '../module.json';
import EntityType from '../types/EntityType';
import { kankaImportTypeSetting, KankaSettings } from '../types/KankaSettings';
import getSetting from './getSettings';
import { ensureJournalFolder, findEntriesByType, findEntryByEntity, findEntryByEntityId, writeJournalEntry } from './journal';
import { findEntriesByType, findEntryByEntity, findEntryByEntityId, writeJournalEntry } from './journal';

interface EntityList {
items: PrimaryEntity[];
Expand Down Expand Up @@ -219,6 +219,7 @@ export default class KankaBrowser extends Application {
const entity = await campaign.getByType(type)?.byId(Number(id));
if (!entity) return;
await this.syncEntity(entity, action === 'link-entry');
this.render();
break;
}

Expand All @@ -232,11 +233,13 @@ export default class KankaBrowser extends Application {
case 'sync-folder':
if (!type) return;
await this.syncFolder(type);
this.render();
break;

case 'link-folder': {
if (!type) return;
await this.linkFolder(type);
this.render();
break;
}

Expand All @@ -255,10 +258,8 @@ export default class KankaBrowser extends Application {
return;
}

await ensureJournalFolder(type);

const linkedEntities = entities.filter(entity => !!findEntryByEntity(entity));
await Promise.all(linkedEntities.map(entity => this.syncEntity(entity, false, false)));
await this.syncEntities(linkedEntities);
this.showInfo('BrowserNotificationSyncedFolder', { type });
this.render();
}
Expand All @@ -271,17 +272,21 @@ export default class KankaBrowser extends Application {
return;
}

await ensureJournalFolder(type);

const unlinkedEntities = entities.filter(entity => !findEntryByEntity(entity));
await Promise.all(unlinkedEntities.map(entity => this.syncEntity(entity, false, false)));
await this.syncEntities(unlinkedEntities);
this.showInfo('BrowserNotificationLinkedFolder', { type });
this.render();
}

private async syncEntities(entities: PrimaryEntity[]): Promise<void> {
for (let i = 0; i < entities.length; i += 1) {
// eslint-disable-next-line no-await-in-loop
await this.syncEntity(entities[i], false, false);
}
}

private async syncEntity(entity: PrimaryEntity, renderSheet = false, notification = true): Promise<void> {
await writeJournalEntry(entity, { renderSheet, notification });
this.render();
}

private showInfo(msg: string, params?: Record<string, unknown>): void {
Expand Down
Loading