diff --git a/src/components/itemPopup/Description.tsx b/src/components/itemPopup/Description.tsx index 33fb74e..ced3e22 100644 --- a/src/components/itemPopup/Description.tsx +++ b/src/components/itemPopup/Description.tsx @@ -15,6 +15,7 @@ const calculateStat = (formula?: string) => { } const otherOptions = (linesContent: LinesContent) => { + if (linesContent?.link) return {linesContent.text} if (linesContent?.formula) return ( @@ -25,6 +26,8 @@ const otherOptions = (linesContent: LinesContent) => { return null } + + const joinClassNames = (classNames: (string | null | undefined)[] | undefined) => { return classNames ?.flatMap((className: string | null | undefined) => { diff --git a/src/components/sideBar/Selection.tsx b/src/components/sideBar/Selection.tsx index 36526c6..f32988e 100644 --- a/src/components/sideBar/Selection.tsx +++ b/src/components/sideBar/Selection.tsx @@ -1,197 +1,232 @@ -import { FolderTypes, PerkTypes } from '@icemourne/description-converter' -import { TypedObject } from '@icemourne/tool-box' -import { AnyAction, Dispatch, ThunkDispatch } from '@reduxjs/toolkit' -import { useEffect, useState } from 'react' -import { changePerkType, changeSelectedPerk } from 'src/redux/globalSlice' -import { useAppDispatch, useAppSelector } from 'src/redux/hooks' -import { store } from 'src/redux/store' -import { GlobalState } from 'src/redux/types' -import { sortPerks } from 'src/utils/sortPerks' -import { Updater, useImmer } from 'use-immer' - -import { Select } from '../universal/Select' +import { FolderTypes, PerkTypes } from "@icemourne/description-converter"; +import { TypedObject } from "@icemourne/tool-box"; +import { AnyAction, Dispatch, ThunkDispatch } from "@reduxjs/toolkit"; +import { useEffect, useState } from "react"; +import { changePerkType, changeSelectedPerk } from "src/redux/globalSlice"; +import { useAppDispatch, useAppSelector } from "src/redux/hooks"; +import { store } from "src/redux/store"; +import { GlobalState } from "src/redux/types"; +import { sortPerks } from "src/utils/sortPerks"; +import { Updater, useImmer } from "use-immer"; + +import { Select } from "../universal/Select"; export function DescriptionTypeSelection() { - const dispatch = useAppDispatch() - const { settings } = useAppSelector((state) => state.global) - - const options: { [key: string]: { [key in PerkTypes]?: string } } = { - 'Exotics': { - 'Armor Trait Exotic': 'Armor', - 'Weapon Frame Exotic': 'Weapon' - }, - 'Weapon': { - 'Weapon Trait': 'Trait', - 'Weapon Trait Enhanced': 'Enhanced Trait', - 'Weapon Perk': 'Perk', - 'Weapon Trait Origin': 'Origin Trait', - 'Weapon Frame': 'Frame', - 'Weapon Frame Enhanced': 'Enhanced Frame' - }, - 'Abilities / Subclass Options': { - 'Subclass Fragment': 'Fragment', - 'Subclass Aspect': 'Aspect', - 'Subclass Super': 'Super', - 'Subclass Grenade': 'Grenade', - 'Subclass Melee': 'Melee', - 'Subclass Class': 'Class', - 'Subclass Movement': 'Movement' - }, - 'Mods': { - 'Armor Mod General': 'Armor General', - 'Armor Mod Activity': 'Armor Activity', - 'Armor Mod Seasonal': 'Armor Seasonal', - 'Weapon Mod': 'Weapon', - 'Ghost Mod': 'Ghost' - } - } - - return ( - - ) + const dispatch = useAppDispatch(); + const { settings } = useAppSelector((state) => state.global); + + const options: { [key: string]: { [key in PerkTypes]?: string } } = { + Exotics: { + "Armor Trait Exotic": "Armor", + "Weapon Frame Exotic": "Weapon", + }, + Weapon: { + "Weapon Trait": "Trait", + "Weapon Trait Enhanced": "Enhanced Trait", + "Weapon Perk": "Perk", + "Weapon Trait Origin": "Origin Trait", + "Weapon Frame": "Frame", + "Weapon Frame Enhanced": "Enhanced Frame", + }, + "Abilities / Subclass Options": { + "Subclass Fragment": "Fragment", + "Subclass Aspect": "Aspect", + "Subclass Super": "Super", + "Subclass Grenade": "Grenade", + "Subclass Melee": "Melee", + "Subclass Class": "Class", + "Subclass Movement": "Movement", + }, + Mods: { + "Armor Mod General": "Armor General", + "Armor Mod Activity": "Armor Activity", + "Armor Mod Seasonal": "Armor Seasonal", + "Weapon Mod": "Weapon", + "Ghost Mod": "Ghost", + }, + }; + + return ( + + ); } // change selected perk with shift mouse wheel -type DispatchType = ThunkDispatch<{ global: GlobalState }, undefined, AnyAction> & Dispatch -const changePerk = (displayedPerkList: string[] | number[], dispatch: DispatchType, setSelectedPerk: Updater) => { - const [externalEvent, setExternalEvent] = useState(null) - useEffect(() => { - const changePerkEvent = (e: WheelEvent) => { - if (!e.shiftKey) return - e.preventDefault() - setExternalEvent(e) - } - const mainEditor = document.querySelector("#main-editor > div") as any - const secondaryEditor = document.querySelector("#secondary-editor > div") as any - window.addEventListener('wheel', changePerkEvent, { passive: false }) - mainEditor?.addEventListener('wheel', changePerkEvent, { passive: false }) - secondaryEditor?.addEventListener('wheel', changePerkEvent, { passive: false }) - }, [externalEvent]) - useEffect(() => { - if (externalEvent === null) return - const currentlySelected = store.getState().global.settings.currentlySelected - const index = Object.values(displayedPerkList).findIndex((hash) => Number(hash) === currentlySelected) - - const perkHash = - externalEvent.deltaY < 0 - ? displayedPerkList[Math.max(index - 1, 0)] - : displayedPerkList[Math.min(index + 1, displayedPerkList.length - 1)] - - if (perkHash) { - dispatch(changeSelectedPerk(Number(perkHash))) - setSelectedPerk(Number(perkHash)) - } - }, [externalEvent]) -} - -const PerkSelectionOptions = ({ displayedPerkList }: { displayedPerkList: string[] | number[] }) => { - const { database, settings } = useAppSelector((state) => state.global) - const { language } = settings - - const component = displayedPerkList.map((perkHash, i) => { - if (database[perkHash] === undefined) return - const perk = database[perkHash] - const updateTracker = perk.updateTracker.descriptions - return ( - - ) - }) - - return <>{component} -} +type DispatchType = ThunkDispatch< + { global: GlobalState }, + undefined, + AnyAction +> & + Dispatch; +const changePerk = ( + displayedPerkList: string[] | number[], + dispatch: DispatchType, + setSelectedPerk: Updater +) => { + const [externalEvent, setExternalEvent] = useState(null); + useEffect(() => { + const changePerkEvent = (e: WheelEvent) => { + if (!e.shiftKey) return; + e.preventDefault(); + setExternalEvent(e); + }; + const mainEditor = document.querySelector("#main-editor > div") as any; + const secondaryEditor = document.querySelector( + "#secondary-editor > div" + ) as any; + window.addEventListener("wheel", changePerkEvent, { passive: false }); + mainEditor?.addEventListener("wheel", changePerkEvent, { passive: false }); + secondaryEditor?.addEventListener("wheel", changePerkEvent, { + passive: false, + }); + }, [externalEvent]); + useEffect(() => { + if (externalEvent === null) return; + const currentlySelected = + store.getState().global.settings.currentlySelected; + const index = Object.values(displayedPerkList).findIndex( + (hash) => Number(hash) === currentlySelected + ); + + const perkHash = + externalEvent.deltaY < 0 + ? displayedPerkList[Math.max(index - 1, 0)] + : displayedPerkList[Math.min(index + 1, displayedPerkList.length - 1)]; + + if (perkHash) { + dispatch(changeSelectedPerk(Number(perkHash))); + setSelectedPerk(Number(perkHash)); + } + }, [externalEvent]); +}; + +const PerkSelectionOptions = ({ + displayedPerkList, +}: { + displayedPerkList: string[] | number[]; +}) => { + const { database, settings } = useAppSelector((state) => state.global); + const { language } = settings; + + const component = displayedPerkList.map((perkHash, i) => { + if (database[perkHash] === undefined) return; + const perk = database[perkHash]; + const updateTracker = perk.updateTracker.descriptions; + return ( + + ); + }); + + return <>{component}; +}; export const PerkSelection = () => { - const dispatch = useAppDispatch() - const { - settings, - databaseSettings: { folders } - } = useAppSelector((state) => state.global) - - const [displayedPerkList, setDisplayedPerkList] = useImmer([]) - const [displayedFolderList, setDisplayedFolderList] = useImmer(null) - const [selectedPerk, setSelectedPerk] = useImmer(0) - const [selectedFolder, setSelectedFolder] = useImmer(null) - - const setDisplayedPerks = (foldersName: string) => { - const folderPerks = folders[settings.selectedType as FolderTypes].find((folder) => folder.name === foldersName) - setDisplayedPerkList(folderPerks?.has ?? []) - dispatch(changeSelectedPerk(Number([folderPerks?.has[0] ?? 0]))) - } - - useEffect(() => { - if (folders[settings.selectedType as FolderTypes]) { - const folderNames = folders[settings.selectedType as FolderTypes].map((folder) => folder.name) - const firstFolder = folderNames[0] - setDisplayedFolderList(folderNames) - setSelectedFolder(firstFolder) - - const folderPerks = folders[settings.selectedType as FolderTypes][0].has - setDisplayedPerkList(folderPerks) - setSelectedPerk(folderPerks[0]) - dispatch(changeSelectedPerk(folderPerks[0])) - } else { - const sortedPerks = sortPerks() - - setDisplayedPerkList(sortedPerks) - setSelectedPerk(Number(sortedPerks[0])) - dispatch(changeSelectedPerk(Number(sortedPerks[0]))) - - setDisplayedFolderList(null) - setSelectedFolder(null) - } - }, [settings.selectedType]) - - changePerk(displayedPerkList, dispatch, setSelectedPerk) - - return ( - <> - {displayedFolderList && selectedFolder && ( - - )} - - - - ) -} + const dispatch = useAppDispatch(); + const { + settings, + databaseSettings: { folders }, + } = useAppSelector((state) => state.global); + + const [displayedPerkList, setDisplayedPerkList] = useImmer< + string[] | number[] + >([]); + const [displayedFolderList, setDisplayedFolderList] = useImmer< + string[] | null + >(null); + const [selectedPerk, setSelectedPerk] = useImmer(0); + const [selectedFolder, setSelectedFolder] = useImmer(null); + + const setDisplayedPerks = (foldersName: string) => { + const folderPerks = folders[settings.selectedType as FolderTypes].find( + (folder) => folder.name === foldersName + ); + setDisplayedPerkList(folderPerks?.has ?? []); + dispatch(changeSelectedPerk(Number([folderPerks?.has[0] ?? 0]))); + }; + + useEffect(() => { + if (folders[settings.selectedType as FolderTypes]) { + const folderNames = folders[settings.selectedType as FolderTypes].map( + (folder) => folder.name + ); + const firstFolder = folderNames[0]; + setDisplayedFolderList(folderNames); + setSelectedFolder(firstFolder); + + const folderPerks = folders[settings.selectedType as FolderTypes][0].has; + setDisplayedPerkList(folderPerks); + setSelectedPerk(folderPerks[0]); + dispatch(changeSelectedPerk(folderPerks[0])); + } else { + const sortedPerks = sortPerks(); + debugger; + + setDisplayedPerkList(sortedPerks); + setSelectedPerk(Number(sortedPerks[0])); + dispatch(changeSelectedPerk(Number(sortedPerks[0]))); + + setDisplayedFolderList(null); + setSelectedFolder(null); + } + }, [settings.selectedType]); + + changePerk(displayedPerkList, dispatch, setSelectedPerk); + + return ( + <> + {displayedFolderList && selectedFolder && ( + + )} + + + + ); +}; diff --git a/src/redux/reducers/settings.ts b/src/redux/reducers/settings.ts index c97d423..0d99b24 100644 --- a/src/redux/reducers/settings.ts +++ b/src/redux/reducers/settings.ts @@ -1,23 +1,33 @@ -import { GlobalState, SettingsState } from "../types" import { PerkTypes, WeaponTypes } from "@icemourne/description-converter" - import { PayloadAction } from "@reduxjs/toolkit" import { WritableDraft } from "immer/dist/internal" +import { GlobalState, SettingsState } from "../types" + type State = WritableDraft +const url = new URL(window.location.href) export const settingsReducers = { changePerkType: (state: State, action: PayloadAction) => { state.settings.selectedType = action.payload + + url.searchParams.set('type', String(action.payload)); + history.pushState({}, '', url); }, changeSelectedPerk: (state: State, action: PayloadAction) => { state.settings.currentlySelected = action.payload + + url.searchParams.set('perkHash', String(action.payload)); + history.pushState({}, '', url); }, changeEditorType: (state: State, action: PayloadAction) => { state.settings.editorType = action.payload }, changeLanguage: (state: State, action: PayloadAction) => { state.settings.language = action.payload + + url.searchParams.set('language', String(action.payload)); + history.pushState({}, '', url); }, addMessage: (state: State, action: PayloadAction<{message: string, type?: 'error' | 'success'}>) => { state.settings.messages = [ diff --git a/src/redux/store.ts b/src/redux/store.ts index 6c7e9f8..8eedcfd 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,3 +1,4 @@ +import { PerkTypes } from '@icemourne/description-converter' import { fetchBungieManifest } from '@icemourne/tool-box' import { configureStore } from '@reduxjs/toolkit' import _ from 'lodash' @@ -10,6 +11,9 @@ import { GlobalState } from './types' const { inventoryItem, stat } = await fetchBungieManifest(['inventoryItem', 'stat'], 'en', false) const { live, intermediate } = await getStartUpDescriptions() +const type = window.location.search.match(/type=(\w|\+)+/)?.[0].replace('type=', '').replaceAll('+', ' ') as PerkTypes | undefined +const hash = window.location.search.match(/perkHash=\d+/)?.[0].replace('perkHash=', '') + const preloadedState: { global: GlobalState } = { global: { database: { @@ -26,9 +30,9 @@ const preloadedState: { global: GlobalState } = { }, databaseSettings: intermediate.databaseSettings, settings: { - currentlySelected: 0, + currentlySelected: hash ? Number(hash) : 0, language: 'en' as const, - selectedType: 'none' as const, + selectedType: type || 'none' as const, editorType: 'normal' as const, messages: [], weaponType: 'AR' as const,