Skip to content

Commit

Permalink
Merge pull request #619 from wowsims/feature/importer-fixes
Browse files Browse the repository at this point in the history
Fix 60U reforges & add WSE import warning
  • Loading branch information
rosenrusinov authored Jun 7, 2024
2 parents 5e57377 + 161ee04 commit 50172d2
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 90 deletions.
181 changes: 111 additions & 70 deletions ui/core/components/importers.ts → ui/core/components/importers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JsonObject } from '@protobuf-ts/runtime';
import { default as pako } from 'pako';
import { ref } from 'tsx-vanilla';

import { IndividualSimUI } from '../individual_sim_ui';
import { Class, EquipmentSpec, Glyphs, ItemSlot, ItemSpec, Profession, Race, Spec } from '../proto/common';
Expand All @@ -26,40 +27,46 @@ export abstract class Importer extends BaseModal {
this.includeFile = includeFile;
const uploadInputId = 'upload-input-' + title.toLowerCase().replaceAll(' ', '-');

this.body.innerHTML = `
<div class="import-description"></div>
<textarea spellCheck="false" class="importer-textarea form-control"></textarea>
`;
this.footer!.innerHTML = `
${
this.includeFile
? `
<label for="${uploadInputId}" class="importer-button btn btn-primary upload-button me-2">
<i class="fas fa-file-arrow-up"></i>
Upload File
</label>
<input type="file" id="${uploadInputId}" class="importer-upload-input d-none" hidden>
`
: ''
}
<button class="importer-button btn btn-primary import-button">
<i class="fa fa-download"></i>
Import
</button>
`;

this.textElem = this.rootElem.getElementsByClassName('importer-textarea')[0] as HTMLTextAreaElement;
this.descriptionElem = this.rootElem.getElementsByClassName('import-description')[0] as HTMLElement;

if (this.includeFile) {
const uploadInput = this.rootElem.getElementsByClassName('importer-upload-input')[0] as HTMLButtonElement;
uploadInput.addEventListener('change', async event => {
const data: string = await (event as any).target.files[0].text();
this.textElem.textContent = data;
const descriptionElemRef = ref<HTMLDivElement>();
const textElemRef = ref<HTMLTextAreaElement>();
const importButtonRef = ref<HTMLButtonElement>();
const uploadInputRef = ref<HTMLInputElement>();

this.body.replaceChildren(
<>
<div ref={descriptionElemRef} className="import-description"></div>
<textarea ref={textElemRef} className="importer-textarea form-control" attributes={{ spellcheck: false }}></textarea>
</>,
);

this.footer!.appendChild(
<>
{this.includeFile && (
<>
<label htmlFor={uploadInputId} className="importer-button btn btn-primary upload-button me-2">
<i className="fas fa-file-arrow-up me-1"></i>
Upload File
</label>
<input ref={uploadInputRef} type="file" id={uploadInputId} className="importer-upload-input d-none" hidden />
</>
)}
<button ref={importButtonRef} className="importer-button btn btn-primary import-button">
<i className="fa fa-download me-1"></i>
Import
</button>
</>,
);

this.descriptionElem = descriptionElemRef.value!;
this.textElem = textElemRef.value!;

if (this.includeFile && uploadInputRef.value) {
uploadInputRef.value.addEventListener('change', async event => {
this.textElem.textContent = await (event as any).target.files[0].text();
});
}

this.importButton = this.rootElem.getElementsByClassName('import-button')[0] as HTMLButtonElement;
this.importButton = importButtonRef.value!;
this.importButton.addEventListener('click', async _event => {
try {
await this.onImport(this.textElem.value || '');
Expand Down Expand Up @@ -193,10 +200,12 @@ export class IndividualJsonImporter<SpecType extends Spec> extends Importer {
super(parent, simUI, 'JSON Import', true);
this.simUI = simUI;

this.descriptionElem.innerHTML = `
<p>Import settings from a JSON file, which can be created using the JSON Export feature.</p>
<p>To import, upload the file or paste the text below, then click, 'Import'.</p>
`;
this.descriptionElem.appendChild(
<>
<p>Import settings from a JSON file, which can be created using the JSON Export feature.</p>
<p>To import, upload the file or paste the text below, then click, 'Import'.</p>
</>,
);
}

async onImport(data: string) {
Expand All @@ -220,31 +229,33 @@ export class IndividualJsonImporter<SpecType extends Spec> extends Importer {
}
}

export class Individual80UImporter<SpecType extends Spec> extends Importer {
export class Individual60UImporter<SpecType extends Spec> extends Importer {
private readonly simUI: IndividualSimUI<SpecType>;
constructor(parent: HTMLElement, simUI: IndividualSimUI<SpecType>) {
super(parent, simUI, '60 Upgrades Cataclysm Import', true);
this.simUI = simUI;

this.descriptionElem.innerHTML = `
<p>
Import settings from <a href="https://sixtyupgrades.com/cata" target="_blank">60 Upgrades</a>.
</p>
<p>
This feature imports gear, race, and (optionally) talents. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.
</p>
<p>
To import, paste the output from the site's export option below and click, 'Import'.
</p>
`;
this.descriptionElem.appendChild(
<>
<p>
Import settings from{' '}
<a href="https://sixtyupgrades.com/cata" target="_blank">
60 Upgrades
</a>
.
</p>
<p>This feature imports gear, race, and (optionally) talents. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.</p>
<p>To import, paste the output from the site's export option below and click, 'Import'.</p>
</>,
);
}

async onImport(data: string) {
let importJson: any | null;
try {
importJson = JSON.parse(data);
} catch {
throw new Error('Please use a valid 80U export.');
throw new Error('Please use a valid 60U export.');
}

// Parse all the settings.
Expand Down Expand Up @@ -274,6 +285,10 @@ export class Individual80UImporter<SpecType extends Spec> extends Importer {
if (itemJson.gems) {
itemSpec.gems = (itemJson.gems as Array<any>).filter(gemJson => gemJson?.id).map(gemJson => gemJson.id);
}
if (itemJson.reforge?.id) {
itemSpec.reforging = itemJson.reforge.id;
}

equipmentSpec.items.push(itemSpec);
});

Expand All @@ -289,17 +304,19 @@ export class IndividualWowheadGearPlannerImporter<SpecType extends Spec> extends
super(parent, simUI, 'Wowhead Import', true);
this.simUI = simUI;

this.descriptionElem.innerHTML = `
<p>
Import settings from <a href="https://www.wowhead.com/cata/gear-planner" target="_blank">Wowhead Gear Planner</a>.
</p>
<p>
This feature imports gear, race, and (optionally) talents. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.
</p>
<p>
To import, paste the gear planner link below and click, 'Import'.
</p>
`;
this.descriptionElem.appendChild(
<>
<p>
Import settings from{' '}
<a href="https://www.wowhead.com/cata/gear-planner" target="_blank">
Wowhead Gear Planner
</a>
.
</p>
<p>This feature imports gear, race, and (optionally) talents. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.</p>
<p>To import, paste the gear planner link below and click, 'Import'.</p>
</>,
);
}

async onImport(url: string) {
Expand Down Expand Up @@ -475,17 +492,41 @@ export class IndividualAddonImporter<SpecType extends Spec> extends Importer {
super(parent, simUI, 'Addon Import', true);
this.simUI = simUI;

this.descriptionElem.innerHTML = `
<p>
Import settings from the <a href="https://www.curseforge.com/wow/addons/wowsimsexporter" target="_blank">WoWSims Importer In-Game Addon</a>.
</p>
<p>
This feature imports gear, race, talents, glyphs, and professions. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.
</p>
<p>
To import, paste the output from the addon below and click, 'Import'.
</p>
`;
const warningRef = ref<HTMLDivElement>();
this.descriptionElem.appendChild(
<>
<p>
Import settings from the{' '}
<a href="https://www.curseforge.com/wow/addons/wowsimsexporter" target="_blank">
WoWSims Importer In-Game Addon
</a>
.
</p>
<p>
This feature imports gear, race, talents, glyphs, and professions. It does NOT import buffs, debuffs, consumes, rotation, or custom stats.
</p>
<p>To import, paste the output from the addon below and click, 'Import'.</p>
<div ref={warningRef} />
</>,
);

if (warningRef.value)
new Toast({
title: 'Reforging issues',
body: (
<>
There are known issues with Reforging when using the WSE addon.
<br />
Always make sure to double check your reforges after importing.
</>
),
additionalClasses: ['toast-import-warning'],
container: warningRef.value,
variant: 'warning',
canClose: false,
autoShow: true,
autohide: false,
});
}

async onImport(data: string) {
Expand Down
50 changes: 31 additions & 19 deletions ui/core/components/toast.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import { Toast as BootstrapToast } from 'bootstrap';
import clsx from 'clsx';

type ToastOptions = {
title?: string;
variant: 'info' | 'success' | 'error';
variant: 'info' | 'success' | 'error' | 'warning';
body: string | Element;
autoShow?: boolean;
canClose?: boolean;
container?: Element;
additionalClasses?: string[];
} & Partial<BootstrapToast.Options>;

class Toast {
private element: HTMLElement;
private container: HTMLElement;
readonly element: HTMLElement;
private container: Element;
private title: ToastOptions['title'];
private body: ToastOptions['body'];
private variant: ToastOptions['variant'];
private canClose: ToastOptions['canClose'];
private additionalClasses: ToastOptions['additionalClasses'];

public instance;
constructor(options: ToastOptions) {
const { title, variant, autoShow = true, body, ...bootstrapOptions } = options || {};
this.container = document.getElementById('toastContainer')!;
const { title, variant, autoShow = true, canClose = true, body, additionalClasses, container, ...bootstrapOptions } = options || {};
this.container = container || document.getElementById('toastContainer')!;
this.additionalClasses = additionalClasses;
this.title = title || 'WowSims';
this.variant = variant || 'info';
this.body = body;
this.canClose = canClose;

this.element = this.template();
this.container.appendChild(this.element);
Expand Down Expand Up @@ -58,13 +66,15 @@ class Toast {
return 'fa-check-circle';
case 'error':
return 'fa-exclamation-circle';
case 'warning':
return 'fa-triangle-exclamation';
}
}

template() {
return (
<div
className={`toast position-relative bottom-0 end-0 toast--${this.variant}`}
className={clsx('toast position-relative bottom-0 end-0', `toast--${this.variant}`, this.additionalClasses)}
attributes={{
role: 'alert',
'aria-live': 'assertive',
Expand All @@ -73,19 +83,21 @@ class Toast {
<div className="toast-header">
<i className={`d-block fas fa-2xl me-2 ${this.getVariantIcon()}`}></i>
<strong className="me-auto">{this.title}</strong>
<a
href="javascript:void(0);"
className="btn-close"
attributes={{
role: 'button',
'aria-label': 'Close',
}}
dataset={{
bsDismiss: 'toast',
}}
aria-label="Close">
<i className={`fas fa-times fa-1xl ${this.getVariantIcon()}`}></i>
</a>
{this.canClose && (
<a
href="javascript:void(0);"
className="btn-close"
attributes={{
role: 'button',
'aria-label': 'Close',
}}
dataset={{
bsDismiss: 'toast',
}}
aria-label="Close">
<i className={`fas fa-times fa-1xl ${this.getVariantIcon()}`}></i>
</a>
)}
</div>
<div className="toast-body">{this.body}</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion ui/core/individual_sim_ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {

private addTopbarComponents() {
this.simHeader.addImportLink('JSON', new Importers.IndividualJsonImporter(this.rootElem, this), true);
this.simHeader.addImportLink('60U Cata', new Importers.Individual80UImporter(this.rootElem, this), true);
this.simHeader.addImportLink('60U Cata', new Importers.Individual60UImporter(this.rootElem, this), true);
this.simHeader.addImportLink('WoWHead', new Importers.IndividualWowheadGearPlannerImporter(this.rootElem, this), false);
this.simHeader.addImportLink('Addon', new Importers.IndividualAddonImporter(this.rootElem, this), true);

Expand Down
5 changes: 5 additions & 0 deletions ui/scss/core/components/_importers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@
width: 12rem;
}
}

.toast-import-warning {
width: 100%;
margin-bottom: var(--spacer-3);
}
}

0 comments on commit 50172d2

Please sign in to comment.