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

Rebase 3.25 #36

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: release

on:
push:
tags:
- v*
branches:
- main

jobs:
release:
Expand Down
24 changes: 16 additions & 8 deletions frontend/src/lib/components/SearchResult.svelte
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
<script lang="ts">
import type { SearchWithSeed } from '../skill_tree';
import { skillTree, translateStat, openTrade } from '../skill_tree';
import { openQueryTrade } from '$lib/utils/trade_utils';
import { constructSingleResultQuery, type SearchWithSeed } from '../skill_tree';
import { skillTree, translateStat } from '../skill_tree';

export let highlight: (newSeed: number, passives: number[]) => void;
export let set: SearchWithSeed;
export let jewel: number;
export let conqueror: string;
</script>

<div
class="my-2 border-white/50 border p-2 flex flex-col cursor-pointer"
on:click={() =>
const handleOnClick = () =>
highlight(
set.seed,
set.skills.map((s) => s.passive)
)}>
);
</script>

<div
class="my-2 border-white/50 border p-2 flex flex-col cursor-pointer"
on:click={handleOnClick}
on:keydown={handleOnClick}
role="button"
tabindex="0">
<div class="flex flex-row justify-between">
<!-- Padding -->
<button class="px-3 invisible">Trade</button>
<div class="font-bold text-orange-500 text-center">
Seed {set.seed} (weight {set.weight})
</div>
<button class="px-3 bg-blue-500/40 rounded" on:click={() => openTrade(jewel, conqueror, [set])}>Trade</button>
<button
class="px-3 bg-blue-500/40 rounded"
on:click={() => openQueryTrade(constructSingleResultQuery(jewel, conqueror, set))}>Trade</button>
</div>
{#each set.skills as skill}
<div class="mt-2">
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/lib/components/TradeButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts">
import { openQueryTrade, type Query } from '$lib/utils/trade_utils';

export let queries: Query[];
export let showTradeLinks = false;

$: console.log(showTradeLinks);

$: hasMultipleQueries = queries.length > 1;

const handleOnClick = () => {
if (queries.length === 1) {
// if all filter fit into a single query, link straight to trade website
openQueryTrade(queries[0]);
} else if (hasMultipleQueries) {
// otherwise toggle display of batched trade links
showTradeLinks = !showTradeLinks;
}
};
</script>

<button
class="p-1 px-3 bg-blue-500/40 rounded disabled:bg-blue-900/40 mr-2"
on:click={handleOnClick}
disabled={!queries}>
{hasMultipleQueries ? (showTradeLinks ? 'Hide Trade Links' : 'Show Trade Links') : 'Trade'}
</button>
14 changes: 14 additions & 0 deletions frontend/src/lib/components/TradeLinks.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import { openQueryTrade, type Query } from '$lib/utils/trade_utils';

// queries to display trade link buttons for
export let queries: Query[];
</script>

<div class="flex flex-wrap gap-2.5 my-2">
{#each queries as query, index}
<button class="p-1 px-3 bg-blue-500/40 rounded" on:click={() => openQueryTrade(query)}>
Trade Batch {index + 1}
</button>
{/each}
</div>
125 changes: 47 additions & 78 deletions frontend/src/lib/skill_tree.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Translation, Node, SkillTreeData, Group, Sprite, TranslationFile } from './skill_tree_types';
import { data } from './types';
import { type Filter, type Query, filterGroupsToQuery, filtersToFilterGroup } from './utils/trade_utils';
import { chunkArray } from './utils/utils';

export let skillTree: SkillTreeData;

Expand Down Expand Up @@ -384,91 +386,58 @@ const tradeStatNames: { [key: number]: { [key: string]: string } } = {
}
};

export const constructQuery = (jewel: number, conqueror: string, result: SearchWithSeed[]) => {
const max_filter_length = 50;
const max_filters = 4;
const max_query_length = max_filter_length * max_filters;
const final_query = [];
const stat = {
type: 'count',
value: { min: 1 },
filters: [],
disabled: false
};
export const constructSingleResultQuery = (jewel: number, conqueror: string | null, result: SearchWithSeed): Query => {
const anyConqueror = conqueror === null;

// single seed case
if (result.length == 1) {
for (const conq of Object.keys(tradeStatNames[jewel])) {
stat.filters.push({
id: tradeStatNames[jewel][conq],
value: {
min: result[0].seed,
max: result[0].seed
},
disabled: conq != conqueror
});
}
const filters: Filter[] = Object.keys(tradeStatNames[jewel]).map((conq) => ({
id: tradeStatNames[jewel][conq],
value: {
min: result.seed,
max: result.seed
},
disabled: anyConqueror ? false : conq != conqueror
}));

final_query.push(stat);
// too many results case
} else if (result.length > max_query_length) {
for (let i = 0; i < max_filters; i++) {
final_query.push({
type: 'count',
value: { min: 1 },
filters: [],
disabled: i == 0 ? false : true
});
}
const filterGroup = filtersToFilterGroup(filters, false);
const query: Query = filterGroupsToQuery([filterGroup]);
return query;
};

for (const [i, r] of result.slice(0, max_query_length).entries()) {
const index = Math.floor(i / max_filter_length);
const constructSearchFilter = (jewel: number, conqueror: string | null, result: SearchWithSeed): Filter[] => {
// null conqueror indicates to search for any conqueror
const anyConqueror = conqueror === null;
const conquerors = anyConqueror ? Object.keys(tradeStatNames[jewel]) : [conqueror];

final_query[index].filters.push({
id: tradeStatNames[jewel][conqueror],
value: {
min: r.seed,
max: r.seed
}
});
return conquerors.map((conq) => ({
id: tradeStatNames[jewel][conq],
value: {
min: result.seed,
max: result.seed
}
} else {
for (const conq of Object.keys(tradeStatNames[jewel])) {
stat.disabled = conq != conqueror;

for (const r of result) {
stat.filters.push({
id: tradeStatNames[jewel][conq],
value: {
min: r.seed,
max: r.seed
}
});
}
}));
};

if (stat.filters.length > max_filter_length) {
stat.filters = stat.filters.slice(0, max_filter_length);
}
export const constructQueries = (jewel: number, conqueror: string | null, results: SearchWithSeed[]) => {
const max_filter_length = 50;
const max_filters = 4;
const max_query_length = max_filter_length * max_filters;

final_query.push(stat);
}
}
// convert all results into filters
const allFilters = results.flatMap((result) => constructSearchFilter(jewel, conqueror, result));

return {
query: {
status: {
option: 'online'
},
stats: final_query
},
sort: {
price: 'asc'
}
};
};
// group filters into groups of max_query_length, where each group is further grouped into chunks of max_filter_length
// this represents multiple trade links, where each trade link has multiple filter groups, and each filter group has multiple filters
const queryFilterGroups = chunkArray(allFilters, max_query_length).map((chunk) =>
chunkArray(chunk, max_filter_length)
);

// map filters groups to queries
const tradeQueries = queryFilterGroups.map((queryFilterGroup) => {
// for each query, map the chunks within it to filter groups
const tradeFilterGroups = queryFilterGroup.map((filters, index) => filtersToFilterGroup(filters, index !== 0));
const tradeQuery: Query = filterGroupsToQuery(tradeFilterGroups);
return tradeQuery;
});

export const openTrade = (jewel: number, conqueror: string, results: SearchWithSeed[]) => {
const url = new URL('https://www.pathofexile.com/trade/search/Necropolis');
url.searchParams.set('q', JSON.stringify(constructQuery(jewel, conqueror, results)));
window.open(url, '_blank');
return tradeQueries;
};
47 changes: 47 additions & 0 deletions frontend/src/lib/utils/trade_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export type Filter = {
id: string;
value: { min: number; max: number };
disabled?: boolean;
};
export type FilterGroup = {
type: string;
value: { min: number };
filters: Filter[];
disabled: boolean;
};
export type Query = {
query: {
status: {
option: string;
};
stats: FilterGroup[];
};
sort: {
price: string;
};
};

export const filtersToFilterGroup = (filters: Filter[], disabled: boolean): FilterGroup => ({
type: 'count',
value: { min: 1 },
filters: filters,
disabled: disabled
});

export const filterGroupsToQuery = (FilterGroups: FilterGroup[]): Query => ({
query: {
status: {
option: 'online'
},
stats: FilterGroups
},
sort: {
price: 'asc'
}
});

export const openQueryTrade = (query: Query) => {
const url = new URL('https://www.pathofexile.com/trade/search/Necropolis');
url.searchParams.set('q', JSON.stringify(query));
window.open(url, '_blank');
};
19 changes: 19 additions & 0 deletions frontend/src/lib/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Break an array into chunks of the given size
*
* e.g. for chunk size 2:
*
* [1, 2, 3, 4, 5] => [[1, 2], [3, 4], [5]]
*/
export const chunkArray = <T>(inputArray: Array<T>, chunkSize: number): Array<T>[] =>
inputArray.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index / chunkSize);

if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = []; // start a new chunk
}

resultArray[chunkIndex].push(item);

return resultArray;
}, [] as Array<T>[]);
5 changes: 3 additions & 2 deletions frontend/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@
{#if result.AlternatePassiveSkill}
<div class="mt-4">
<h3>Alternate Passive Skill</h3>
<span
>{result.AlternatePassiveSkill.Name} ({result.AlternatePassiveSkill.ID}) ({result.AlternatePassiveSkill})</span>
<span>
{result.AlternatePassiveSkill.Name} ({result.AlternatePassiveSkill.ID})
</span>
</div>

{#if result.StatRolls && Object.keys(result.StatRolls).length > 0}
Expand Down
Loading
Loading