ソースを参照

Merge branch 'release/0.11.0'

Warafear 9 ヶ月 前
コミット
48218baa13

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "dndtools",
-  "version": "0.10.6",
+  "version": "0.11.0",
   "scripts": {
     "ng": "ng",
     "start": "nx serve",

+ 12 - 6
src/app/journal/journal-spellbook/custom-spells-modal/custom-spells-modal.component.html

@@ -1,6 +1,6 @@
 <div class="dimensions">
   <div class="content">
-    <div class="title">{{ "spellcards.manage" | translate }}</div>
+    <div class="title">{{ "spellbook.manage" | translate }}</div>
     <div class="spell-list">
       @for (spell of spells; let index = $index; track spell) {
         <div
@@ -18,18 +18,24 @@
       }
     </div>
   </div>
-  <div class="horizontal-buttons" style="padding: 2rem 1.5rem">
-    <ui-button
+  <div class="button-container">
+    <!-- <ui-button
       [color]="'red'"
       [width]="'w15'"
       [type]="'deleteSelected'"
       (click)="delete()"
-    ></ui-button>
-    <ui-button
+    ></ui-button> -->
+    <!-- <ui-button
       [color]="'red'"
       [width]="'w15'"
       [type]="'cancel'"
       (click)="cancel()"
-    ></ui-button>
+    ></ui-button> -->
+    <button
+      [class]="indexList.length === 0 ? 'disabled' : ''"
+      (click)="indexList.length !== 0 ? delete() : ''"
+    >
+      {{ "spellbook.delete" | translate }}
+    </button>
   </div>
 </div>

+ 33 - 12
src/app/journal/journal-spellbook/custom-spells-modal/custom-spells-modal.component.scss

@@ -3,20 +3,23 @@
   height: 45rem;
   padding: 0;
   overflow: hidden;
+  background-image: url("/assets/images/bg.jpg");
+}
+
+.content {
+  margin: 0;
 }
 
 .title {
   box-shadow: var(--shadow-bottom);
   padding: 1.5rem 0 1rem 0;
   margin-top: 0;
-}
-
-.content {
-  margin: 0;
+  // color: #58180d;
+  // font-family: "Bookinsanity", serif;
 }
 
 .spell-list {
-  height: 32.5rem;
+  height: 34rem;
   padding: 1rem 1rem 0 1rem;
   overflow-y: auto;
   gap: 0.5rem;
@@ -30,12 +33,10 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  background: var(--items);
+  background-image: url("/assets/images/texture.png");
   border-radius: 10px;
-  border: var(--border);
   font-size: 1rem;
   font-weight: 600;
-  // text-align: center;
   cursor: pointer;
   box-shadow:
     0 5px 5px -3px rgba(0, 0, 0, 0.2),
@@ -43,19 +44,39 @@
     0 3px 10px 2px rgba(0, 0, 0, 0.12);
   transition: background-color 0.2s ease-in-out;
   &:hover {
-    background-color: #f8d8c6;
+    border: 1px solid #9b8559;
   }
 }
 
 .selected {
-  background-color: #f8d8c6;
+  background-color: #58180d;
   box-sizing: border-box;
-  border: 3px solid var(--primary);
+  border: 3px solid #9b8559 !important;
   &:hover {
     background-color: var(--items);
   }
 }
 
-.horizontal-buttons {
+.button-container {
   box-shadow: var(--shadow-top);
+  height: 5.5rem;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+
+  button {
+    font-size: 1.25rem;
+    font-weight: 600;
+    width: 18.5rem;
+    border-radius: 10px;
+    padding: 0.5rem 1rem;
+    background-color: var(--delete);
+    border: none;
+    box-shadow: var(--shadow);
+  }
+
+  .disabled {
+    filter: grayscale(0.5);
+    cursor: not-allowed;
+  }
 }

+ 2 - 1
src/app/journal/journal-spellbook/journal-spellbook.component.html

@@ -15,7 +15,8 @@
       </div>
     </div>
 
-    <hr />
+    <!-- <hr /> -->
+    <div class="divider"></div>
     <div class="class-picker">
       @for (className of translator.magicClasses; track className) {
         <div class="class">

+ 16 - 2
src/app/journal/journal-spellbook/journal-spellbook.component.scss

@@ -9,6 +9,22 @@
   background-image: url("/assets/images/bg.jpg");
 }
 
+h1 {
+  // color: #58180d;
+  // font-family: "Bookinsanity", serif;
+  font-size: 3.5rem;
+  font-weight: 500;
+  margin-bottom: 0;
+  // padding-left: 2rem;
+}
+
+.divider {
+  border-top: 2px solid #9b8559;
+  height: 1px;
+  width: 100%;
+  margin-bottom: 0.75rem;
+}
+
 .header-row {
   display: flex;
   flex-direction: row;
@@ -111,7 +127,6 @@
   // background-color: var(--tab);
   background-color: #cbbea4;
   background-color: #b5a27d;
-
   color: black;
   font-size: 1.123rem;
   font-weight: 500;
@@ -126,7 +141,6 @@
   // background-color: var(--tab-active);
   background-color: #b5a27d;
   background-color: #9b8559;
-
   width: 9.25rem;
   font-size: 1.25rem;
   font-weight: 550;

+ 44 - 5
src/app/journal/journal-spellbook/journal-spellbook.component.ts

@@ -319,15 +319,53 @@ export class JournalSpellbookComponent {
       (result) => {
         if (result.state === 'delete') {
           result.data.forEach((spell: Spell) => {
-            // TODO: Implement deletion of custom spells
-            // this.deleteCustomSpell(spell);
+            this.dataAccessor.deleteCustomSpell(spell);
+            this.dataAccessor.removeFavoriteSpell(spell);
+            this.deleteSpellFromPrepared(spell);
           });
+          this.refreshFilteredSpells();
         }
         resultSubscription.unsubscribe();
       },
     );
   }
 
+  /**
+   * Searches for the level of the given spell and deletes it from the prepared spells.
+   * @param spell The spell that is to be deleted
+   */
+  public deleteSpellFromPrepared(spell: Spell): void {
+    if (spell.level === 0) {
+      this.dataAccessor.removeSpellFromLevel0(spell);
+    } else if (spell.level === 1) {
+      this.dataAccessor.removeSpellFromLevel1(spell);
+    }
+    if (spell.level === 2) {
+      this.dataAccessor.removeSpellFromLevel2(spell);
+    }
+    if (spell.level === 3) {
+      this.dataAccessor.removeSpellFromLevel3(spell);
+    }
+    if (spell.level === 4) {
+      this.dataAccessor.removeSpellFromLevel4(spell);
+    }
+    if (spell.level === 5) {
+      this.dataAccessor.removeSpellFromLevel5(spell);
+    }
+    if (spell.level === 6) {
+      this.dataAccessor.removeSpellFromLevel6(spell);
+    }
+    if (spell.level === 7) {
+      this.dataAccessor.removeSpellFromLevel7(spell);
+    }
+    if (spell.level === 8) {
+      this.dataAccessor.removeSpellFromLevel8(spell);
+    }
+    if (spell.level === 9) {
+      this.dataAccessor.removeSpellFromLevel9(spell);
+    }
+  }
+
   // SPELLCARDS
 
   /**
@@ -348,6 +386,7 @@ export class JournalSpellbookComponent {
       alreadyPrepared: alreadyPrepared,
       alreadyInFavorites: alreadyInFavorites,
     });
+    // This subscription is used to receive messages from the spellcard without closing the modal.
     const actionSubscription = this.modalAccessor.action$.subscribe(
       (message) => {
         if (message.action === 'addToFavorites') {
@@ -358,21 +397,20 @@ export class JournalSpellbookComponent {
           this.addSpellToPrepared(spell);
         } else if (message.action === 'removeFromPrepared') {
           this.removeSpellFromPrepared(spell);
+          this.dataAccessor.removeFavoriteSpell(spell);
         }
       },
     );
+    // This subscription is used to receive the result from the spellcard modal.
     const resultSubscription = this.modalAccessor.result$.subscribe(
       (result) => {
         resultSubscription.unsubscribe();
         actionSubscription.unsubscribe();
-
         if (result.state === 'copy') {
           setTimeout(() => {
             this.openSpellCreationModal(true, spell);
           }, 100);
         } else if (result.state === 'update') {
-          console.log('Update spell, caught in spellbook component');
-          // TODO: Implement Modification of spells
           setTimeout(() => {
             this.openSpellModificationModal(spell);
           }, 100);
@@ -394,6 +432,7 @@ export class JournalSpellbookComponent {
       (result) => {
         if (result.state === 'update') {
           this.dataAccessor.updateCustomSpell(result.data);
+          // TODO: Update the spell in the prepared spells and favorite spells
         }
         resultSubscription.unsubscribe();
         this.refreshFilteredSpells();

+ 10 - 35
src/app/journal/journal-spellcards/add-card/add-card.component.html

@@ -1,26 +1,9 @@
 <div class="add-card">
-  @if (state === 1) {
-    <div class="clickable-card" (click)="state = 2; closeOthers()">
+  @if (isIdle) {
+    <div class="clickable-card" (click)="isIdle = false; closeOthers()">
       <img class="add-icon" src="assets/icons/UIIcons/add.svg" />
     </div>
-  } @else if (state === 2) {
-    <div class="button-card">
-      <button (click)="continueToSpellSelection(false); state = 3">
-        {{ "spellcards.add.official" | translate }}
-      </button>
-
-      <hr />
-      <button (click)="continueToSpellSelection(true); state = 3">
-        {{ "spellcards.add.edit" | translate }}
-      </button>
-
-      <hr />
-
-      <button (click)="emitNewSpell()">
-        {{ "spellcards.add.custom" | translate }}
-      </button>
-    </div>
-  } @else if (state === 3) {
+  } @else {
     <div class="spell-selection">
       <div class="controll-row">
         <div class="toggle-row">
@@ -42,24 +25,18 @@
         class="spell-name"
         [(ngModel)]="newSpellName"
         [placeholder]="'spellcards.add.official' | translate"
-        (keyup)="
-          isModification
-            ? filterSpellArrayForModification()
-            : filterSpellArray()
-        "
+        (keyup)="filterSpellArray()"
       />
       <div class="available-spells">
         <ul>
           @for (spell of availableSpells; track spell) {
             <li>
-              <button
-                (click)="
-                  isModification
-                    ? emitNewSpellFromOfficial(spell)
-                    : spellSelected(spell)
-                "
-              >
-                {{ spell.german }}
+              <button (click)="spellSelected(spell)">
+                @if (translate.getDefaultLang() == "de") {
+                  {{ spell.german }}
+                } @else {
+                  {{ spell.english }}
+                }
               </button>
             </li>
           } @empty {
@@ -67,8 +44,6 @@
           }
         </ul>
       </div>
-
-      <!-- <button class="cancel-button" (click)="resetThis()">Abbrechen</button> -->
     </div>
   }
 </div>

+ 8 - 58
src/app/journal/journal-spellcards/add-card/add-card.component.ts

@@ -1,7 +1,8 @@
-import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { Component, Input, Output, EventEmitter, inject } from '@angular/core';
 import { DataService } from 'src/services/data/data.service';
 import { Spell } from 'src/interfaces/spell';
 import { SpellsService } from 'src/services/spells/spells.service';
+import { TranslateService } from '@ngx-translate/core';
 
 @Component({
   selector: 'add-card',
@@ -11,23 +12,18 @@ import { SpellsService } from 'src/services/spells/spells.service';
 export class AddCardComponent {
   @Input() level!: number;
   @Input() alreadyUsedSpells!: any[];
-
-  @Output() createNewSpell = new EventEmitter<any>();
-  @Output() createNewSpellFromOfficial = new EventEmitter<any>();
   @Output() onSpellSelected = new EventEmitter<any>();
 
   public newSpellName: string = '';
   private classAvailableSpells: any[] = [];
   private allAvailableSpells: any[] = [];
   public availableSpells: any[] = [];
-  public isModification: boolean | undefined;
-  public state: number = 1;
+  public isIdle: boolean = true;
   public showAll: boolean = false;
 
-  public constructor(
-    private spellsAccessor: SpellsService,
-    private dataAccessor: DataService,
-  ) {}
+  private spellsAccessor = inject(SpellsService);
+  private dataAccessor = inject(DataService);
+  public translate: any = inject(TranslateService);
 
   public ngOnInit(): void {
     this.classAvailableSpells = this.spellsAccessor.getAvailableSpells(
@@ -47,35 +43,7 @@ export class AddCardComponent {
 
   public toggleShowAll(): void {
     this.showAll = !this.showAll;
-    if (this.isModification) {
-      this.filterSpellArrayForModification();
-    } else {
-      this.filterSpellArray();
-    }
-  }
-
-  public emitNewSpell(): void {
-    this.createNewSpell.emit(this.level);
-    this.resetThis();
-  }
-
-  public emitNewSpellFromOfficial(spell: Spell): void {
-    this.createNewSpellFromOfficial.emit(spell);
-    this.resetThis();
-  }
-
-  public continueToSpellSelection(modify: boolean): void {
-    this.classAvailableSpells = this.spellsAccessor.getAvailableSpells(
-      this.level,
-      this.dataAccessor.characterData.class,
-    );
-    this.isModification = modify;
-    if (modify) {
-      this.filterSpellArrayForModification();
-    } else {
-      this.filterSpellArray();
-    }
-    this.closeOthers();
+    this.filterSpellArray();
   }
 
   public spellSelected(spell: any): void {
@@ -89,8 +57,7 @@ export class AddCardComponent {
 
   public resetThis(): void {
     this.newSpellName = '';
-    this.state = 1;
-    this.isModification = undefined;
+    this.isIdle = true;
     this.showAll = false;
   }
 
@@ -115,21 +82,4 @@ export class AddCardComponent {
       );
     }
   }
-
-  public filterSpellArrayForModification(): void {
-    let array = this.showAll
-      ? this.allAvailableSpells
-      : this.classAvailableSpells;
-    if (this.newSpellName.length > 0) {
-      this.availableSpells = array.filter(
-        (spell) =>
-          spell.german
-            .toLowerCase()
-            .includes(this.newSpellName.toLowerCase()) ||
-          spell.english.toLowerCase().includes(this.newSpellName.toLowerCase()),
-      );
-    } else {
-      this.availableSpells = array;
-    }
-  }
 }

+ 1 - 5
src/app/journal/journal-spellcards/journal-spellcards.component.html

@@ -64,11 +64,7 @@
             <add-card
               [level]="index"
               [alreadyUsedSpells]="getUsedIDs(index)"
-              (createNewSpell)="openSpellCreationModal(index, false)"
-              (createNewSpellFromOfficial)="
-                openSpellCreationModal(index, true, $event)
-              "
-              (onSpellSelected)="addSpellToSpelllist($event, index)"
+              (onSpellSelected)="addSpell($event, index)"
             ></add-card>
           }
         </div>

+ 19 - 128
src/app/journal/journal-spellcards/journal-spellcards.component.ts

@@ -55,6 +55,11 @@ export class JournalSpellcardsComponent {
 
   ///////// FUNCTIONS //////////
 
+  /**
+   * Collects all ids of the spells in the spell list of a given level.
+   * They are passed to the add-card to exclude them from the list of available spells.
+   * @param level The level of the spell.
+   */
   public getUsedIDs(level: number): number[] {
     const usedIDs: number[] = [];
     this.getSpellList(level).forEach((spell) => {
@@ -102,127 +107,10 @@ export class JournalSpellcardsComponent {
           this.getSpellList(level).splice(spellIndex, 1);
           this.updateSpellsInDatabase(level);
         }
-        // else if (result.state === 'update') {
-        //   setTimeout(() => {
-        //     this.openSpellModificationModal(level, spellIndex);
-        //   }, 100);
-        // }
-      },
-    );
-  }
-
-  /**
-   * Opens a modal to modify an existing spell. The spell is then updated in the dataAccessor, local level and favorites.
-   * @param level The current level of the spell.
-   * @param index The index where the spell is located in the spell list.
-   */
-  public openSpellModificationModal(level: number, index: number): void {
-    this.modalAccessor.openModal(SpellModalComponent, {
-      spell:
-        index !== undefined
-          ? JSON.parse(JSON.stringify(this.getSpellList(level)![index]))
-          : undefined,
-      isModification: true,
-    });
-    const resultSubscription = this.modalAccessor.result$.subscribe(
-      (result) => {
-        if (result.state === 'update') {
-          // level was not modified
-          if (level === result.data.level) {
-            this.updateSpell(result.data, level, index);
-            this.dataAccessor.updateFavoriteSpell(result.data);
-            this.dataAccessor.updateCustomSpell(result.data);
-          } else {
-            // level was modified
-            this.getSpellList(level).splice(index, 1);
-            this.addSpell(result.data, result.data.level);
-          }
-        }
-        resultSubscription.unsubscribe();
-      },
-    );
-  }
-
-  /**
-   * In this modal new spells can be created. This can be completely new spells or
-   * modified official spells. If successful, the spell is added to the spell list,
-   * sent to the daService and sent to the spellsService which in return sends it to the dataBase
-   * @param level
-   * @param isBasedOnOfficialSpell
-   * @param spell
-   */
-  public openSpellCreationModal(
-    level: number,
-    isBasedOnOfficialSpell: boolean,
-    spell?: Spell,
-  ): void {
-    this.modalAccessor.openModal(SpellModalComponent, {
-      spell: isBasedOnOfficialSpell ? spell : undefined,
-      level: level,
-      id: this.dataAccessor.customSpellId,
-      isBasedOnOfficialSpell: isBasedOnOfficialSpell,
-      classes: [this.dataAccessor.characterData.class],
-    });
-    const resultSubscription = this.modalAccessor.result$.subscribe(
-      (result) => {
-        if (result.state === 'add') {
-          this.addSpell(result.data, level);
-          // this.spellsService.addCustomSpell(result.data);
-          this.dataAccessor.addCustomSpell(result.data);
-        } else {
-        }
-        resultSubscription.unsubscribe();
       },
     );
   }
 
-  /**
-   * Opens the modal to manage custom spells. Here, custom spells can be deleted.
-   */
-  // TODO: DELETE
-  // public openManageCustomSpellsModal(): void {
-  //   this.modalAccessor.openModal(CustomSpellsModalComponent, {
-  //     spells: this.dataAccessor.customSpells,
-  //   });
-  //   const resultSubscription = this.modalAccessor.result$.subscribe(
-  //     (result) => {
-  //       if (result.state === 'delete') {
-  //         result.data.forEach((spell: Spell) => {
-  //           this.deleteCustomSpell(spell);
-  //         });
-  //       }
-  //       resultSubscription.unsubscribe();
-  //     },
-  //   );
-  // }
-
-  /**
-   * Deletes a custom spell from the custom spells list.
-   * It is deleted in the spells and data service.
-   * It is also removed from the prepared and favorite spells list if present.
-   * @param spell
-   */
-  public deleteCustomSpell(spell: Spell): void {
-    this.dataAccessor.deleteCustomSpell(spell);
-    this.spellsService.deleteCustomSpell(spell);
-    let list = this.getSpellList(spell.level);
-    const index = list.findIndex((spellList) => spellList.id === spell.id);
-    if (index > -1) {
-      list.splice(index, 1);
-      this.dataAccessor.removeFavoriteSpell(spell);
-      this.updateSpellsInDatabase(spell.level);
-    }
-  }
-
-  /**
-   * Adds a given spell to the spelllist of a specific level.
-   * @param spell The spell to add.
-   * @param level The level the spell needs to be added to.
-   */
-  public addSpellToSpelllist(spell: Spell, level: number): void {
-    this.addSpell(spell, level);
-  }
-
   /**
    * Adds a given spell to the spell list specified by level.
    * Also updates the spell list in the database.
@@ -234,17 +122,6 @@ export class JournalSpellcardsComponent {
     this.updateSpellsInDatabase(level);
   }
 
-  /**
-   * Overrides a spell in a given spell list, specified by the index.
-   * @param spell The new spell.
-   * @param level The level to add the spell to.
-   * @param index The index at which the spell should be overridden.
-   */
-  public updateSpell(spell: Spell, level: number, index: number): void {
-    this.getSpellList(level)![index] = spell;
-    this.updateSpellsInDatabase(level);
-  }
-
   /**
    * Returns the reference to the spell list specified by the level.
    * @param level Specifies the level of the spell list.
@@ -405,12 +282,26 @@ export class JournalSpellcardsComponent {
     }
   }
 
+  /**
+   * When a spell is dragged, the index, representing the level of the spell is stored in the draggingIndex variable.
+   * @param index The spell's level.
+   */
   public dragStart(index: number) {
     this.draggingIndex = index;
   }
 
+  /**
+   * Checks if the dragged element was dropped over the removal card,
+   * the spell is removed from the prepared list and the favorites list.
+   * @param event The event that contains the dragged element.
+   */
   public dragEnd(event: any) {
     if (event.event.target.classList.contains('removal-card')) {
+      this.dataAccessor.removeFavoriteSpell(
+        this.getSpellList(this.draggingIndex!)[
+          event.source.element.nativeElement.id
+        ],
+      );
       this.getSpellList(this.draggingIndex!).splice(
         event.source.element.nativeElement.id,
         1,

+ 6 - 0
src/app/journal/spell-modal/spell-modal.component.scss

@@ -1,5 +1,9 @@
 @import "../../../responsive";
 
+.dimensions {
+  background-image: url("/assets/images/bg.jpg");
+}
+
 .checkbox-column {
   width: 33.3%;
   display: flex;
@@ -110,6 +114,8 @@ div.nav-pills.flex-column.nav {
     &.active,
     &:hover {
       background-color: var(--tab-active);
+      background-color: #b5a27d;
+
       box-shadow: var(--shadow);
     }
   }

+ 1 - 1
src/app/shared-components/full-spellcard/full-spellcard.component.scss

@@ -208,7 +208,7 @@
     font-size: 1.25rem;
     font-weight: 600;
     height: 4.25rem;
-    width: 12rem;
+    width: 13rem;
     border-radius: 10px;
     cursor: pointer;
     box-shadow: var(--shadow);

+ 3 - 2
src/assets/i18n/de.json

@@ -798,7 +798,8 @@
     },
     "manage": "Eigene Zauber verwalten",
     "add": "Neuen Zauber erstellen",
-    "noSpells": "Für diesen Filter gibt es keine Zauber"
+    "noSpells": "Für diesen Filter gibt es keine Zauber",
+    "delete": "Ausgewählte Zauber löschen"
   },
 
   "creator": {
@@ -816,7 +817,7 @@
     "hint": "Die App befindet sich immer noch in einem Entwicklungsstadium und es können Fehler auftreten",
     "issues": "<p>Fehler und Anmerkungen bitte auf dem <a href='https://gogs.koljastrohm-games.com/Warafear/DNDTools/issues'>Git-Server in Issues</a> vermerken.<p>",
     "okay": "Verstanden",
-    "version": "0.10.6",
+    "version": "0.11.0",
     "test": "Test"
   }
 }

+ 3 - 2
src/assets/i18n/en.json

@@ -793,7 +793,8 @@
     },
     "manage": "Manage custom spells",
     "add": "Create new spell",
-    "noSpells": "No spells found for this filter"
+    "noSpells": "No spells found for this filter",
+    "delete": "Delete selected spells"
   },
   "creator": {
     "new": "Create New Character",
@@ -810,7 +811,7 @@
     "hint": "The app is still in a development stage and errors may occur",
     "issues": "<p>Please note errors and comments on the <a href='https://gogs.koljastrohm-games.com/Warafear/DNDTools/issues'>Git server in Issues</a>.<p>",
     "okay": "Understood",
-    "version": "0.10.6",
+    "version": "0.11.0",
     "test": "Test"
   }
 }

+ 11 - 0
src/services/data/data.service.ts

@@ -303,6 +303,17 @@ export class DataService {
     });
   }
 
+  public deleteCustomSpellArray(spells: Spell[]): void {
+    spells.forEach((spell) => {
+      const index = this._customSpells.indexOf(spell);
+      this._customSpells.splice(index, 1);
+    });
+    this.setData('customSpells', {
+      spells: this._customSpells,
+      id: this.customSpellId,
+    });
+  }
+
   private _customSpellId: number = 10000;
 
   public get customSpellId(): number {