Compare commits

...

13 Commits

Author SHA1 Message Date
Kvan7
1245ef96d6 Fixes error and adds basic price check (#37)
* bump for updating branding

* Bugfix/testing-potential-fix (#31)

* removes the problem

* sdfhgdf

* change to no-referrer-when-downgrade

* Start of fixes for price check (#36)

* feat: Adds new trade web api

Adds types for trade api

* fix lint

* push edits

* Add basic items to items.ndjson

* Added images from old items files

* Fix armor values

* WOOO

* bump version
2024-12-13 00:07:53 -06:00
Kvan7
ab93c8b96a Update issue templates 2024-12-12 21:31:36 -06:00
Kvan7
ab7cb30588 Update issue templates 2024-12-12 21:28:23 -06:00
kvan7
f7d686291f Add release steps to DEVELOPING 2024-12-12 07:10:08 -06:00
kvan7
4b8e2ae8cf bump to prove im not (completely) insane 2024-12-12 06:20:22 -06:00
Kvan7
3450f39381 Update README.md 2024-12-11 21:08:34 -06:00
Kvan7
854887056e Finish updating branding (#26)
* remove debugging from proxy

* Removes leftover, fixed title oops

* bump for updating branding
2024-12-11 20:56:00 -06:00
kvan7
292e2de2dd fix lint 2024-12-11 17:23:42 -06:00
kvan7
3cda3a74b8 Add lots more testing code 2024-12-11 17:21:18 -06:00
kvan7
32215e5c03 fix name 2024-12-11 17:21:18 -06:00
kvan7
fd4917ca7f style(Update to 2): Update branding
Renamed to Exalted PoE2 Trade
2024-12-11 17:21:18 -06:00
kvan7
c3d6c69d9d change to node 18 2024-12-11 17:21:18 -06:00
Kvan7
0774814912 Update issue templates 2024-12-11 06:34:12 -06:00
55 changed files with 14064 additions and 17054 deletions

View File

@@ -0,0 +1,21 @@
---
name: An error occurred while parsing the item
about: This is probably a bug and you can report it on GitHub.
title: "[Parse Error] - item name here"
labels: bug, parse error
assignees: ''
---
## Parse Error
***Put name of item here, and in the title***
### Item Text
***Please provide the item's description***
- Can be copied with `ctrl`+`shift`+`c`
- Can also be copied from below the error message
```
Please paste it HERE
```

View File

@@ -0,0 +1,24 @@
---
name: Something Broken in PoE2
about: Use this for things that worked in PoE 1 and do not in PoE 2
title: "[PoE2] - Summary"
labels: bug
assignees: Kvan7
---
**Describe the problem**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem. This is very helpful for comparing the PoE1 vs 2 problems

22
.github/ISSUE_TEMPLATE/unknown-item.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Unknown Item
about: If this Item was introduced in this League, it will likely be supported in
the next app update.
title: "[Unknown Item] - item name here"
labels: bug, missing item
assignees: ''
---
## Unknown Item
***Put name of item here, and in the title***
### Item Text
***Please provide the item's description***
- Can be copied with `ctrl`+`shift`+`c`
- Can also be copied from below the error message
```
Please paste it HERE
```

View File

@@ -11,7 +11,7 @@ Note that these 2 both depend on each other, and one cannot run without the othe
The most up-to-date instructions can always be derived from CI: The most up-to-date instructions can always be derived from CI:
[.github/workflows/main.yml](https://github.com/Kvan7/Exiles-Exchange/blob/master/.github/workflows/main.yml) [.github/workflows/main.yml](https://github.com/Kvan7/exiled-exchange-2/blob/master/.github/workflows/main.yml)
Here's what that looks like as of 2023-12-03. Here's what that looks like as of 2023-12-03.
@@ -41,3 +41,15 @@ npm run build
# install without errors # install without errors
CSC_NAME="Certificate name in Keychain" yarn package CSC_NAME="Certificate name in Keychain" yarn package
``` ```
# How to release a build
1. Commit all changes
2. Bump version in `main/package.json`
3. `npm i` in renderer & main (update `package-lock.json` with new version)
4. `npm run build` in renderer & main
5. Stage & commit bumped version
6. `git push`
7. `git tag vX.X.X`
8. `git push origin vX.X.X`
9. Open release page, create release with tag & title as text of tag & save as draft

View File

@@ -1,16 +1,22 @@
# ![Exalted Orb](./renderer/dist/images/exa.png) Exile's Exchange # ![Exalted Orb](./renderer/public/images/exa.png) Exiled Exchange 2
## Moving from POE1 ## Moving from POE1/Awakened PoE Trade
1. Download latest release from [releases](https://github.com/Kvan7/Exiles-Exchange/releases) 1. Download latest release from [releases](https://github.com/Kvan7/exiled-exchange-2/releases)
- Currently only Windows is supported - Currently only Windows is supported
- Only available as pre-release right now - Only available as pre-release right now
2. Run installer 2. Run installer
3. Copy `apt-data` from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\Exiles-Exchange` to copy your previous settings 3. Run Exiled Exchange 2
4. Launch PoE2 to generate correct files
5. Copy `apt-data` from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exiled-exchange-2` to copy your previous settings
- Resulting directory structure should look like this: - Resulting directory structure should look like this:
- `%APPDATA%\Exiles-Exchange\apt-data\` - `%APPDATA%\exiled-exchange-2\apt-data\`
- `config.json` - `config.json`
4. Run Exiles Exchange 6. Restart Exiled Exchange 2
#### Updating from 0.0.1 -> 0.0.10
Follow same steps as tranfering from PoE1, instead of copying from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exiled-exchange-2` instead copy from `%APPDATA%\awakened-poe2-trade` or `%APPDATA%\awakened-poe2-trade2` to `%APPDATA%\exiled-exchange-2`. After copying files feel free to run the uninstaller for `awakened-poe2-trade` to remove old executables, or delete `%APPDATA%\awakened-poe2-trade` and `Local\Programs\awakened-poe2-trade`
## Tool showcase ## Tool showcase

View File

@@ -1,9 +1,9 @@
import { defineConfig } from 'vitepress' import { defineConfig } from 'vitepress'
const BASE = '/Exiles-Exchange/' const BASE = '/exiled-exchange-2/'
export default defineConfig({ export default defineConfig({
title: 'Exiles Exchange', title: 'Exiled Exchange 2',
description: 'App for price-checking items in Path of Exile 2', description: 'App for price-checking items in Path of Exile 2',
base: BASE, base: BASE,
mpa: true, mpa: true,
@@ -22,7 +22,7 @@ export default defineConfig({
// logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401 // logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401
appVersion: '3.25.101', appVersion: '3.25.101',
github: { github: {
releasesUrl: 'https://github.com/Kvan7/Exiles-Exchange/releases' releasesUrl: 'https://github.com/Kvan7/exiled-exchange-2/releases'
}, },
socialLinks: [ socialLinks: [
{ {
@@ -33,7 +33,7 @@ export default defineConfig({
{ {
text: 'GitHub', text: 'GitHub',
color: '#181717', color: '#181717',
link: 'https://github.com/Kvan7/Exiles-Exchange' link: 'https://github.com/Kvan7/exiled-exchange-2'
} }
], ],
sidebar: [ sidebar: [

View File

@@ -8,15 +8,15 @@ import { useData } from 'vitepress'
const { theme } = useData() const { theme } = useData()
</script> </script>
You can download Awakened Poe Trade here. Any other mirrors are not known You can download Exalted Poe Trade here. Any other mirrors are not known
to the developer, downloading from them may be unsafe. to the developer, downloading from them may be unsafe.
| Download link | Automatic updates | Startup time | | Download link | Automatic updates | Startup time |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | ------------ | | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------------ |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exiles-Exchange-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/exiled-exchange-2-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exiles-Exchange-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/exiled-exchange-2-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exiles-Exchange-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/exiled-exchange-2-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a |
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exiles-Exchange-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a | | <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/exiled-exchange-2-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a |
Latest version is <span class="bg-gray-100 border rounded px-1">{{ theme.appVersion }}</span> Latest version is <span class="bg-gray-100 border rounded px-1">{{ theme.appVersion }}</span>
@@ -36,6 +36,6 @@ warnings on Windows and [macOS](https://support.apple.com/en-us/HT202491#openany
No Administrator rights required, but\ No Administrator rights required, but\
⚠ **If you run PoE client as Admin, OS security boundaries take effect. ⚠ **If you run PoE client as Admin, OS security boundaries take effect.
In order for Exiles Exchange to have access to the PoE window, it must be started with Administrator rights.** In order for Exiled Exchange 2 to have access to the PoE window, it must be started with Administrator rights.**
**Not compatible with "GeForce Now" or any other cloud gaming service that do not forward clipboard data.** **Not compatible with "GeForce Now" or any other cloud gaming service that do not forward clipboard data.**

View File

@@ -14,7 +14,7 @@ title: Common issues
If Awakened works for you with DirectX11/12 renderer, If Awakened works for you with DirectX11/12 renderer,
then problem is old Vulkan drivers for sure. then problem is old Vulkan drivers for sure.
4. Delete `%appdata%\Exiles-Exchange` 4. Delete `%appdata%\exiled-exchange-2`
If needed, backup `apt-data` folder with your configuration inside. If needed, backup `apt-data` folder with your configuration inside.
@@ -22,7 +22,7 @@ title: Common issues
Launch them later one at a time to identify **conflict**. Launch them later one at a time to identify **conflict**.
6. Restart Exiles Exchange. 6. Restart Exiled Exchange 2.
*(don't forget to quit first, otherwise launching second instance will do nothing).* *(don't forget to quit first, otherwise launching second instance will do nothing).*

View File

@@ -5,7 +5,7 @@ title: Quick Start
#### First of all, how does it work? {:style="margin-top: 0;"} #### First of all, how does it work? {:style="margin-top: 0;"}
When you press `Ctrl + C` Path of Exile 2 copies the item's text (under cursor, if any) to the clipboard. When you press `Ctrl + C` Path of Exile 2 copies the item's text (under cursor, if any) to the clipboard.
All that remains is to parse text in Exiles Exchange and show to you in a fancy way. All that remains is to parse text in Exiled Exchange 2 and show to you in a fancy way.
### Usage ### Usage

View File

@@ -25,8 +25,18 @@
"dist/": true "dist/": true
}, },
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.insertSpaces": true,
"conventionalCommits.scopes": [ "conventionalCommits.scopes": [
"Update to 2" "Update to 2"
],
"cSpell.words": [
"edps",
"ilvl",
"nonunique",
"onlineleague",
"pdps",
"uniquefoil",
"WAYSTONE"
] ]
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 841 KiB

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
main/build/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,6 +1,6 @@
publish: publish:
- "github" - "github"
productName: "Exiles Exchange" productName: "Exiled Exchange 2"
npmRebuild: false npmRebuild: false
files: files:
- "package.json" - "package.json"

11305
main/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,38 @@
{ {
"name": "Exiles-Exchange", "name": "exiled-exchange-2",
"version": "0.0.5", "version": "0.0.12",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "node build/script.mjs", "dev": "node build/script.mjs",
"build": "tsc --noEmit && node build/script.mjs --prod", "build": "tsc --noEmit && node build/script.mjs --prod",
"package": "electron-builder build" "package": "electron-builder build",
}, "lint": "eslint src",
"author": { "fix": "eslint src --fix"
"name": "Alexander Drozdov" },
}, "author": {
"repository": { "name": "Garrett Parker"
"type": "git", },
"url": "https://github.com/Kvan7/Exiles-Exchange.git" "repository": {
}, "type": "git",
"main": "dist/main.js", "url": "https://github.com/Kvan7/exiled-exchange-2.git"
"dependencies": { },
"electron-overlay-window": "3.3.0", "main": "dist/main.js",
"uiohook-napi": "1.5.x" "dependencies": {
}, "electron-overlay-window": "3.3.0",
"devDependencies": { "uiohook-napi": "1.5.x"
"@types/ini": "^4.1.0", },
"@types/node": "20.x.x", "devDependencies": {
"@types/ws": "^8.5.3", "@types/ini": "^4.1.0",
"@wokwi/bmp-ts": "^3.0.0", "@types/node": "20.x.x",
"comlink": "^4.3.1", "@types/ws": "^8.5.3",
"electron": "33.2.1", "@wokwi/bmp-ts": "^3.0.0",
"electron-builder": "25.1.8", "comlink": "^4.3.1",
"electron-updater": "^6.3.0", "electron": "33.2.1",
"esbuild": "^0.24.0", "electron-builder": "25.1.8",
"ini": "^5.0.0", "electron-updater": "^6.3.0",
"typescript": "5.6.x", "esbuild": "^0.24.0",
"ws": "^8.16.0" "ini": "^5.0.0",
}, "typescript": "5.6.x",
"engines": { "ws": "^8.16.0"
"node": ">=16" }
}
} }

View File

@@ -23,7 +23,7 @@ export class AppTray {
} }
this.tray = new Tray(trayImage); this.tray = new Tray(trayImage);
this.tray.setToolTip(`Exiles Exchange v${app.getVersion()}`); this.tray.setToolTip(`Exiled Exchange 2 v${app.getVersion()}`);
this.rebuildMenu(); this.rebuildMenu();
server.onEventAnyClient("CLIENT->MAIN::user-action", ({ action }) => { server.onEventAnyClient("CLIENT->MAIN::user-action", ({ action }) => {

View File

@@ -18,18 +18,10 @@ export class HttpProxy {
) { ) {
server.addListener('request', (req, res) => { server.addListener('request', (req, res) => {
if (!req.url?.startsWith('/proxy/')) return if (!req.url?.startsWith('/proxy/')) return
const host = req.url.split('/', 3)[2] const host = req.url.split('/', 3)[2]
logger.write(`Incoming request to proxy: ${req.url}`); // Log incoming request
const official = PROXY_HOSTS.find(entry => entry.host === host)?.official const official = PROXY_HOSTS.find(entry => entry.host === host)?.official
if (official === undefined) { if (official === undefined) return req.destroy()
logger.write(`Host not officially supported: ${host}`);
return req.destroy() // Log rejection on unsupported host
}
// Log headers before modifying them
logger.write(`Incoming request headers: ${JSON.stringify(req.headers)}`);
for (const key in req.headers) { for (const key in req.headers) {
if (key.startsWith('sec-') || key === 'host' || key === 'origin' || key === 'content-length') { if (key.startsWith('sec-') || key === 'host' || key === 'origin' || key === 'content-length') {
@@ -37,35 +29,28 @@ export class HttpProxy {
} }
} }
const url = req.url.slice('/proxy/'.length);
const proxyReq = net.request({ const proxyReq = net.request({
url: 'https://' + url, url: 'https://' + req.url.slice('/proxy/'.length),
method: req.method, method: req.method,
headers: { headers: {
...req.headers, ...req.headers,
'user-agent': app.userAgentFallback 'user-agent': app.userAgentFallback
}, },
useSessionCookies: true useSessionCookies: true,
referrerPolicy: 'no-referrer-when-downgrade'
}) })
proxyReq.addListener('response', (proxyRes) => { proxyReq.addListener('response', (proxyRes) => {
const resHeaders = { ...proxyRes.headers } const resHeaders = { ...proxyRes.headers }
// Log response status and headers
logger.write(`Proxy response status: ${proxyRes.statusCode}, status message: ${proxyRes.statusMessage}`);
logger.write(`Proxy response headers: ${JSON.stringify(resHeaders)}`);
delete resHeaders['content-encoding'] delete resHeaders['content-encoding']
res.writeHead(proxyRes.statusCode, proxyRes.statusMessage, resHeaders) res.writeHead(proxyRes.statusCode, proxyRes.statusMessage, resHeaders)
;(proxyRes as unknown as NodeJS.ReadableStream).pipe(res) ;(proxyRes as unknown as NodeJS.ReadableStream).pipe(res)
}) })
proxyReq.addListener('error', (err) => { proxyReq.addListener('error', (err) => {
logger.write(`Error during proxy request: ${err.message} (${host}); is this a network error?`); logger.write(`error [cors-proxy] ${err.message} (${host})`)
res.writeHead(502, 'Bad Gateway'); res.destroy(err)
res.end(`Proxy error: ${err.message}`);
}) })
req.pipe(proxyReq as unknown as NodeJS.WritableStream); req.pipe(proxyReq as unknown as NodeJS.WritableStream)
}) })
} }
} }

View File

@@ -161,7 +161,7 @@ export class OverlayWindow {
// ---------------------- // ----------------------
"Path of Exile 2 is running with administrator rights.\n" + "Path of Exile 2 is running with administrator rights.\n" +
"\n" + "\n" +
"You need to restart Exiles Exchange with administrator rights." "You need to restart Exiled Exchange 2 with administrator rights."
); );
} else { } else {
this.server.sendEventTo("broadcast", { this.server.sendEventTo("broadcast", {

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="color-scheme" content="dark"> <meta name="color-scheme" content="dark">
<link rel="icon" href="/icon.ico"> <link rel="icon" href="/icon.ico">
<title>Exiles Exchange</title> <title>Exiled Exchange 2</title>
</head> </head>
<body> <body>

12786
renderer/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,63 +1,63 @@
{ {
"name": "Exiles-Exchange", "name": "exiled-exchange-2",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"lint": "eslint --ext .ts,.vue src", "lint": "eslint --ext .ts,.vue src",
"lint-fix": "eslint --ext .ts,.vue src --fix", "lint-fix": "eslint --ext .ts,.vue src --fix",
"build": "vue-tsc --noEmit && vite build", "build": "vue-tsc --noEmit && vite build",
"make-index-files": "node src/assets/make-index-files.mjs" "make-index-files": "node src/assets/make-index-files.mjs"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "6.x.x", "@fortawesome/fontawesome-free": "6.x.x",
"@sindresorhus/fnv1a": "^3.0.0", "@sindresorhus/fnv1a": "^3.0.0",
"@vueuse/core": "^11.0.0", "@vueuse/core": "^11.0.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"apexcharts": "^4.0.0", "apexcharts": "^4.0.0",
"dot-prop": "9.x.x", "dot-prop": "9.x.x",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fastest-levenshtein": "^1.0.16", "fastest-levenshtein": "^1.0.16",
"luxon": "3.x.x", "luxon": "3.x.x",
"neverthrow": "^8.0.0", "neverthrow": "^8.0.0",
"object-hash": "^3.0.0", "object-hash": "^3.0.0",
"sockette": "^2.0.6", "sockette": "^2.0.6",
"tailwindcss": "3.x.x", "tailwindcss": "3.x.x",
"tippy.js": "^6.2.7", "tippy.js": "^6.2.7",
"vue": "3.2.37", "vue": "3.2.37",
"vue-i18n": "^10.0.0", "vue-i18n": "^10.0.0",
"vue3-apexcharts": "^1.1.1", "vue3-apexcharts": "^1.1.1",
"vuedraggable": "4.1.0" "vuedraggable": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/luxon": "^3.0.0", "@types/luxon": "^3.0.0",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@types/object-hash": "^3.0.0", "@types/object-hash": "^3.0.0",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"autoprefixer": "^10.0.2", "autoprefixer": "^10.0.2",
"postcss": "^8.2.14", "postcss": "^8.2.14",
"typescript": "5.6.x", "typescript": "5.6.x",
"vite": "^5.0.0", "vite": "^5.0.0",
"vue-tsc": "^2.0.0" "vue-tsc": "^2.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.21.0", "eslint": "^8.21.0",
"eslint-config-standard-with-typescript": "^31.0.0", "eslint-config-standard-with-typescript": "^31.0.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-n": "^15.0.0", "eslint-plugin-n": "^15.0.0",
"eslint-plugin-promise": "^6.0.0", "eslint-plugin-promise": "^6.0.0",
"eslint-plugin-vue": "^9.1.1" "eslint-plugin-vue": "^9.1.1"
}, },
"postcss": { "postcss": {
"plugins": { "plugins": {
"tailwindcss/nesting": {}, "tailwindcss/nesting": {},
"tailwindcss": {}, "tailwindcss": {},
"autoprefixer": {} "autoprefixer": {}
} }
}, },
"browserslist": [ "browserslist": [
"chrome >= 101" "chrome >= 101"
] ]
} }

View File

@@ -50,6 +50,7 @@
"elemental_dps": "Elemental DPS: {0}", "elemental_dps": "Elemental DPS: {0}",
"crit": "Critical Strike Chance: {0}%", "crit": "Critical Strike Chance: {0}%",
"aps": "Attacks per Second: {0}", "aps": "Attacks per Second: {0}",
"spirit": "Spirit: {0}",
"has_empty_modifier": "1 Empty or Crafted Modifier", "has_empty_modifier": "1 Empty or Crafted Modifier",
"has_empty_affix": "Any", "has_empty_affix": "Any",
"has_empty_prefix": "Prefix", "has_empty_prefix": "Prefix",
@@ -63,6 +64,8 @@
"white_sockets": "White: {0}", "white_sockets": "White: {0}",
"quality": "Quality: {0}", "quality": "Quality: {0}",
"gem_level": "Level: {0}", "gem_level": "Level: {0}",
"gem_sockets": "Sockets: {0}",
"rune_sockets": "Sockets: {0}",
"sentinel_charge": "Charge: {0}", "sentinel_charge": "Charge: {0}",
"find_in_stash": "Find in Stash", "find_in_stash": "Find in Stash",
"parse_error": "An error occurred while parsing the item", "parse_error": "An error occurred while parsing the item",
@@ -269,7 +272,7 @@
"stack": "Stack" "stack": "Stack"
}, },
"settings": { "settings": {
"title": "Settings - Exiles Exchange", "title": "Settings - Exiled Exchange 2",
"language": "Language", "language": "Language",
"private_league": "or Private League", "private_league": "or Private League",
"account_name": "Account name", "account_name": "Account name",

File diff suppressed because it is too large Load Diff

View File

@@ -266,7 +266,7 @@
"stack": "스택" "stack": "스택"
}, },
"settings": { "settings": {
"title": "세팅 - Exiles Exchange", "title": "세팅 - Exiled Exchange 2",
"language": "언어", "language": "언어",
"private_league": "개인리그", "private_league": "개인리그",
"account_name": "계정명", "account_name": "계정명",

View File

@@ -282,7 +282,7 @@
"stack": "Стак" "stack": "Стак"
}, },
"settings": { "settings": {
"title": "Настройки - Exiles Exchange", "title": "Настройки - Exiled Exchange 2",
"language": "Язык", "language": "Язык",
"private_league": "или Приватная лига", "private_league": "или Приватная лига",
"account_name": "Имя учетной записи", "account_name": "Имя учетной записи",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -48,6 +48,9 @@ export enum ItemCategory {
Tincture = 'Tincture', Tincture = 'Tincture',
Charm = 'Charm', Charm = 'Charm',
Crossbow = 'Crossbow', Crossbow = 'Crossbow',
SkillGem = 'Skill Gem',
SupportGem = 'Support Gem',
MetaGem = 'Meta Gem',
} }
export const WEAPON_ONE_HANDED_MELEE = new Set([ export const WEAPON_ONE_HANDED_MELEE = new Set([
@@ -76,6 +79,7 @@ export const WEAPONE_TWO_HANDED_MELEE = new Set([
export const WEAPON = new Set([ export const WEAPON = new Set([
ItemCategory.FishingRod, ItemCategory.FishingRod,
ItemCategory.Bow, ItemCategory.Bow,
ItemCategory.Crossbow,
...WEAPON_ONE_HANDED, ...WEAPON_ONE_HANDED,
...WEAPONE_TWO_HANDED_MELEE ...WEAPONE_TWO_HANDED_MELEE
]) ])

View File

@@ -76,9 +76,9 @@ export async function initConfig () {
// TODO // TODO
// dialog.showErrorBox( // dialog.showErrorBox(
// 'Exiles Exchange - Incompatible configuration', // 'Exiled Exchange 2 - Incompatible configuration',
// // ---------------------- // // ----------------------
// 'You are trying to use an older version of Exiles Exchange with a newer incompatible configuration file.\n' + // 'You are trying to use an older version of Exiled Exchange 2 with a newer incompatible configuration file.\n' +
// 'You need to install the latest version to continue using it.' // 'You need to install the latest version to continue using it.'
// ) // )
} }

View File

@@ -8,7 +8,7 @@
:class="$style.starredItem" :class="$style.starredItem"
@click="starredItemClick($event, item)"> @click="starredItemClick($event, item)">
<ItemQuickPrice <ItemQuickPrice
:item-img="item.info.icon" :item-img="item.info.icon === '%NOT_FOUND%' || item.info.icon === '' ? '/images/404.png' : item.info.icon"
:price="item.price" :price="item.price"
currency-text currency-text
></ItemQuickPrice> ></ItemQuickPrice>

View File

@@ -4,7 +4,7 @@
<div :class="$style.widget" v-if="show"> <div :class="$style.widget" v-if="show">
<div :class="$style.box"> <div :class="$style.box">
<div class="py-2 px-4"> <div class="py-2 px-4">
<div class="text-base">Exiles Exchange</div> <div class="text-base">Exiled Exchange 2</div>
<p>{{ t('app_is_ready') }}</p> <p>{{ t('app_is_ready') }}</p>
</div> </div>
</div> </div>
@@ -25,7 +25,9 @@ const show = shallowRef(false)
Host.onEvent('MAIN->OVERLAY::overlay-attached', () => { Host.onEvent('MAIN->OVERLAY::overlay-attached', () => {
if (!show.value && AppConfig().showAttachNotification) { if (!show.value && AppConfig().showAttachNotification) {
show.value = true show.value = true
setTimeout(() => { show.value = false }, 2500) setTimeout(() => {
show.value = false
}, 2500)
} }
}) })
</script> </script>
@@ -51,7 +53,7 @@ Host.onEvent('MAIN->OVERLAY::overlay-attached', () => {
.box::before { .box::before {
position: absolute; position: absolute;
content: ''; content: '';
background: url('/images/TransferOrb.png') no-repeat top right/contain; background: url('/images/exa.png') no-repeat top right/contain;
right: 100%; right: 100%;
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@@ -4,9 +4,9 @@
<price-prediction v-if="showPredictedPrice" class="mb-4" :item="item" /> <price-prediction v-if="showPredictedPrice" class="mb-4" :item="item" />
<price-trend v-else :item="item" :filters="itemFilters" /> <price-trend v-else :item="item" :filters="itemFilters" />
<filters-block ref="filtersComponent" :filters="itemFilters" :stats="itemStats" :item="item" :presets="presets" <filters-block ref="filtersComponent" :filters="itemFilters" :stats="itemStats" :item="item" :presets="presets"
@preset="selectPreset" @submit="doSearch = true" /> @preset="selectPreset" @submit="doSearch = true" />
<trade-listing v-if="tradeAPI === 'trade' && doSearch" ref="tradeService" :filters="itemFilters" :stats="itemStats" <trade-listing v-if="tradeAPI === 'trade' && doSearch" ref="tradeService" :filters="itemFilters" :stats="itemStats"
:item="item" /> :item="item" />
<trade-bulk v-if="tradeAPI === 'bulk' && doSearch" ref="tradeService" :filters="itemFilters" :item="item" /> <trade-bulk v-if="tradeAPI === 'bulk' && doSearch" ref="tradeService" :filters="itemFilters" :item="item" />
<div v-if="!doSearch" class="flex justify-between items-center"> <div v-if="!doSearch" class="flex justify-between items-center">
<div class="flex w-40" @mouseenter="handleSearchMouseenter"> <div class="flex w-40" @mouseenter="handleSearchMouseenter">

View File

@@ -1,23 +1,52 @@
<template> <template>
<div style="top: 0; left: 0; height: 100%; width: 100%; position: absolute;" <div
class="flex grow h-full pointer-events-none" :class="{ style="top: 0; left: 0; height: 100%; width: 100%; position: absolute"
class="flex grow h-full pointer-events-none"
:class="{
'flex-row': clickPosition === 'stash', 'flex-row': clickPosition === 'stash',
'flex-row-reverse': clickPosition === 'inventory', 'flex-row-reverse': clickPosition === 'inventory',
}"> }"
<div v-if="!isBrowserShown" class="layout-column shrink-0" style="width: var(--game-panel);"> >
</div> <div
<div id="price-window" class="layout-column shrink-0 text-gray-200 pointer-events-auto" style="width: 28.75rem;"> v-if="!isBrowserShown"
class="layout-column shrink-0"
style="width: var(--game-panel)"
></div>
<div
id="price-window"
class="layout-column shrink-0 text-gray-200 pointer-events-auto"
style="width: 28.75rem"
>
<ConversionWarningBanner /> <ConversionWarningBanner />
<AppTitleBar @close="closePriceCheck" @click="openLeagueSelection" :title="title"> <AppTitleBar
<ui-popover v-if="stableOrbCost" trigger="click" boundary="#price-window"> @close="closePriceCheck"
@click="openLeagueSelection"
:title="title"
>
<ui-popover
v-if="stableOrbCost"
trigger="click"
boundary="#price-window"
>
<template #target> <template #target>
<button><i class="fas fa-exchange-alt" /> {{ stableOrbCost }}</button> <button>
<i class="fas fa-exchange-alt" /> {{ stableOrbCost }}
</button>
</template> </template>
<template #content> <template #content>
<item-quick-price class="text-base" :price="{ min: stableOrbCost, max: stableOrbCost, currency: 'chaos' }" <item-quick-price
item-img="/images/divine.png" /> class="text-base"
:price="{
min: stableOrbCost,
max: stableOrbCost,
currency: 'chaos',
}"
item-img="/images/divine.png"
/>
<div v-for="i in 9" :key="i"> <div v-for="i in 9" :key="i">
<div class="pl-1">{{ i / 10 }} div {{ Math.round(stableOrbCost * i / 10) }} c</div> <div class="pl-1">
{{ i / 10 }} div {{ Math.round((stableOrbCost * i) / 10) }} c
</div>
</div> </div>
</template> </template>
</ui-popover> </ui-popover>
@@ -26,33 +55,61 @@
</AppTitleBar> </AppTitleBar>
<div class="grow layout-column min-h-0 bg-gray-800"> <div class="grow layout-column min-h-0 bg-gray-800">
<background-info /> <background-info />
<check-position-circle v-if="showCheckPos" :position="checkPosition" style="z-index: -1;" /> <check-position-circle
v-if="showCheckPos"
:position="checkPosition"
style="z-index: -1"
/>
<template v-if="item?.isErr()"> <template v-if="item?.isErr()">
<ui-error-box class="m-4"> <ui-error-box class="m-4">
<template #name>{{ t(item.error.name) }}</template> <template #name>{{ t(item.error.name) }}</template>
<p>{{ t(item.error.message) }}</p> <p>{{ t(item.error.message) }}</p>
</ui-error-box> </ui-error-box>
<pre class="bg-gray-900 rounded m-4 overflow-x-hidden p-2">{{ item.error.rawText }}</pre> <pre class="bg-gray-900 rounded m-4 overflow-x-hidden p-2">{{
item.error.rawText
}}</pre>
</template> </template>
<template v-else-if="item?.isOk()"> <template v-else-if="item?.isOk()">
<unidentified-resolver :item="item.value" @identify="handleIdentification($event)" /> <unidentified-resolver
<checked-item v-if="isLeagueSelected" :item="item.value" :advanced-check="advancedCheck" /> :item="item.value"
@identify="handleIdentification($event)"
/>
<checked-item
v-if="isLeagueSelected"
:item="item.value"
:advanced-check="advancedCheck"
/>
</template> </template>
<div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate"> <div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate">
<i18n-t keypath="app.toggle_browser_hint" tag="div"> <i18n-t keypath="app.toggle_browser_hint" tag="div">
<span class="bg-gray-400 text-gray-900 rounded px-1">{{ overlayKey }}</span> <span class="bg-gray-400 text-gray-900 rounded px-1">{{
overlayKey
}}</span>
</i18n-t> </i18n-t>
</div> </div>
</div> </div>
</div> </div>
<webview v-if="isBrowserShown" ref="iframeEl" class="pointer-events-auto flex-1" width="100%" height="100%" /> <webview
v-if="isBrowserShown"
ref="iframeEl"
class="pointer-events-auto flex-1"
width="100%"
height="100%"
/>
<div v-else class="layout-column flex-1 min-w-0"> <div v-else class="layout-column flex-1 min-w-0">
<div class="flex" :class="{ <div
'flex-row': clickPosition === 'stash', class="flex"
'flex-row-reverse': clickPosition === 'inventory' :class="{
}"> 'flex-row': clickPosition === 'stash',
<related-items v-if="item?.isOk()" class="pointer-events-auto" :item="item.value" 'flex-row-reverse': clickPosition === 'inventory',
:click-position="clickPosition" /> }"
>
<related-items
v-if="item?.isOk()"
class="pointer-events-auto"
:item="item.value"
:click-position="clickPosition"
/>
<rate-limiter-state class="pointer-events-auto" /> <rate-limiter-state class="pointer-events-auto" />
</div> </div>
</div> </div>
@@ -81,7 +138,11 @@ import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue'
import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces' import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces'
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue' import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
type ParseError = { name: string; message: string; rawText: ParsedItem['rawText'] } type ParseError = {
name: string;
message: string;
rawText: ParsedItem['rawText'];
};
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -122,9 +183,13 @@ export default defineComponent({
if (Host.isElectron && !e.focusOverlay) { if (Host.isElectron && !e.focusOverlay) {
// everything in CSS pixels // everything in CSS pixels
const width = 28.75 * AppConfig().fontSize const width = 28.75 * AppConfig().fontSize
const screenX = ((e.position.x - window.screenX) > window.innerWidth / 2) const screenX =
? (window.screenX + window.innerWidth) - wm.poePanelWidth.value - width e.position.x - window.screenX > window.innerWidth / 2
: window.screenX + wm.poePanelWidth.value ? window.screenX +
window.innerWidth -
wm.poePanelWidth.value -
width
: window.screenX + wm.poePanelWidth.value
MainProcess.sendEvent({ MainProcess.sendEvent({
name: 'OVERLAY->MAIN::track-area', name: 'OVERLAY->MAIN::track-area',
payload: { payload: {
@@ -146,13 +211,18 @@ export default defineComponent({
checkPosition.value = e.position checkPosition.value = e.position
advancedCheck.value = e.focusOverlay advancedCheck.value = e.focusOverlay
item.value = (e.item ? ok(e.item as ParsedItem) : parseClipboard(e.clipboard)) item.value = (
.andThen(item => ( e.item ? ok(e.item as ParsedItem) : parseClipboard(e.clipboard)
(item.category === ItemCategory.HeistContract && item.rarity !== ItemRarity.Unique) || )
(item.category === ItemCategory.Sentinel && item.rarity !== ItemRarity.Unique)) .andThen((item) =>
? err('item.unknown') (item.category === ItemCategory.HeistContract &&
: ok(item)) item.rarity !== ItemRarity.Unique) ||
.mapErr(err => ({ (item.category === ItemCategory.Sentinel &&
item.rarity !== ItemRarity.Unique)
? err('item.unknown')
: ok(item)
)
.mapErr((err) => ({
name: `${err}`, name: `${err}`,
message: `${err}_help`, message: `${err}_help`,
rawText: e.clipboard rawText: e.clipboard
@@ -171,14 +241,17 @@ export default defineComponent({
wm.hide(props.config.wmId) wm.hide(props.config.wmId)
}) })
watch(() => props.config.wmWants, (state) => { watch(
if (state === 'hide') { () => props.config.wmWants,
closeBrowser() (state) => {
if (state === 'hide') {
closeBrowser()
}
} }
}) )
const leagues = useLeagues() const leagues = useLeagues()
const title = computed(() => leagues.selectedId.value || 'Exiles Exchange') const title = computed(() => leagues.selectedId.value || 'Exiled Exchange 2')
const stableOrbCost = computed(() => (xchgRate.value) ? Math.round(xchgRate.value) : null) const stableOrbCost = computed(() => (xchgRate.value) ? Math.round(xchgRate.value) : null)
const isBrowserShown = computed(() => props.config.wmFlags.includes('has-browser')) const isBrowserShown = computed(() => props.config.wmFlags.includes('has-browser'))
const overlayKey = computed(() => AppConfig().overlayKey) const overlayKey = computed(() => AppConfig().overlayKey)

View File

@@ -5,7 +5,7 @@
> >
<template #item> <template #item>
<div class="flex"> <div class="flex">
<img v-for="icon in result.icons" class="w-8 h-8" :src="icon"> <img v-for="icon in result.icons" class="w-8 h-8" :src="icon === '%NOT_FOUND%' || icon === '' ? '/images/404.png' : icon">
</div> </div>
</template> </template>
</item-quick-price> </item-quick-price>

View File

@@ -48,6 +48,19 @@
: t('filters.selected_none')" : t('filters.selected_none')"
/> />
</div> </div>
<!-- Warning that many stats may not work -->
<div v-if="!statsVisibility.disabled && hasStats" class="mb-4 text-center bg-teal-800 rounded-xl" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'" >
Not all stats may load (WIP)
</div>
<!-- Handled parse error -->
<div v-if="!hasStats && item.rarity !== undefined && item.rarity !== 'Normal'" class="mb-4 text-center bg-purple-800 rounded-xl" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'" >
For some reason this item's stats did not parse. <br/> It likely has a mod that is not supported yet. <br/>(Potentially new or changed wording from poe1)
</div>
<!-- Warning that bulk exchange does not work -->
<div v-if="!hasStats && item.category === 'Currency'" class="mb-4 text-center bg-teal-900 rounded-xl" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'" >
Bulk exchange rates are not implemented yet
</div>
<div v-if="!statsVisibility.disabled && hasStats" class="mb-4" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'"> <div v-if="!statsVisibility.disabled && hasStats" class="mb-4" :class="(presets.length > 1) ? 'mt-1' : 'mt-4'">
<div class="flex" v-if="presets.length > 1"> <div class="flex" v-if="presets.length > 1">
<div class="w-5 border-b border-gray-700" /> <div class="w-5 border-b border-gray-700" />
@@ -134,6 +147,8 @@ export default defineComponent({
statsVisibility.disabled = false statsVisibility.disabled = false
}) })
console.log(props.item)
const showUnknownMods = computed(() => const showUnknownMods = computed(() =>
props.item.unknownModifiers.length && props.item.unknownModifiers.length &&
props.item.category !== ItemCategory.Sentinel && props.item.category !== ItemCategory.Sentinel &&

View File

@@ -116,7 +116,8 @@ export const INTERNAL_TRADE_IDS = [
'item.elemental_dps', 'item.elemental_dps',
'item.crit', 'item.crit',
'item.aps', 'item.aps',
'item.has_empty_modifier' 'item.has_empty_modifier',
'item.spirit'
] as const ] as const
export type InternalTradeId = typeof INTERNAL_TRADE_IDS[number] export type InternalTradeId = typeof INTERNAL_TRADE_IDS[number]

View File

@@ -13,7 +13,7 @@
<div class="flex items-center pb-4"> <div class="flex items-center pb-4">
<item-quick-price class="flex-1 text-base justify-center" <item-quick-price class="flex-1 text-base justify-center"
:price="price" :price="price"
:item-img="item.info.icon" :item-img="item.info.icon === '%NOT_FOUND%' || item.info.icon === '' ? '/images/404.png' : item.info.icon"
:item-base="item.info" :item-base="item.info"
approx approx
/> />

View File

@@ -8,7 +8,7 @@
:class="{ 'bg-gray-700': item.highlight }" class="rounded px-1"> :class="{ 'bg-gray-700': item.highlight }" class="rounded px-1">
<item-quick-price currency-text fraction class="text-base" <item-quick-price currency-text fraction class="text-base"
:price="item.price" :price="item.price"
:item-img="item.icon" /> :item-img="item.icon === '%NOT_FOUND%' || item.icon === '' ? '/images/404.png' : item.icon" />
<div class="text-left text-gray-600 mb-1 whitespace-nowrap overflow-hidden">{{ item.name }}</div> <div class="text-left text-gray-600 mb-1 whitespace-nowrap overflow-hidden">{{ item.name }}</div>
</div> </div>
</div> </div>
@@ -16,7 +16,7 @@
<div v-for="item in result.items" :key="item.name"> <div v-for="item in result.items" :key="item.name">
<item-quick-price currency-text fraction class="text-base" <item-quick-price currency-text fraction class="text-base"
:price="item.price" :price="item.price"
:item-img="item.icon" /> :item-img="item.icon === '%NOT_FOUND%' || item.icon === '' ? '/images/404.png' : item.icon" />
<div class="text-left text-gray-600 mb-1 whitespace-nowrap overflow-hidden">{{ item.name }}</div> <div class="text-left text-gray-600 mb-1 whitespace-nowrap overflow-hidden">{{ item.name }}</div>
</div> </div>
</div> </div>

View File

@@ -22,6 +22,7 @@ export type TradeResponse<T> = (T & { error?: null }) | {
} }
export function apiToSatisfySearch (item: ParsedItem, stats: StatFilter[], filters: ItemFilters): 'trade' | 'bulk' { export function apiToSatisfySearch (item: ParsedItem, stats: StatFilter[], filters: ItemFilters): 'trade' | 'bulk' {
console.log('item', item, 'filter', filters, 'stats', stats)
if (stats.some(s => !s.disabled)) { if (stats.some(s => !s.disabled)) {
return 'trade' return 'trade'
} }

View File

@@ -1,4 +1,4 @@
import { ItemInfluence, ItemCategory, ParsedItem, ItemRarity } from '@/parser' import { ItemCategory, ParsedItem, ItemRarity } from '@/parser'
import { import {
ItemFilters, ItemFilters,
StatFilter, StatFilter,
@@ -60,7 +60,11 @@ export const CATEGORY_TO_TRADE_ID = new Map([
[ItemCategory.Trinket, 'accessory.trinket'], [ItemCategory.Trinket, 'accessory.trinket'],
[ItemCategory.SanctumRelic, 'sanctum.relic'], [ItemCategory.SanctumRelic, 'sanctum.relic'],
[ItemCategory.Tincture, 'tincture'], [ItemCategory.Tincture, 'tincture'],
[ItemCategory.Charm, 'azmeri.charm'] [ItemCategory.Charm, 'azmeri.charm'],
[ItemCategory.Crossbow, 'weapon.crossbow'],
[ItemCategory.SkillGem, 'gem.activegem'],
[ItemCategory.SupportGem, 'gem.supportgem'],
[ItemCategory.MetaGem, 'gem.metagem']
]) ])
const TOTAL_MODS_TEXT = { const TOTAL_MODS_TEXT = {
@@ -77,14 +81,14 @@ const TOTAL_MODS_TEXT = {
TOTAL_MODIFIERS: ['# Modifiers', '# Prefix Modifiers', '# Suffix Modifiers'] TOTAL_MODIFIERS: ['# Modifiers', '# Prefix Modifiers', '# Suffix Modifiers']
} }
const INFLUENCE_PSEUDO_TEXT = { // const INFLUENCE_PSEUDO_TEXT = {
[ItemInfluence.Shaper]: 'Has Shaper Influence', // [ItemInfluence.Shaper]: 'Has Shaper Influence',
[ItemInfluence.Crusader]: 'Has Crusader Influence', // [ItemInfluence.Crusader]: 'Has Crusader Influence',
[ItemInfluence.Hunter]: 'Has Hunter Influence', // [ItemInfluence.Hunter]: 'Has Hunter Influence',
[ItemInfluence.Elder]: 'Has Elder Influence', // [ItemInfluence.Elder]: 'Has Elder Influence',
[ItemInfluence.Redeemer]: 'Has Redeemer Influence', // [ItemInfluence.Redeemer]: 'Has Redeemer Influence',
[ItemInfluence.Warlord]: 'Has Warlord Influence' // [ItemInfluence.Warlord]: 'Has Warlord Influence'
} // }
interface FilterBoolean { interface FilterBoolean {
option?: 'true' | 'false' option?: 'true' | 'false'
@@ -123,72 +127,64 @@ interface TradeRequest {
category?: { category?: {
option?: string option?: string
} }
ilvl?: FilterRange
quality?: FilterRange
} }
} }
socket_filters?: { equipment_filters?: {
filters: { filters: {
links?: FilterRange // Attacks per Second
sockets?: { aps?: FilterRange
w?: number // Armor Rating
} ar?: FilterRange
// Block
block?: FilterRange
// Critical Strike Chance
crit?: FilterRange
// Damage (not used)
// damage?: FilterRange
// Damage per Second
dps?: FilterRange
// Elemental Damage per Second
edps?: FilterRange
// Energy Shield
es?: FilterRange
// Evasion Rating
ev?: FilterRange
// Physical Damage per Second
pdps?: FilterRange
// Rune Slots
rune_sockets?: FilterRange
// Spirit
spirit?: FilterRange
}
}
req_filters?: {
filters: {
dex?: FilterRange
int?: FilterRange
lvl?: FilterRange
str?: FilterRange
}
}
// WILL PROBABLY BE REMOVED SOON
map_filters?: {
filters: {
map_bonus?: FilterRange
map_tier?: FilterRange
} }
} }
misc_filters?: { misc_filters?: {
filters: { filters: {
ilvl?: FilterRange alternate_art?: FilterBoolean
quality?: FilterRange
gem_level?: FilterRange
corrupted?: FilterBoolean
fractured_item?: FilterBoolean
mirrored?: FilterBoolean
identified?: FilterBoolean
stack_size?: FilterRange
}
}
armour_filters?: {
filters: {
ar?: FilterRange
es?: FilterRange
ev?: FilterRange
ward?: FilterRange
block?: FilterRange
base_defence_percentile?: FilterRange
}
}
weapon_filters?: {
filters: {
dps?: FilterRange
pdps?: FilterRange
edps?: FilterRange
crit?: FilterRange
aps?: FilterRange
}
}
map_filters?: {
filters: {
map_tier?: FilterRange
map_blighted?: FilterBoolean
map_uberblighted?: FilterBoolean
area_level?: FilterRange area_level?: FilterRange
} corrupted?: FilterBoolean
} gem_level?: FilterRange
heist_filters?: { gem_sockets?: FilterRange
filters: { identified?: FilterBoolean
heist_wings?: FilterRange mirrored?: FilterBoolean
heist_agility?: FilterRange sanctum_gold?: FilterRange
heist_brute_force?: FilterRange unidentified_tier?: FilterRange
heist_counter_thaumaturgy?: FilterRange
heist_deception?: FilterRange
heist_demolition?: FilterRange
heist_engineering?: FilterRange
heist_lockpicking?: FilterRange
heist_perception?: FilterRange
heist_trap_disarmament?: FilterRange
}
}
sentinel_filters?: {
filters: {
sentinel_durability?: FilterRange
} }
} }
trade_filters?: { trade_filters?: {
@@ -320,6 +316,16 @@ export function createTradeRequest (
query.type = nameToQuery(activeSearch.baseType, filters) query.type = nameToQuery(activeSearch.baseType, filters)
} }
// TYPE FILTERS
if (activeSearch.category) {
const id = CATEGORY_TO_TRADE_ID.get(activeSearch.category)
if (id) {
propSet(query.filters, 'type_filters.filters.category.option', id)
} else {
throw new Error(`Invalid category: ${activeSearch.category}`)
}
}
if (filters.foil && !filters.foil.disabled) { if (filters.foil && !filters.foil.disabled) {
propSet(query.filters, 'type_filters.filters.rarity.option', 'uniquefoil') propSet(query.filters, 'type_filters.filters.rarity.option', 'uniquefoil')
} else if (filters.rarity) { } else if (filters.rarity) {
@@ -330,15 +336,65 @@ export function createTradeRequest (
) )
} }
if (activeSearch.category) { if (filters.itemLevel && !filters.itemLevel.disabled) {
const id = CATEGORY_TO_TRADE_ID.get(activeSearch.category) propSet(
if (id) { query.filters,
propSet(query.filters, 'type_filters.filters.category.option', id) 'type_filters.filters.ilvl.min',
} else { filters.itemLevel.value
throw new Error(`Invalid category: ${activeSearch.category}`) )
if (filters.itemLevel.max) {
propSet(
query.filters,
'type_filters.filters.ilvl.max',
filters.itemLevel.max
)
} }
} }
if (filters.quality && !filters.quality.disabled) {
propSet(
query.filters,
'type_filters.filters.quality.min',
filters.quality.value
)
}
// EQUIPMENT FILTERS
// REQ FILTERS
// MAP (WAYSTONE) FILTERS
if (filters.mapTier && !filters.mapTier.disabled) {
propSet(
query.filters,
'map_filters.filters.map_tier.min',
filters.mapTier.value
)
propSet(
query.filters,
'map_filters.filters.map_tier.max',
filters.mapTier.value
)
}
// MISC FILTERS
if (filters.gemLevel && !filters.gemLevel.disabled) {
propSet(
query.filters,
'misc_filters.filters.gem_level.min',
filters.gemLevel.value
)
}
if (filters.unidentified && !filters.unidentified.disabled) {
propSet(
query.filters,
'misc_filters.filters.identified.option',
String(false)
)
}
if (filters.corrupted?.value === false || filters.corrupted?.exact) { if (filters.corrupted?.value === false || filters.corrupted?.exact) {
propSet( propSet(
query.filters, query.filters,
@@ -346,13 +402,7 @@ export function createTradeRequest (
String(filters.corrupted.value) String(filters.corrupted.value)
) )
} }
if (filters.fractured?.value === false) {
propSet(
query.filters,
'misc_filters.filters.fractured_item.option',
String(false)
)
}
if (filters.mirrored) { if (filters.mirrored) {
if (filters.mirrored.disabled) { if (filters.mirrored.disabled) {
propSet( propSet(
@@ -373,121 +423,9 @@ export function createTradeRequest (
) )
} }
if (filters.gemLevel && !filters.gemLevel.disabled) { // TRADE FILTERS
propSet(
query.filters,
'misc_filters.filters.gem_level.min',
filters.gemLevel.value
)
}
if (filters.quality && !filters.quality.disabled) { // BREAK ==============================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================
propSet(
query.filters,
'misc_filters.filters.quality.min',
filters.quality.value
)
}
if (filters.itemLevel && !filters.itemLevel.disabled) {
propSet(
query.filters,
'misc_filters.filters.ilvl.min',
filters.itemLevel.value
)
if (filters.itemLevel.max) {
propSet(
query.filters,
'misc_filters.filters.ilvl.max',
filters.itemLevel.max
)
}
}
if (filters.stackSize && !filters.stackSize.disabled) {
propSet(
query.filters,
'misc_filters.filters.stack_size.min',
filters.stackSize.value
)
}
if (filters.linkedSockets && !filters.linkedSockets.disabled) {
propSet(
query.filters,
'socket_filters.filters.links.min',
filters.linkedSockets.value
)
}
if (filters.whiteSockets && !filters.whiteSockets.disabled) {
propSet(
query.filters,
'socket_filters.filters.sockets.w',
filters.whiteSockets.value
)
}
if (filters.mapTier && !filters.mapTier.disabled) {
propSet(
query.filters,
'map_filters.filters.map_tier.min',
filters.mapTier.value
)
propSet(
query.filters,
'map_filters.filters.map_tier.max',
filters.mapTier.value
)
}
if (filters.mapBlighted) {
if (filters.mapBlighted.value === 'Blighted') {
propSet(
query.filters,
'map_filters.filters.map_blighted.option',
String(true)
)
} else if (filters.mapBlighted.value === 'Blight-ravaged') {
propSet(
query.filters,
'map_filters.filters.map_uberblighted.option',
String(true)
)
}
}
if (filters.unidentified && !filters.unidentified.disabled) {
propSet(
query.filters,
'misc_filters.filters.identified.option',
String(false)
)
}
if (filters.areaLevel && !filters.areaLevel.disabled) {
propSet(
query.filters,
'map_filters.filters.area_level.min',
filters.areaLevel.value
)
}
if (filters.heistWingsRevealed && !filters.heistWingsRevealed.disabled) {
propSet(
query.filters,
'heist_filters.filters.heist_wings.min',
filters.heistWingsRevealed.value
)
}
if (filters.sentinelCharge && !filters.sentinelCharge.disabled) {
propSet(
query.filters,
'sentinel_filters.filters.sentinel_durability.min',
filters.sentinelCharge.value
)
}
for (const stat of stats) { for (const stat of stats) {
if (stat.tradeId[0] === 'item.has_empty_modifier') { if (stat.tradeId[0] === 'item.has_empty_modifier') {
@@ -557,135 +495,135 @@ export function createTradeRequest (
const input = stat.roll! const input = stat.roll!
switch (stat.tradeId[0] as InternalTradeId) { switch (stat.tradeId[0] as InternalTradeId) {
case 'item.base_percentile': // case 'item.base_percentile':
propSet( // propSet(
query.filters, // query.filters,
'armour_filters.filters.base_defence_percentile.min', // 'equipment_filters.filters.base_defence_percentile.min',
typeof input.min === 'number' ? input.min : undefined // typeof input.min === 'number' ? input.min : undefined
) // )
propSet( // propSet(
query.filters, // query.filters,
'armour_filters.filters.base_defence_percentile.max', // 'equipment_filters.filters.base_defence_percentile.max',
typeof input.max === 'number' ? input.max : undefined // typeof input.max === 'number' ? input.max : undefined
) // )
break // break
case 'item.armour': case 'item.armour':
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.ar.min', 'equipment_filters.filters.ar.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.ar.max', 'equipment_filters.filters.ar.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.evasion_rating': case 'item.evasion_rating':
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.ev.min', 'equipment_filters.filters.ev.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.ev.max', 'equipment_filters.filters.ev.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.energy_shield': case 'item.energy_shield':
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.es.min', 'equipment_filters.filters.es.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.es.max', 'equipment_filters.filters.es.max',
typeof input.max === 'number' ? input.max : undefined
)
break
case 'item.ward':
propSet(
query.filters,
'armour_filters.filters.ward.min',
typeof input.min === 'number' ? input.min : undefined
)
propSet(
query.filters,
'armour_filters.filters.ward.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.block': case 'item.block':
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.block.min', 'equipment_filters.filters.block.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'armour_filters.filters.block.max', 'equipment_filters.filters.block.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.total_dps': case 'item.total_dps':
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.dps.min', 'equipment_filters.filters.dps.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.dps.max', 'equipment_filters.filters.dps.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.physical_dps': case 'item.physical_dps':
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.pdps.min', 'equipment_filters.filters.pdps.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.pdps.max', 'equipment_filters.filters.pdps.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.elemental_dps': case 'item.elemental_dps':
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.edps.min', 'equipment_filters.filters.edps.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.edps.max', 'equipment_filters.filters.edps.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.crit': case 'item.crit':
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.crit.min', 'equipment_filters.filters.crit.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.crit.max', 'equipment_filters.filters.crit.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
case 'item.aps': case 'item.aps':
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.aps.min', 'equipment_filters.filters.aps.min',
typeof input.min === 'number' ? input.min : undefined typeof input.min === 'number' ? input.min : undefined
) )
propSet( propSet(
query.filters, query.filters,
'weapon_filters.filters.aps.max', 'equipment_filters.filters.aps.max',
typeof input.max === 'number' ? input.max : undefined
)
break
case 'item.spirit':
propSet(
query.filters,
'equipment_filters.filters.spirit.min',
typeof input.min === 'number' ? input.min : undefined
)
propSet(
query.filters,
'equipment_filters.filters.spirit.max',
typeof input.max === 'number' ? input.max : undefined typeof input.max === 'number' ? input.max : undefined
) )
break break
@@ -695,33 +633,33 @@ export function createTradeRequest (
stats = stats.filter( stats = stats.filter(
(stat) => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any) (stat) => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any)
) )
if (filters.veiled) { // if (filters.veiled) {
for (const statRef of filters.veiled.statRefs) { // for (const statRef of filters.veiled.statRefs) {
stats.push({ // stats.push({
disabled: filters.veiled.disabled, // disabled: filters.veiled.disabled,
statRef: undefined!, // statRef: undefined!,
text: undefined!, // text: undefined!,
tag: undefined!, // tag: undefined!,
sources: undefined!, // sources: undefined!,
tradeId: STAT_BY_REF(statRef)!.trade.ids[ModifierType.Veiled] // tradeId: STAT_BY_REF(statRef)!.trade.ids[ModifierType.Veiled]
}) // })
} // }
} // }
if (filters.influences) { // if (filters.influences) {
for (const influence of filters.influences) { // for (const influence of filters.influences) {
stats.push({ // stats.push({
disabled: influence.disabled, // disabled: influence.disabled,
statRef: undefined!, // statRef: undefined!,
text: undefined!, // text: undefined!,
tag: undefined!, // tag: undefined!,
sources: undefined!, // sources: undefined!,
tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[ // tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[
ModifierType.Pseudo // ModifierType.Pseudo
] // ]
}) // })
} // }
} // }
const qAnd = query.stats[0] const qAnd = query.stats[0]
for (const stat of stats) { for (const stat of stats) {

View File

@@ -10,7 +10,7 @@
<item-quick-price class="flex-1 text-base justify-center" <item-quick-price class="flex-1 text-base justify-center"
:price="trend.price" :price="trend.price"
:fraction="filters.stackSize != null" :fraction="filters.stackSize != null"
:item-img="item.info.icon" :item-img="item.info.icon === '%NOT_FOUND%' || item.info.icon === '' ? '/images/404.png' : item.info.icon"
:item-base="item.info" :item-base="item.info"
> >
<template #item v-if="isValuableBasetype"> <template #item v-if="isValuableBasetype">
@@ -53,7 +53,7 @@
<div v-else-if="!item.info.craftable" class="flex items-center pb-4" style="min-height: 3rem;"> <div v-else-if="!item.info.craftable" class="flex items-center pb-4" style="min-height: 3rem;">
<item-quick-price class="flex-1 text-base justify-center" <item-quick-price class="flex-1 text-base justify-center"
currency-text currency-text
:item-img="item.info.icon" :item-img="item.info.icon === '%NOT_FOUND%' || item.info.icon === '' ? '/images/404.png' : item.info.icon"
:item-base="item.info" /> :item-base="item.info" />
</div> </div>
</template> </template>

View File

@@ -6,7 +6,7 @@
<div class="grid grid-cols-2 gap-2 overflow-auto pb-4 px-4"> <div class="grid grid-cols-2 gap-2 overflow-auto pb-4 px-4">
<div v-for="item in identifiedVariants" :key="item.name" class="flex"> <div v-for="item in identifiedVariants" :key="item.name" class="flex">
<button @click="select(item)" class="bg-gray-700 rounded flex gap-x-3 items-center p-2 w-full"> <button @click="select(item)" class="bg-gray-700 rounded flex gap-x-3 items-center p-2 w-full">
<img :src="item.icon" class="w-12" /> <img :src="item.icon === '%NOT_FOUND%' || item.icon === '' ? '/images/404.png' : item.icon" class="w-12" />
<div class="leading-tight text-left">{{ item.name }}</div> <div class="leading-tight text-left">{{ item.name }}</div>
</button> </button>
</div> </div>

View File

@@ -2,12 +2,12 @@
<div class="p-2 flex flex-col h-full items-center"> <div class="p-2 flex flex-col h-full items-center">
<div class="flex flex-col items-center p-2 mb-4"> <div class="flex flex-col items-center p-2 mb-4">
<img class="w-12 h-12" src="/images/TransferOrb.png"> <img class="w-12 h-12" src="/images/TransferOrb.png">
<p class="text-base">Exiles Exchange</p> <p class="text-base">Exiled Exchange 2</p>
<p class="">{{ t('app.version', [version]) }}</p> <p class="">{{ t('app.version', [version]) }}</p>
<div class="flex gap-2"> <div class="flex gap-2">
<a class="border-b" href="https://github.com/Kvan7/Exiles-Exchange/releases" target="_blank">{{ <a class="border-b" href="https://github.com/Kvan7/exiled-exchange-2/releases" target="_blank">{{
t('app.release_notes') }}</a> t('app.release_notes') }}</a>
<a class="border-b" href="https://github.com/Kvan7/Exiles-Exchange/issues" target="_blank">{{ <a class="border-b" href="https://github.com/Kvan7/exiled-exchange-2/issues" target="_blank">{{
t('app.report_bug') }}</a> t('app.report_bug') }}</a>
</div> </div>
</div> </div>
@@ -43,7 +43,7 @@ function checkForUpdates () {
} }
function openDownloadPage () { function openDownloadPage () {
window.open('https://github.com/Kvan7/Exiles-Exchange/releases') window.open('https://github.com/Kvan7/exiled-exchange-2/releases')
} }
function quitAndInstall () { function quitAndInstall () {
@@ -67,19 +67,50 @@ export default defineComponent({
const rawInfo = Host.updateInfo.value const rawInfo = Host.updateInfo.value
switch (rawInfo.state) { switch (rawInfo.state) {
case 'initial': case 'initial':
return { str1: t('updates.maybe_outdated'), str2: t('updates.never_checked'), action: checkForUpdates, actionText: t('updates.check_now') } return {
str1: t('updates.maybe_outdated'),
str2: t('updates.never_checked'),
action: checkForUpdates,
actionText: t('updates.check_now')
}
case 'checking-for-update': case 'checking-for-update':
return { str1: t('updates.checking'), str2: t('please_wait') } return { str1: t('updates.checking'), str2: t('please_wait') }
case 'update-not-available': case 'update-not-available':
return { str1: t('updates.latest'), str2: t('updates.last_checked', [fmtTime(rawInfo.checkedAt)]), action: checkForUpdates, actionText: t('updates.check_now') } return {
str1: t('updates.latest'),
str2: t('updates.last_checked', [fmtTime(rawInfo.checkedAt)]),
action: checkForUpdates,
actionText: t('updates.check_now')
}
case 'error': case 'error':
return { str1: t('updates.maybe_outdated'), str2: t('updates.error'), action: openDownloadPage, actionText: t('updates.downloads_page') } return {
str1: t('updates.maybe_outdated'),
str2: t('updates.error'),
action: openDownloadPage,
actionText: t('updates.downloads_page')
}
case 'update-downloaded': case 'update-downloaded':
return { str1: t('updates.available', [rawInfo.version]), str2: t('updates.installed_on_exit'), action: quitAndInstall, actionText: t('updates.install_now') } return {
str1: t('updates.available', [rawInfo.version]),
str2: t('updates.installed_on_exit'),
action: quitAndInstall,
actionText: t('updates.install_now')
}
case 'update-available': case 'update-available':
return (rawInfo.noDownloadReason) return rawInfo.noDownloadReason
? { str1: t('updates.available', [rawInfo.version]), str2: (rawInfo.noDownloadReason === 'not-supported') ? t('updates.download_manually') : t('updates.download_disabled'), action: openDownloadPage, actionText: t('updates.downloads_page') } ? {
: { str1: t('updates.available', [rawInfo.version]), str2: t('updates.downloading') } str1: t('updates.available', [rawInfo.version]),
str2:
rawInfo.noDownloadReason === 'not-supported'
? t('updates.download_manually')
: t('updates.download_disabled'),
action: openDownloadPage,
actionText: t('updates.downloads_page')
}
: {
str1: t('updates.available', [rawInfo.version]),
str2: t('updates.downloading')
}
} }
}) })

22
testUpdate.sh Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Remove ./renderer/dist if it exists
rm -rf ./renderer/dist
# Remove ./main/dist if it exists
rm -rf ./main/dist
cd ./renderer
npm install
npm run make-index-files
npm run build
cd ..
cd ./main
npm install
npm run build
npm run package