mirror of
https://github.com/Kvan7/Exiled-Exchange-2.git
synced 2025-11-21 00:46:27 +00:00
Compare commits
1 Commits
v0.0.10
...
features/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7000dbc58c |
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Something Broken in PoE2
|
||||
about: Use this for things that worked in PoE 1 and do not in PoE 2
|
||||
title: "[PoE2]"
|
||||
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
|
||||
10
.github/workflows/main.yml
vendored
10
.github/workflows/main.yml
vendored
@@ -20,8 +20,8 @@ jobs:
|
||||
working-directory: ./renderer
|
||||
- run: npm run make-index-files
|
||||
working-directory: ./renderer
|
||||
- run: npm run lint
|
||||
working-directory: ./renderer
|
||||
# - run: npm run lint
|
||||
# working-directory: ./renderer
|
||||
- run: npm run build
|
||||
working-directory: ./renderer
|
||||
- uses: actions/upload-artifact@v4
|
||||
@@ -43,11 +43,11 @@ jobs:
|
||||
with:
|
||||
name: renderer-dist
|
||||
path: ./renderer/dist
|
||||
- run: npm ci
|
||||
- run: yarn --frozen-lockfile
|
||||
working-directory: ./main
|
||||
- run: npm run build
|
||||
- run: yarn build
|
||||
working-directory: ./main
|
||||
- run: npm run package "--" -p onTagOrDraft
|
||||
- run: yarn package -p onTagOrDraft
|
||||
working-directory: ./main
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -11,32 +11,32 @@ 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:
|
||||
|
||||
[.github/workflows/main.yml](https://github.com/Kvan7/exiled-exchange-2/blob/master/.github/workflows/main.yml)
|
||||
[.github/workflows/main.yml](https://github.com/Kvan7/exalted-poe2-trade/blob/master/.github/workflows/main.yml)
|
||||
|
||||
Here's what that looks like as of 2023-12-03.
|
||||
|
||||
```shell
|
||||
cd renderer
|
||||
npm install
|
||||
npm run make-index-files
|
||||
npm run dev
|
||||
yarn install
|
||||
yarn make-index-files
|
||||
yarn dev
|
||||
|
||||
# In a second shell
|
||||
cd main
|
||||
npm install
|
||||
npm run dev
|
||||
yarn install
|
||||
yarn dev
|
||||
```
|
||||
|
||||
# How to build
|
||||
|
||||
```shell
|
||||
cd renderer
|
||||
npm install
|
||||
npm run make-index-files
|
||||
npm run build
|
||||
yarn install
|
||||
yarn make-index-files
|
||||
yarn build
|
||||
|
||||
cd ../main
|
||||
npm run build
|
||||
yarn build
|
||||
# We want to sign with a distribution certificate to ensure other users can
|
||||
# install without errors
|
||||
CSC_NAME="Certificate name in Keychain" yarn package
|
||||
|
||||
12
README.md
12
README.md
@@ -1,16 +1,16 @@
|
||||
#  Exiled Exchange 2
|
||||
#  Exalted PoE2 Trade
|
||||
|
||||
## Moving from POE1/Awakened PoE Trade
|
||||
## Moving from POE1
|
||||
|
||||
1. Download latest release from [releases](https://github.com/Kvan7/exiled-exchange-2/releases)
|
||||
1. Download latest release from [releases](https://github.com/Kvan7/exalted-poe2-trade/releases)
|
||||
- Currently only Windows is supported
|
||||
- Only available as pre-release right now
|
||||
2. Run installer
|
||||
3. Copy `apt-data` from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exiled-exchange-2` to copy your previous settings
|
||||
3. Copy `apt-data` from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exalted-poe2-trade` to copy your previous settings
|
||||
- Resulting directory structure should look like this:
|
||||
- `%APPDATA%\exiled-exchange-2\apt-data\`
|
||||
- `%APPDATA%\exalted-poe2-trade\apt-data\`
|
||||
- `config.json`
|
||||
4. Run Exiled Exchange 2
|
||||
4. Run Exalted PoE2 Trade
|
||||
|
||||
## Tool showcase
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
const BASE = '/exiled-exchange-2/'
|
||||
const BASE = '/exalted-poe2-trade/'
|
||||
|
||||
export default defineConfig({
|
||||
title: 'Exiled Exchange 2',
|
||||
title: 'Exalted PoE2 Trade',
|
||||
description: 'App for price-checking items in Path of Exile 2',
|
||||
base: BASE,
|
||||
mpa: true,
|
||||
@@ -22,7 +22,7 @@ export default defineConfig({
|
||||
// logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401
|
||||
appVersion: '3.25.101',
|
||||
github: {
|
||||
releasesUrl: 'https://github.com/Kvan7/exiled-exchange-2/releases'
|
||||
releasesUrl: 'https://github.com/Kvan7/exalted-poe2-trade/releases'
|
||||
},
|
||||
socialLinks: [
|
||||
{
|
||||
@@ -33,7 +33,7 @@ export default defineConfig({
|
||||
{
|
||||
text: 'GitHub',
|
||||
color: '#181717',
|
||||
link: 'https://github.com/Kvan7/exiled-exchange-2'
|
||||
link: 'https://github.com/Kvan7/exalted-poe2-trade'
|
||||
}
|
||||
],
|
||||
sidebar: [
|
||||
|
||||
@@ -11,12 +11,12 @@ const { theme } = useData()
|
||||
You can download Exalted Poe Trade here. Any other mirrors are not known
|
||||
to the developer, downloading from them may be unsafe.
|
||||
|
||||
| Download link | Automatic updates | Startup time |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------------ |
|
||||
| <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}/exiled-exchange-2-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower |
|
||||
| <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}/exiled-exchange-2-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a |
|
||||
| Download link | Automatic updates | Startup time |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ------------ |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exalted-PoE2-Trade-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exalted-PoE2-Trade-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exalted-PoE2-Trade-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Exalted-PoE2-Trade-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a |
|
||||
|
||||
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\
|
||||
⚠ **If you run PoE client as Admin, OS security boundaries take effect.
|
||||
In order for Exiled Exchange 2 to have access to the PoE window, it must be started with Administrator rights.**
|
||||
In order for Exalted PoE2 Trade 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.**
|
||||
|
||||
@@ -14,7 +14,7 @@ title: Common issues
|
||||
If Awakened works for you with DirectX11/12 renderer,
|
||||
then problem is old Vulkan drivers for sure.
|
||||
|
||||
4. Delete `%appdata%\exiled-exchange-2`
|
||||
4. Delete `%appdata%\exalted-poe2-trade`
|
||||
|
||||
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**.
|
||||
|
||||
6. Restart Exiled Exchange 2.
|
||||
6. Restart Exalted PoE2 Trade.
|
||||
|
||||
*(don't forget to quit first, otherwise launching second instance will do nothing).*
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ title: Quick Start
|
||||
#### 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.
|
||||
All that remains is to parse text in Exiled Exchange 2 and show to you in a fancy way.
|
||||
All that remains is to parse text in Exalted PoE2 Trade and show to you in a fancy way.
|
||||
|
||||
### Usage
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
"dist/": true
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"conventionalCommits.scopes": [
|
||||
"Update to 2"
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
publish:
|
||||
- "github"
|
||||
productName: "Exiled Exchange 2"
|
||||
productName: "Exalted PoE2 Trade"
|
||||
npmRebuild: false
|
||||
files:
|
||||
- "package.json"
|
||||
|
||||
5656
main/package-lock.json
generated
5656
main/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,18 @@
|
||||
{
|
||||
"name": "exiled-exchange-2",
|
||||
"version": "0.0.10",
|
||||
"name": "exalted-poe2-trade",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node build/script.mjs",
|
||||
"build": "tsc --noEmit && node build/script.mjs --prod",
|
||||
"package": "electron-builder build",
|
||||
"lint": "eslint src",
|
||||
"fix": "eslint src --fix"
|
||||
"package": "electron-builder build"
|
||||
},
|
||||
"author": {
|
||||
"name": "Garrett Parker"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Kvan7/exiled-exchange-2.git"
|
||||
"url": "https://github.com/Kvan7/exalted-poe2-trade.git"
|
||||
},
|
||||
"main": "dist/main.js",
|
||||
"dependencies": {
|
||||
@@ -27,12 +25,15 @@
|
||||
"@types/ws": "^8.5.3",
|
||||
"@wokwi/bmp-ts": "^3.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
"electron": "33.2.1",
|
||||
"electron-builder": "25.1.8",
|
||||
"electron-updater": "^6.3.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"ini": "^5.0.0",
|
||||
"typescript": "5.6.x",
|
||||
"electron": "31.3.1",
|
||||
"electron-builder": "24.13.3",
|
||||
"electron-updater": "^6.1.0",
|
||||
"esbuild": "^0.23.0",
|
||||
"ini": "^4.0.0",
|
||||
"typescript": "5.5.x",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export class AppTray {
|
||||
}
|
||||
|
||||
this.tray = new Tray(trayImage);
|
||||
this.tray.setToolTip(`Exiled Exchange 2 v${app.getVersion()}`);
|
||||
this.tray.setToolTip(`Exalted PoE2 Trade v${app.getVersion()}`);
|
||||
this.rebuildMenu();
|
||||
|
||||
server.onEventAnyClient("CLIENT->MAIN::user-action", ({ action }) => {
|
||||
|
||||
@@ -12,25 +12,25 @@ const POSSIBLE_PATH =
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("documents"),
|
||||
"My Games\\Path of Exile 2\\poe2_production_Config.ini"
|
||||
"My Games\\Path of Exile 2\\production_Config.ini"
|
||||
),
|
||||
]
|
||||
: process.platform === "linux"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("documents"),
|
||||
"My Games/Path of Exile 2/poe2_production_Config.ini"
|
||||
"My Games/Path of Exile 2/production_Config.ini"
|
||||
),
|
||||
path.join(
|
||||
app.getPath("home"),
|
||||
".local/share/Steam/steamapps/compatdata/238960/pfx/drive_c/users/steamuser/Documents/My Games/Path of Exile 2/poe2_production_Config.ini"
|
||||
".local/share/Steam/steamapps/compatdata/238960/pfx/drive_c/users/steamuser/Documents/My Games/Path of Exile 2/production_Config.ini"
|
||||
),
|
||||
]
|
||||
: process.platform === "darwin"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("appData"),
|
||||
"Path of Exile 2/Preferences/poe2_production_Config.ini"
|
||||
"Path of Exile 2/Preferences/production_Config.ini"
|
||||
),
|
||||
]
|
||||
: [];
|
||||
|
||||
@@ -22,7 +22,7 @@ export class HttpProxy {
|
||||
|
||||
const official = PROXY_HOSTS.find(entry => entry.host === host)?.official
|
||||
if (official === undefined) return req.destroy()
|
||||
|
||||
|
||||
for (const key in req.headers) {
|
||||
if (key.startsWith('sec-') || key === 'host' || key === 'origin' || key === 'content-length') {
|
||||
delete req.headers[key]
|
||||
@@ -40,6 +40,7 @@ export class HttpProxy {
|
||||
})
|
||||
proxyReq.addListener('response', (proxyRes) => {
|
||||
const resHeaders = { ...proxyRes.headers }
|
||||
// `net.request` returns an already decoded body
|
||||
delete resHeaders['content-encoding']
|
||||
res.writeHead(proxyRes.statusCode, proxyRes.statusMessage, resHeaders)
|
||||
;(proxyRes as unknown as NodeJS.ReadableStream).pipe(res)
|
||||
@@ -48,7 +49,6 @@ export class HttpProxy {
|
||||
logger.write(`error [cors-proxy] ${err.message} (${host})`)
|
||||
res.destroy(err)
|
||||
})
|
||||
|
||||
req.pipe(proxyReq as unknown as NodeJS.WritableStream)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function startServer (
|
||||
socket.on('close', () => {
|
||||
const clients = websocketServer.clients
|
||||
if (clients.size === 1) {
|
||||
lastActiveClient = clients.values().next().value!
|
||||
lastActiveClient = clients.values().next().value
|
||||
evBus.emit('CLIENT->MAIN::used-recently', { isOverlay: true })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -161,7 +161,7 @@ export class OverlayWindow {
|
||||
// ----------------------
|
||||
"Path of Exile 2 is running with administrator rights.\n" +
|
||||
"\n" +
|
||||
"You need to restart Exiled Exchange 2 with administrator rights."
|
||||
"You need to restart Exalted PoE2 Trade with administrator rights."
|
||||
);
|
||||
} else {
|
||||
this.server.sendEventTo("broadcast", {
|
||||
|
||||
1956
main/yarn.lock
Normal file
1956
main/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,56 +1,60 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
// 'only-warn'
|
||||
],
|
||||
extends: [
|
||||
'plugin:vue/base',
|
||||
'standard-with-typescript',
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'quote-props': ['error', 'consistent-as-needed'],
|
||||
'no-labels': ['error', { allowLoop: true }],
|
||||
'multiline-ternary': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-undef': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error'],
|
||||
'@typescript-eslint/strict-boolean-expressions': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/prefer-readonly': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-misused-promises': 'off',
|
||||
'@typescript-eslint/prefer-reduce-type-parameter': 'off',
|
||||
'@typescript-eslint/no-invalid-void-type': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'import/first': 'off',
|
||||
'import/no-duplicates': 'off',
|
||||
'func-call-spacing': 'off',
|
||||
// TODO: refactor IPC and enable
|
||||
'@typescript-eslint/consistent-type-assertions': 'off',
|
||||
},
|
||||
overrides: [{
|
||||
files: ['src/main/**/*'],
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
}
|
||||
}, {
|
||||
files: ['*.ts'],
|
||||
node: true
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'prettier'
|
||||
// 'only-warn'
|
||||
],
|
||||
extends: [
|
||||
'plugin:vue/base',
|
||||
'standard-with-typescript',
|
||||
'plugin:prettier/recommended',
|
||||
'eslint-plugin-prettier/recommended',
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'quote-props': ['error', 'consistent-as-needed'],
|
||||
'no-labels': ['error', { allowLoop: true }],
|
||||
'multiline-ternary': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-undef': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error'],
|
||||
'@typescript-eslint/strict-boolean-expressions': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/prefer-readonly': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-misused-promises': 'off',
|
||||
'@typescript-eslint/prefer-reduce-type-parameter': 'off',
|
||||
'@typescript-eslint/no-invalid-void-type': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'import/first': 'off',
|
||||
'import/no-duplicates': 'off',
|
||||
'func-call-spacing': 'off',
|
||||
// TODO: refactor IPC and enable
|
||||
'@typescript-eslint/consistent-type-assertions': 'off',
|
||||
"indent": ["error", "tab"]
|
||||
},
|
||||
overrides: [{
|
||||
files: ['src/main/**/*'],
|
||||
env: {
|
||||
node: true
|
||||
}
|
||||
}, {
|
||||
files: ['*.ts'],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json'
|
||||
parser: '@typescript-eslint/parser',
|
||||
extraFileExtensions: ['.vue']
|
||||
}
|
||||
}],
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
extraFileExtensions: ['.vue']
|
||||
}
|
||||
}
|
||||
|
||||
7
renderer/.prettierrc
Normal file
7
renderer/.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"tabWidth": 2,
|
||||
"useTabs": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,18 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<link rel="icon" href="/icon.ico">
|
||||
<title>Exiled Exchange 2</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<link rel="icon" href="/icon.ico">
|
||||
<title>Exalted PoE2 Trade</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
12906
renderer/package-lock.json
generated
12906
renderer/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,63 +1,65 @@
|
||||
{
|
||||
"name": "exiled-exchange-2",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"lint": "eslint --ext .ts,.vue src",
|
||||
"lint-fix": "eslint --ext .ts,.vue src --fix",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"make-index-files": "node src/assets/make-index-files.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.x.x",
|
||||
"@sindresorhus/fnv1a": "^3.0.0",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"apexcharts": "^4.0.0",
|
||||
"dot-prop": "9.x.x",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"luxon": "3.x.x",
|
||||
"neverthrow": "^8.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"sockette": "^2.0.6",
|
||||
"tailwindcss": "3.x.x",
|
||||
"tippy.js": "^6.2.7",
|
||||
"vue": "3.2.37",
|
||||
"vue-i18n": "^10.0.0",
|
||||
"vue3-apexcharts": "^1.1.1",
|
||||
"vuedraggable": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/object-hash": "^3.0.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"autoprefixer": "^10.0.2",
|
||||
"postcss": "^8.2.14",
|
||||
"typescript": "5.6.x",
|
||||
"vite": "^5.0.0",
|
||||
"vue-tsc": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-standard-with-typescript": "^31.0.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-n": "^15.0.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-vue": "^9.1.1"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"tailwindcss/nesting": {},
|
||||
"tailwindcss": {},
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"chrome >= 101"
|
||||
]
|
||||
"name": "exalted-poe2-trade",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"lint": "eslint --ext .ts,.vue src",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"make-index-files": "node src/assets/make-index-files.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.x.x",
|
||||
"@sindresorhus/fnv1a": "^3.0.0",
|
||||
"@vueuse/core": "^11.0.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"apexcharts": "^4.0.0",
|
||||
"dot-prop": "9.x.x",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"luxon": "3.x.x",
|
||||
"neverthrow": "^8.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"sockette": "^2.0.6",
|
||||
"tailwindcss": "3.x.x",
|
||||
"tippy.js": "^6.2.7",
|
||||
"vue": "3.2.37",
|
||||
"vue-i18n": "^10.0.0",
|
||||
"vue3-apexcharts": "^1.1.1",
|
||||
"vuedraggable": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/luxon": "^3.0.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/object-hash": "^3.0.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"autoprefixer": "^10.0.2",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"postcss": "^8.2.14",
|
||||
"prettier": "3.4.2",
|
||||
"typescript": "5.6.x",
|
||||
"vite": "^5.0.0",
|
||||
"vue-tsc": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-standard-with-typescript": "^31.0.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-n": "^15.0.0",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-vue": "^9.1.1"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"tailwindcss/nesting": {},
|
||||
"tailwindcss": {},
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"chrome >= 101"
|
||||
]
|
||||
}
|
||||
@@ -269,7 +269,7 @@
|
||||
"stack": "Stack"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings - Exiled Exchange 2",
|
||||
"title": "Settings - Exalted PoE2 Trade",
|
||||
"language": "Language",
|
||||
"private_league": "or Private League",
|
||||
"account_name": "Account name",
|
||||
|
||||
@@ -266,7 +266,7 @@
|
||||
"stack": "스택"
|
||||
},
|
||||
"settings": {
|
||||
"title": "세팅 - Exiled Exchange 2",
|
||||
"title": "세팅 - Exalted PoE2 Trade",
|
||||
"language": "언어",
|
||||
"private_league": "개인리그",
|
||||
"account_name": "계정명",
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
"stack": "Стак"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки - Exiled Exchange 2",
|
||||
"title": "Настройки - Exalted PoE2 Trade",
|
||||
"language": "Язык",
|
||||
"private_league": "или Приватная лига",
|
||||
"account_name": "Имя учетной записи",
|
||||
|
||||
@@ -299,8 +299,8 @@ function parseNamePlate (section: string[]) {
|
||||
const item: ParserState = {
|
||||
rarity: undefined,
|
||||
category: undefined,
|
||||
name,
|
||||
baseType,
|
||||
name: name,
|
||||
baseType: baseType,
|
||||
isUnidentified: false,
|
||||
isCorrupted: false,
|
||||
newMods: [],
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
export enum ItemCategory {
|
||||
Map = 'Map',
|
||||
CapturedBeast = 'Captured Beast',
|
||||
MetamorphSample = 'Metamorph Sample',
|
||||
Helmet = 'Helmet',
|
||||
BodyArmour = 'Body Armour',
|
||||
Gloves = 'Gloves',
|
||||
Boots = 'Boots',
|
||||
Shield = 'Shield',
|
||||
Amulet = 'Amulet',
|
||||
Belt = 'Belt',
|
||||
Ring = 'Ring',
|
||||
Flask = 'Flask',
|
||||
AbyssJewel = 'Abyss Jewel',
|
||||
Jewel = 'Jewel',
|
||||
Quiver = 'Quiver',
|
||||
Claw = 'Claw',
|
||||
Bow = 'Bow',
|
||||
Sceptre = 'Sceptre',
|
||||
Wand = 'Wand',
|
||||
FishingRod = 'Fishing Rod',
|
||||
Staff = 'Staff',
|
||||
Warstaff = 'Warstaff',
|
||||
Dagger = 'Dagger',
|
||||
RuneDagger = 'Rune Dagger',
|
||||
OneHandedAxe = 'One-Handed Axe',
|
||||
TwoHandedAxe = 'Two-Handed Axe',
|
||||
OneHandedMace = 'One-Handed Mace',
|
||||
TwoHandedMace = 'Two-Handed Mace',
|
||||
OneHandedSword = 'One-Handed Sword',
|
||||
TwoHandedSword = 'Two-Handed Sword',
|
||||
ClusterJewel = 'Cluster Jewel',
|
||||
HeistBlueprint = 'Heist Blueprint',
|
||||
HeistContract = 'Heist Contract',
|
||||
HeistTool = 'Heist Tool',
|
||||
HeistBrooch = 'Heist Brooch',
|
||||
HeistGear = 'Heist Gear',
|
||||
HeistCloak = 'Heist Cloak',
|
||||
Trinket = 'Trinket',
|
||||
Invitation = 'Invitation',
|
||||
Gem = 'Gem',
|
||||
Currency = 'Currency',
|
||||
DivinationCard = 'Divination Card',
|
||||
Voidstone = 'Voidstone',
|
||||
Sentinel = 'Sentinel',
|
||||
MemoryLine = 'Memory Line',
|
||||
SanctumRelic = 'Sanctum Relic',
|
||||
Tincture = 'Tincture',
|
||||
Charm = 'Charm',
|
||||
Crossbow = 'Crossbow',
|
||||
Map = 'Map',
|
||||
CapturedBeast = 'Captured Beast',
|
||||
MetamorphSample = 'Metamorph Sample',
|
||||
Helmet = 'Helmet',
|
||||
BodyArmour = 'Body Armour',
|
||||
Gloves = 'Gloves',
|
||||
Boots = 'Boots',
|
||||
Shield = 'Shield',
|
||||
Amulet = 'Amulet',
|
||||
Belt = 'Belt',
|
||||
Ring = 'Ring',
|
||||
Flask = 'Flask',
|
||||
AbyssJewel = 'Abyss Jewel',
|
||||
Jewel = 'Jewel',
|
||||
Quiver = 'Quiver',
|
||||
Claw = 'Claw',
|
||||
Bow = 'Bow',
|
||||
Sceptre = 'Sceptre',
|
||||
Wand = 'Wand',
|
||||
FishingRod = 'Fishing Rod',
|
||||
Staff = 'Staff',
|
||||
Warstaff = 'Warstaff',
|
||||
Dagger = 'Dagger',
|
||||
RuneDagger = 'Rune Dagger',
|
||||
OneHandedAxe = 'One-Handed Axe',
|
||||
TwoHandedAxe = 'Two-Handed Axe',
|
||||
OneHandedMace = 'One-Handed Mace',
|
||||
TwoHandedMace = 'Two-Handed Mace',
|
||||
OneHandedSword = 'One-Handed Sword',
|
||||
TwoHandedSword = 'Two-Handed Sword',
|
||||
ClusterJewel = 'Cluster Jewel',
|
||||
HeistBlueprint = 'Heist Blueprint',
|
||||
HeistContract = 'Heist Contract',
|
||||
HeistTool = 'Heist Tool',
|
||||
HeistBrooch = 'Heist Brooch',
|
||||
HeistGear = 'Heist Gear',
|
||||
HeistCloak = 'Heist Cloak',
|
||||
Trinket = 'Trinket',
|
||||
Invitation = 'Invitation',
|
||||
Gem = 'Gem',
|
||||
Currency = 'Currency',
|
||||
DivinationCard = 'Divination Card',
|
||||
Voidstone = 'Voidstone',
|
||||
Sentinel = 'Sentinel',
|
||||
MemoryLine = 'Memory Line',
|
||||
SanctumRelic = 'Sanctum Relic',
|
||||
Tincture = 'Tincture',
|
||||
Charm = 'Charm',
|
||||
Crossbow = 'Crossbow',
|
||||
}
|
||||
|
||||
export const WEAPON_ONE_HANDED_MELEE = new Set([
|
||||
ItemCategory.OneHandedAxe,
|
||||
ItemCategory.OneHandedMace,
|
||||
ItemCategory.OneHandedSword,
|
||||
ItemCategory.Claw,
|
||||
ItemCategory.Dagger,
|
||||
ItemCategory.RuneDagger,
|
||||
ItemCategory.Sceptre
|
||||
])
|
||||
ItemCategory.OneHandedAxe,
|
||||
ItemCategory.OneHandedMace,
|
||||
ItemCategory.OneHandedSword,
|
||||
ItemCategory.Claw,
|
||||
ItemCategory.Dagger,
|
||||
ItemCategory.RuneDagger,
|
||||
ItemCategory.Sceptre,
|
||||
]);
|
||||
|
||||
export const WEAPON_ONE_HANDED = new Set([
|
||||
ItemCategory.Wand,
|
||||
...WEAPON_ONE_HANDED_MELEE
|
||||
])
|
||||
ItemCategory.Wand,
|
||||
...WEAPON_ONE_HANDED_MELEE,
|
||||
]);
|
||||
|
||||
export const WEAPONE_TWO_HANDED_MELEE = new Set([
|
||||
ItemCategory.TwoHandedAxe,
|
||||
ItemCategory.TwoHandedMace,
|
||||
ItemCategory.TwoHandedSword,
|
||||
ItemCategory.Staff,
|
||||
ItemCategory.Warstaff
|
||||
])
|
||||
ItemCategory.TwoHandedAxe,
|
||||
ItemCategory.TwoHandedMace,
|
||||
ItemCategory.TwoHandedSword,
|
||||
ItemCategory.Staff,
|
||||
ItemCategory.Warstaff,
|
||||
]);
|
||||
|
||||
export const WEAPON = new Set([
|
||||
ItemCategory.FishingRod,
|
||||
ItemCategory.Bow,
|
||||
...WEAPON_ONE_HANDED,
|
||||
...WEAPONE_TWO_HANDED_MELEE
|
||||
])
|
||||
ItemCategory.FishingRod,
|
||||
ItemCategory.Bow,
|
||||
...WEAPON_ONE_HANDED,
|
||||
...WEAPONE_TWO_HANDED_MELEE,
|
||||
]);
|
||||
|
||||
export const ARMOUR = new Set([
|
||||
ItemCategory.BodyArmour,
|
||||
ItemCategory.Boots,
|
||||
ItemCategory.Gloves,
|
||||
ItemCategory.Helmet,
|
||||
ItemCategory.Shield
|
||||
])
|
||||
ItemCategory.BodyArmour,
|
||||
ItemCategory.Boots,
|
||||
ItemCategory.Gloves,
|
||||
ItemCategory.Helmet,
|
||||
ItemCategory.Shield,
|
||||
]);
|
||||
|
||||
export const ACCESSORY = new Set([
|
||||
ItemCategory.Amulet,
|
||||
ItemCategory.Belt,
|
||||
ItemCategory.Ring,
|
||||
ItemCategory.Trinket
|
||||
// ItemCategory.Quiver
|
||||
])
|
||||
ItemCategory.Amulet,
|
||||
ItemCategory.Belt,
|
||||
ItemCategory.Ring,
|
||||
ItemCategory.Trinket,
|
||||
// ItemCategory.Quiver
|
||||
]);
|
||||
|
||||
@@ -109,7 +109,7 @@ export function translateStatWithRoll (
|
||||
calc.sources.some(s => s.stat.stat.ref === calc.stat.ref && s.stat.roll!.dp)
|
||||
: undefined
|
||||
|
||||
return { string: translation.string, negate: translation.negate || false, dp }
|
||||
return { string: translation.string, negate: translation.negate || false, dp: dp }
|
||||
}
|
||||
|
||||
export enum ModifierType {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +1,118 @@
|
||||
import { computed, shallowRef, readonly } from 'vue'
|
||||
import { createGlobalState } from '@vueuse/core'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
import { computed, shallowRef, readonly } from 'vue';
|
||||
import { createGlobalState } from '@vueuse/core';
|
||||
import { AppConfig, poeWebApi } from '@/web/Config';
|
||||
import { Host } from './IPC';
|
||||
|
||||
// pc-ggg, pc-garena
|
||||
// const PERMANENT_SC = ['Standard', '標準模式']
|
||||
const PERMANENT_HC = ['Hardcore', '專家模式']
|
||||
const PERMANENT_HC = ['Hardcore', '專家模式'];
|
||||
|
||||
interface ApiLeague {
|
||||
id: string
|
||||
event?: boolean
|
||||
rules: Array<{ id: string }>
|
||||
id: string;
|
||||
event?: boolean;
|
||||
rules: Array<{ id: string }>;
|
||||
}
|
||||
|
||||
const DEFAULT_POE2_LEAGUES: ApiLeague[] = [
|
||||
{ id: 'Standard', rules: [] },
|
||||
{
|
||||
id: 'Hardcore',
|
||||
rules: [
|
||||
{
|
||||
id: 'Hardcore',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
interface League {
|
||||
id: string
|
||||
isPopular: boolean
|
||||
id: string;
|
||||
isPopular: boolean;
|
||||
}
|
||||
|
||||
export const useLeagues = createGlobalState(() => {
|
||||
const isLoading = shallowRef(false)
|
||||
const error = shallowRef<string | null>(null)
|
||||
const tradeLeagues = shallowRef<League[]>([])
|
||||
const isLoading = shallowRef(false);
|
||||
const error = shallowRef<string | null>(null);
|
||||
const tradeLeagues = shallowRef<League[]>([]);
|
||||
|
||||
const DEFAULT_POE2_LEAGUES: ApiLeague[] = [
|
||||
{ id: 'Standard', rules: [] },
|
||||
{
|
||||
id: 'Hardcore',
|
||||
rules: [
|
||||
{
|
||||
id: 'Hardcore'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
const selectedId = computed<string | undefined>({
|
||||
get() {
|
||||
return tradeLeagues.value.length ? AppConfig().leagueId : undefined;
|
||||
},
|
||||
set(id) {
|
||||
AppConfig().leagueId = id;
|
||||
},
|
||||
});
|
||||
|
||||
const selectedId = computed<string | undefined>({
|
||||
get () {
|
||||
return tradeLeagues.value.length ? AppConfig().leagueId : undefined
|
||||
},
|
||||
set (id) {
|
||||
AppConfig().leagueId = id
|
||||
}
|
||||
})
|
||||
const selected = computed(() => {
|
||||
const { leagueId } = AppConfig();
|
||||
if (!tradeLeagues.value || !leagueId) return undefined;
|
||||
const listed = tradeLeagues.value.find((league) => league.id === leagueId);
|
||||
return {
|
||||
id: leagueId,
|
||||
realm: AppConfig().realm,
|
||||
isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular),
|
||||
};
|
||||
});
|
||||
|
||||
const selected = computed(() => {
|
||||
const { leagueId } = AppConfig()
|
||||
if (!tradeLeagues.value || !leagueId) return undefined
|
||||
const listed = tradeLeagues.value.find((league) => league.id === leagueId)
|
||||
return {
|
||||
id: leagueId,
|
||||
realm: AppConfig().realm,
|
||||
isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular)
|
||||
}
|
||||
})
|
||||
async function load() {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
|
||||
async function load () {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await Host.proxy(
|
||||
`${poeWebApi()}/api/leagues?type=main&realm=pc`
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error(JSON.stringify(Object.fromEntries(response.headers)));
|
||||
// const leagues: ApiLeague[] = await response.json();
|
||||
const leagues: ApiLeague[] = DEFAULT_POE2_LEAGUES;
|
||||
tradeLeagues.value = leagues
|
||||
.filter(
|
||||
(league) =>
|
||||
!PERMANENT_HC.includes(league.id) &&
|
||||
!league.rules.some(
|
||||
(rule) =>
|
||||
rule.id === 'NoParties' ||
|
||||
(rule.id === 'HardMode' && !league.event)
|
||||
)
|
||||
)
|
||||
.map((league) => {
|
||||
return { id: league.id, isPopular: true };
|
||||
});
|
||||
|
||||
try {
|
||||
// const response = await Host.proxy(
|
||||
// `${poeWebApi()}/api/leagues?type=main&realm=pc`
|
||||
// );
|
||||
// if (!response.ok)
|
||||
// throw new Error(JSON.stringify(Object.fromEntries(response.headers)));
|
||||
// const leagues: ApiLeague[] = await response.json();
|
||||
const leagues: ApiLeague[] = DEFAULT_POE2_LEAGUES
|
||||
tradeLeagues.value = leagues
|
||||
.filter(
|
||||
(league) =>
|
||||
!PERMANENT_HC.includes(league.id) &&
|
||||
!league.rules.some(
|
||||
(rule) =>
|
||||
rule.id === 'NoParties' ||
|
||||
(rule.id === 'HardMode' && !league.event)
|
||||
)
|
||||
)
|
||||
.map((league) => {
|
||||
return { id: league.id, isPopular: true }
|
||||
})
|
||||
const leagueIsAlive = tradeLeagues.value.some(
|
||||
(league) => league.id === selectedId.value
|
||||
);
|
||||
if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) {
|
||||
if (tradeLeagues.value.length > 1) {
|
||||
const TMP_CHALLENGE = 1;
|
||||
selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id;
|
||||
} else {
|
||||
const STANDARD = 0;
|
||||
selectedId.value = tradeLeagues.value[STANDARD].id;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = (e as Error).message;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const leagueIsAlive = tradeLeagues.value.some(
|
||||
(league) => league.id === selectedId.value
|
||||
)
|
||||
if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) {
|
||||
if (tradeLeagues.value.length > 1) {
|
||||
const TMP_CHALLENGE = 1
|
||||
selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id
|
||||
} else {
|
||||
const STANDARD = 0
|
||||
selectedId.value = tradeLeagues.value[STANDARD].id
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = (e as Error).message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
return {
|
||||
isLoading,
|
||||
error,
|
||||
selectedId,
|
||||
selected,
|
||||
list: readonly(tradeLeagues),
|
||||
load,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
error,
|
||||
selectedId,
|
||||
selected,
|
||||
list: readonly(tradeLeagues),
|
||||
load
|
||||
}
|
||||
})
|
||||
|
||||
function isPrivateLeague (id: string) {
|
||||
if (id.includes('Ruthless')) {
|
||||
return true
|
||||
}
|
||||
return /\(PL\d+\)$/.test(id)
|
||||
function isPrivateLeague(id: string) {
|
||||
if (id.includes('Ruthless')) {
|
||||
return true;
|
||||
}
|
||||
return /\(PL\d+\)$/.test(id);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { ItemCheckWidget } from './widget.js'
|
||||
import Widget from '../overlay/Widget.vue'
|
||||
import MapCheck from '../map-check/MapCheck.vue'
|
||||
import ItemInfo from './ItemInfo.vue'
|
||||
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
|
||||
import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
config: ItemCheckWidget
|
||||
|
||||
@@ -1,53 +1,54 @@
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
import { ParsedItem, parseClipboard } from '@/parser'
|
||||
import { Host } from '@/web/background/IPC';
|
||||
import { AppConfig } from '@/web/Config';
|
||||
import { ParsedItem, parseClipboard } from '@/parser';
|
||||
|
||||
const POEDB_LANGS = { 'en': 'us', 'ru': 'ru', 'cmn-Hant': 'tw', 'ko': 'kr' }
|
||||
const POEDB_LANGS = { en: 'us', ru: 'ru', 'cmn-Hant': 'tw', ko: 'kr' };
|
||||
|
||||
export function registerActions () {
|
||||
Host.onEvent('MAIN->CLIENT::item-text', (e) => {
|
||||
if (
|
||||
![
|
||||
'open-wiki',
|
||||
'open-craft-of-exile',
|
||||
'open-poedb',
|
||||
'search-similar'
|
||||
].includes(e.target)
|
||||
) { return }
|
||||
const parsed = parseClipboard(e.clipboard)
|
||||
if (!parsed.isOk()) return
|
||||
export function registerActions() {
|
||||
Host.onEvent('MAIN->CLIENT::item-text', (e) => {
|
||||
if (
|
||||
![
|
||||
'open-wiki',
|
||||
'open-craft-of-exile',
|
||||
'open-poedb',
|
||||
'search-similar',
|
||||
].includes(e.target)
|
||||
)
|
||||
return;
|
||||
const parsed = parseClipboard(e.clipboard);
|
||||
if (!parsed.isOk()) return;
|
||||
|
||||
if (e.target === 'open-wiki') {
|
||||
openWiki(parsed.value)
|
||||
} else if (e.target === 'open-craft-of-exile') {
|
||||
openCoE(parsed.value)
|
||||
} else if (e.target === 'open-poedb') {
|
||||
openPoedb(parsed.value)
|
||||
} else if (e.target === 'search-similar') {
|
||||
findSimilarItems(parsed.value)
|
||||
}
|
||||
})
|
||||
if (e.target === 'open-wiki') {
|
||||
openWiki(parsed.value);
|
||||
} else if (e.target === 'open-craft-of-exile') {
|
||||
openCoE(parsed.value);
|
||||
} else if (e.target === 'open-poedb') {
|
||||
openPoedb(parsed.value);
|
||||
} else if (e.target === 'search-similar') {
|
||||
findSimilarItems(parsed.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function openWiki (item: ParsedItem) {
|
||||
window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`)
|
||||
export function openWiki(item: ParsedItem) {
|
||||
window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`);
|
||||
}
|
||||
export function openPoedb (item: ParsedItem) {
|
||||
window.open(
|
||||
`https://poe2db.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}`
|
||||
)
|
||||
export function openPoedb(item: ParsedItem) {
|
||||
window.open(
|
||||
`https://poe2db.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}`
|
||||
);
|
||||
}
|
||||
export function openCoE (item: ParsedItem) {
|
||||
const encodedClipboard = encodeURIComponent(item.rawText)
|
||||
window.open(
|
||||
`https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}`
|
||||
)
|
||||
export function openCoE(item: ParsedItem) {
|
||||
const encodedClipboard = encodeURIComponent(item.rawText);
|
||||
window.open(
|
||||
`https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}`
|
||||
);
|
||||
}
|
||||
|
||||
export function findSimilarItems (item: ParsedItem) {
|
||||
const text = JSON.stringify(item.info.name)
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'stash-search', text }
|
||||
})
|
||||
export function findSimilarItems(item: ParsedItem) {
|
||||
const text = JSON.stringify(item.info.name);
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'stash-search', text },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,65 +1,67 @@
|
||||
<template>
|
||||
<transition enter-active-class="animate__animated animate__fadeIn"
|
||||
leave-active-class="animate__animated animate__backOutDown">
|
||||
<div :class="$style.widget" v-if="show">
|
||||
<div :class="$style.box">
|
||||
<div class="py-2 px-4">
|
||||
<div class="text-base">Exiled Exchange 2</div>
|
||||
<p>{{ t('app_is_ready') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<transition
|
||||
enter-active-class="animate__animated animate__fadeIn"
|
||||
leave-active-class="animate__animated animate__backOutDown"
|
||||
>
|
||||
<div :class="$style.widget" v-if="show">
|
||||
<div :class="$style.box">
|
||||
<div class="py-2 px-4">
|
||||
<div class="text-base">Exalted PoE2 Trade</div>
|
||||
<p>{{ t('app_is_ready') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { shallowRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
import { shallowRef } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Host } from '@/web/background/IPC';
|
||||
import { AppConfig } from '@/web/Config';
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t } = useI18n();
|
||||
|
||||
const show = shallowRef(false)
|
||||
const show = shallowRef(false);
|
||||
|
||||
Host.onEvent('MAIN->OVERLAY::overlay-attached', () => {
|
||||
if (!show.value && AppConfig().showAttachNotification) {
|
||||
show.value = true
|
||||
setTimeout(() => {
|
||||
show.value = false
|
||||
}, 2500)
|
||||
}
|
||||
})
|
||||
if (!show.value && AppConfig().showAttachNotification) {
|
||||
show.value = true;
|
||||
setTimeout(() => {
|
||||
show.value = false;
|
||||
}, 2500);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="postcss" module>
|
||||
.widget {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
bottom: 20%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
bottom: 20%;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@apply bg-gray-800;
|
||||
@apply text-gray-100;
|
||||
@apply rounded;
|
||||
box-shadow: 0px 0px 1px 2px rgb(255 255 255 / 20%);
|
||||
position: relative;
|
||||
display: flex;
|
||||
@apply bg-gray-800;
|
||||
@apply text-gray-100;
|
||||
@apply rounded;
|
||||
box-shadow: 0px 0px 1px 2px rgb(255 255 255 / 20%);
|
||||
}
|
||||
|
||||
.box::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: url('/images/exa.png') no-repeat top right/contain;
|
||||
right: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 78px;
|
||||
@apply mr-2;
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(2px 4px 6px #000);
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: url('/images/exa.png') no-repeat top right/contain;
|
||||
right: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 78px;
|
||||
@apply mr-2;
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(2px 4px 6px #000);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -61,7 +61,7 @@ export default defineComponent({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const wm = inject<WidgetManager>('wm')!
|
||||
|
||||
const widgets = computed(() => {
|
||||
@@ -79,17 +79,17 @@ export default defineComponent({
|
||||
return {
|
||||
t,
|
||||
widgets,
|
||||
createOfType (type: string) {
|
||||
createOfType(type: string) {
|
||||
wm.create(type)
|
||||
},
|
||||
toggle (widget: IWidget) {
|
||||
toggle(widget: IWidget) {
|
||||
if (widget.wmWants === 'hide') {
|
||||
wm.show(widget.wmId)
|
||||
} else {
|
||||
wm.hide(widget.wmId)
|
||||
}
|
||||
},
|
||||
handleItemPaste (e: Event) {
|
||||
handleItemPaste(e: Event) {
|
||||
const target = e.target as HTMLInputElement
|
||||
const inputRect = target.getBoundingClientRect()
|
||||
Host.selfDispatch({
|
||||
|
||||
@@ -71,7 +71,7 @@ export default defineComponent({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const widget = computed(() => AppConfig<PriceCheckWidget>('price-check')!)
|
||||
const leagues = useLeagues()
|
||||
|
||||
@@ -177,7 +177,7 @@ export default defineComponent({
|
||||
props.item.info.unique == null)
|
||||
})
|
||||
|
||||
function handleSearchMouseenter (e: MouseEvent) {
|
||||
function handleSearchMouseenter(e: MouseEvent) {
|
||||
if ((filtersComponent.value.$el as HTMLElement).contains(e.relatedTarget as HTMLElement)) {
|
||||
doSearch.value = true
|
||||
|
||||
@@ -216,10 +216,10 @@ export default defineComponent({
|
||||
showSupportLinks,
|
||||
presets: computed(() => presets.value.presets.map(preset =>
|
||||
({ id: preset.id, active: (preset.id === presets.value.active) }))),
|
||||
selectPreset (id: string) {
|
||||
selectPreset(id: string) {
|
||||
presets.value.active = id
|
||||
},
|
||||
makeTradeLink () {
|
||||
makeTradeLink() {
|
||||
return `https://${getTradeEndpoint()}/trade2/search/poe2/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,332 +1,356 @@
|
||||
<template>
|
||||
<div
|
||||
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-reverse': clickPosition === 'inventory',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
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 />
|
||||
<AppTitleBar
|
||||
@close="closePriceCheck"
|
||||
@click="openLeagueSelection"
|
||||
:title="title"
|
||||
>
|
||||
<ui-popover
|
||||
v-if="stableOrbCost"
|
||||
trigger="click"
|
||||
boundary="#price-window"
|
||||
>
|
||||
<template #target>
|
||||
<button>
|
||||
<i class="fas fa-exchange-alt" /> {{ stableOrbCost }}
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<item-quick-price
|
||||
class="text-base"
|
||||
:price="{
|
||||
min: stableOrbCost,
|
||||
max: stableOrbCost,
|
||||
currency: 'chaos',
|
||||
}"
|
||||
item-img="/images/divine.png"
|
||||
/>
|
||||
<div v-for="i in 9" :key="i">
|
||||
<div class="pl-1">
|
||||
{{ i / 10 }} div ⇒ {{ Math.round((stableOrbCost * i) / 10) }} c
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ui-popover>
|
||||
<i v-else-if="xchgRateLoading()" class="fas fa-dna fa-spin px-2" />
|
||||
<div v-else class="w-8" />
|
||||
</AppTitleBar>
|
||||
<div class="grow layout-column min-h-0 bg-gray-800">
|
||||
<background-info />
|
||||
<check-position-circle
|
||||
v-if="showCheckPos"
|
||||
:position="checkPosition"
|
||||
style="z-index: -1"
|
||||
/>
|
||||
<template v-if="item?.isErr()">
|
||||
<ui-error-box class="m-4">
|
||||
<template #name>{{ t(item.error.name) }}</template>
|
||||
<p>{{ t(item.error.message) }}</p>
|
||||
</ui-error-box>
|
||||
<pre class="bg-gray-900 rounded m-4 overflow-x-hidden p-2">{{
|
||||
item.error.rawText
|
||||
}}</pre>
|
||||
</template>
|
||||
<template v-else-if="item?.isOk()">
|
||||
<unidentified-resolver
|
||||
:item="item.value"
|
||||
@identify="handleIdentification($event)"
|
||||
/>
|
||||
<checked-item
|
||||
v-if="isLeagueSelected"
|
||||
:item="item.value"
|
||||
:advanced-check="advancedCheck"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate">
|
||||
<i18n-t keypath="app.toggle_browser_hint" tag="div">
|
||||
<span class="bg-gray-400 text-gray-900 rounded px-1">{{
|
||||
overlayKey
|
||||
}}</span>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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
|
||||
class="flex"
|
||||
:class="{
|
||||
'flex-row': clickPosition === 'stash',
|
||||
'flex-row-reverse': clickPosition === 'inventory',
|
||||
}"
|
||||
>
|
||||
<related-items
|
||||
v-if="item?.isOk()"
|
||||
class="pointer-events-auto"
|
||||
:item="item.value"
|
||||
:click-position="clickPosition"
|
||||
/>
|
||||
<rate-limiter-state class="pointer-events-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
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-reverse': clickPosition === 'inventory',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
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 />
|
||||
<AppTitleBar
|
||||
@close="closePriceCheck"
|
||||
@click="openLeagueSelection"
|
||||
:title="title"
|
||||
>
|
||||
<ui-popover
|
||||
v-if="stableOrbCost"
|
||||
trigger="click"
|
||||
boundary="#price-window"
|
||||
>
|
||||
<template #target>
|
||||
<button>
|
||||
<i class="fas fa-exchange-alt" /> {{ stableOrbCost }}
|
||||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
<item-quick-price
|
||||
class="text-base"
|
||||
:price="{
|
||||
min: stableOrbCost,
|
||||
max: stableOrbCost,
|
||||
currency: 'chaos',
|
||||
}"
|
||||
item-img="/images/divine.png"
|
||||
/>
|
||||
<div v-for="i in 9" :key="i">
|
||||
<div class="pl-1">
|
||||
{{ i / 10 }} div ⇒ {{ Math.round((stableOrbCost * i) / 10) }} c
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ui-popover>
|
||||
<i v-else-if="xchgRateLoading()" class="fas fa-dna fa-spin px-2" />
|
||||
<div v-else class="w-8" />
|
||||
</AppTitleBar>
|
||||
<div class="grow layout-column min-h-0 bg-gray-800">
|
||||
<background-info />
|
||||
<check-position-circle
|
||||
v-if="showCheckPos"
|
||||
:position="checkPosition"
|
||||
style="z-index: -1"
|
||||
/>
|
||||
<template v-if="item?.isErr()">
|
||||
<ui-error-box class="m-4">
|
||||
<template #name>{{ t(item.error.name) }}</template>
|
||||
<p>{{ t(item.error.message) }}</p>
|
||||
</ui-error-box>
|
||||
<pre class="bg-gray-900 rounded m-4 overflow-x-hidden p-2">{{
|
||||
item.error.rawText
|
||||
}}</pre>
|
||||
</template>
|
||||
<template v-else-if="item?.isOk()">
|
||||
<unidentified-resolver
|
||||
:item="item.value"
|
||||
@identify="handleIdentification($event)"
|
||||
/>
|
||||
<checked-item
|
||||
v-if="isLeagueSelected"
|
||||
:item="item.value"
|
||||
:advanced-check="advancedCheck"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="isBrowserShown" class="bg-gray-900 px-6 py-2 truncate">
|
||||
<i18n-t keypath="app.toggle_browser_hint" tag="div">
|
||||
<span class="bg-gray-400 text-gray-900 rounded px-1">{{
|
||||
overlayKey
|
||||
}}</span>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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
|
||||
class="flex"
|
||||
:class="{
|
||||
'flex-row': clickPosition === 'stash',
|
||||
'flex-row-reverse': clickPosition === 'inventory',
|
||||
}"
|
||||
>
|
||||
<related-items
|
||||
v-if="item?.isOk()"
|
||||
class="pointer-events-auto"
|
||||
:item="item.value"
|
||||
:click-position="clickPosition"
|
||||
/>
|
||||
<rate-limiter-state class="pointer-events-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, PropType, shallowRef, watch, computed, nextTick, provide } from 'vue'
|
||||
import { Result, ok, err } from 'neverthrow'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import UiErrorBox from '@/web/ui/UiErrorBox.vue'
|
||||
import UiPopover from '@/web/ui/Popover.vue'
|
||||
import CheckedItem from './CheckedItem.vue'
|
||||
import BackgroundInfo from './BackgroundInfo.vue'
|
||||
import { MainProcess, Host } from '@/web/background/IPC'
|
||||
import { usePoeninja } from '../background/Prices'
|
||||
import { useLeagues } from '@/web/background/Leagues'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
import { ItemCategory, ItemRarity, parseClipboard, ParsedItem } from '@/parser'
|
||||
import RelatedItems from './related-items/RelatedItems.vue'
|
||||
import RateLimiterState from './trade/RateLimiterState.vue'
|
||||
import UnidentifiedResolver from './unidentified-resolver/UnidentifiedResolver.vue'
|
||||
import CheckPositionCircle from './CheckPositionCircle.vue'
|
||||
import AppTitleBar from '@/web/ui/AppTitlebar.vue'
|
||||
import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue'
|
||||
import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces'
|
||||
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
|
||||
import {
|
||||
defineComponent,
|
||||
inject,
|
||||
PropType,
|
||||
shallowRef,
|
||||
watch,
|
||||
computed,
|
||||
nextTick,
|
||||
provide,
|
||||
} from 'vue';
|
||||
import { Result, ok, err } from 'neverthrow';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import UiErrorBox from '@/web/ui/UiErrorBox.vue';
|
||||
import UiPopover from '@/web/ui/Popover.vue';
|
||||
import CheckedItem from './CheckedItem.vue';
|
||||
import BackgroundInfo from './BackgroundInfo.vue';
|
||||
import { MainProcess, Host } from '@/web/background/IPC';
|
||||
import { usePoeninja } from '../background/Prices';
|
||||
import { useLeagues } from '@/web/background/Leagues';
|
||||
import { AppConfig } from '@/web/Config';
|
||||
import { ItemCategory, ItemRarity, parseClipboard, ParsedItem } from '@/parser';
|
||||
import RelatedItems from './related-items/RelatedItems.vue';
|
||||
import RateLimiterState from './trade/RateLimiterState.vue';
|
||||
import UnidentifiedResolver from './unidentified-resolver/UnidentifiedResolver.vue';
|
||||
import CheckPositionCircle from './CheckPositionCircle.vue';
|
||||
import AppTitleBar from '@/web/ui/AppTitlebar.vue';
|
||||
import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue';
|
||||
import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces';
|
||||
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue';
|
||||
|
||||
type ParseError = {
|
||||
name: string;
|
||||
message: string;
|
||||
rawText: ParsedItem['rawText'];
|
||||
name: string;
|
||||
message: string;
|
||||
rawText: ParsedItem['rawText'];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
AppTitleBar,
|
||||
CheckedItem,
|
||||
UnidentifiedResolver,
|
||||
BackgroundInfo,
|
||||
RelatedItems,
|
||||
RateLimiterState,
|
||||
CheckPositionCircle,
|
||||
ItemQuickPrice,
|
||||
UiErrorBox,
|
||||
UiPopover,
|
||||
ConversionWarningBanner
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
type: Object as PropType<PriceCheckWidget>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const wm = inject<WidgetManager>('wm')!
|
||||
const { xchgRate, initialLoading: xchgRateLoading, queuePricesFetch } = usePoeninja()
|
||||
components: {
|
||||
AppTitleBar,
|
||||
CheckedItem,
|
||||
UnidentifiedResolver,
|
||||
BackgroundInfo,
|
||||
RelatedItems,
|
||||
RateLimiterState,
|
||||
CheckPositionCircle,
|
||||
ItemQuickPrice,
|
||||
UiErrorBox,
|
||||
UiPopover,
|
||||
ConversionWarningBanner,
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
type: Object as PropType<PriceCheckWidget>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const wm = inject<WidgetManager>('wm')!;
|
||||
const {
|
||||
xchgRate,
|
||||
initialLoading: xchgRateLoading,
|
||||
queuePricesFetch,
|
||||
} = usePoeninja();
|
||||
|
||||
nextTick(() => {
|
||||
props.config.wmWants = 'hide'
|
||||
props.config.wmFlags = ['hide-on-blur', 'skip-menu']
|
||||
})
|
||||
nextTick(() => {
|
||||
props.config.wmWants = 'hide';
|
||||
props.config.wmFlags = ['hide-on-blur', 'skip-menu'];
|
||||
});
|
||||
|
||||
const item = shallowRef<null | Result<ParsedItem, ParseError>>(null)
|
||||
const advancedCheck = shallowRef(false)
|
||||
const checkPosition = shallowRef({ x: 1, y: 1 })
|
||||
const item = shallowRef<null | Result<ParsedItem, ParseError>>(null);
|
||||
const advancedCheck = shallowRef(false);
|
||||
const checkPosition = shallowRef({ x: 1, y: 1 });
|
||||
|
||||
MainProcess.onEvent('MAIN->CLIENT::item-text', (e) => {
|
||||
if (e.target !== 'price-check') return
|
||||
MainProcess.onEvent('MAIN->CLIENT::item-text', (e) => {
|
||||
if (e.target !== 'price-check') return;
|
||||
|
||||
if (Host.isElectron && !e.focusOverlay) {
|
||||
// everything in CSS pixels
|
||||
const width = 28.75 * AppConfig().fontSize
|
||||
const screenX =
|
||||
e.position.x - window.screenX > window.innerWidth / 2
|
||||
? window.screenX +
|
||||
window.innerWidth -
|
||||
wm.poePanelWidth.value -
|
||||
width
|
||||
: window.screenX + wm.poePanelWidth.value
|
||||
MainProcess.sendEvent({
|
||||
name: 'OVERLAY->MAIN::track-area',
|
||||
payload: {
|
||||
holdKey: props.config.hotkeyHold,
|
||||
closeThreshold: 2.5 * AppConfig().fontSize,
|
||||
from: e.position,
|
||||
area: {
|
||||
x: screenX,
|
||||
y: window.screenY,
|
||||
width,
|
||||
height: window.innerHeight
|
||||
},
|
||||
dpr: window.devicePixelRatio
|
||||
}
|
||||
})
|
||||
}
|
||||
closeBrowser()
|
||||
wm.show(props.config.wmId)
|
||||
checkPosition.value = e.position
|
||||
advancedCheck.value = e.focusOverlay
|
||||
if (Host.isElectron && !e.focusOverlay) {
|
||||
// everything in CSS pixels
|
||||
const width = 28.75 * AppConfig().fontSize;
|
||||
const screenX =
|
||||
e.position.x - window.screenX > window.innerWidth / 2
|
||||
? window.screenX +
|
||||
window.innerWidth -
|
||||
wm.poePanelWidth.value -
|
||||
width
|
||||
: window.screenX + wm.poePanelWidth.value;
|
||||
MainProcess.sendEvent({
|
||||
name: 'OVERLAY->MAIN::track-area',
|
||||
payload: {
|
||||
holdKey: props.config.hotkeyHold,
|
||||
closeThreshold: 2.5 * AppConfig().fontSize,
|
||||
from: e.position,
|
||||
area: {
|
||||
x: screenX,
|
||||
y: window.screenY,
|
||||
width,
|
||||
height: window.innerHeight,
|
||||
},
|
||||
dpr: window.devicePixelRatio,
|
||||
},
|
||||
});
|
||||
}
|
||||
closeBrowser();
|
||||
wm.show(props.config.wmId);
|
||||
checkPosition.value = e.position;
|
||||
advancedCheck.value = e.focusOverlay;
|
||||
|
||||
item.value = (
|
||||
e.item ? ok(e.item as ParsedItem) : parseClipboard(e.clipboard)
|
||||
)
|
||||
.andThen((item) =>
|
||||
(item.category === ItemCategory.HeistContract &&
|
||||
item.rarity !== ItemRarity.Unique) ||
|
||||
(item.category === ItemCategory.Sentinel &&
|
||||
item.rarity !== ItemRarity.Unique)
|
||||
? err('item.unknown')
|
||||
: ok(item)
|
||||
)
|
||||
.mapErr((err) => ({
|
||||
name: `${err}`,
|
||||
message: `${err}_help`,
|
||||
rawText: e.clipboard
|
||||
}))
|
||||
item.value = (
|
||||
e.item ? ok(e.item as ParsedItem) : parseClipboard(e.clipboard)
|
||||
)
|
||||
.andThen((item) =>
|
||||
(item.category === ItemCategory.HeistContract &&
|
||||
item.rarity !== ItemRarity.Unique) ||
|
||||
(item.category === ItemCategory.Sentinel &&
|
||||
item.rarity !== ItemRarity.Unique)
|
||||
? err('item.unknown')
|
||||
: ok(item)
|
||||
)
|
||||
.mapErr((err) => ({
|
||||
name: `${err}`,
|
||||
message: `${err}_help`,
|
||||
rawText: e.clipboard,
|
||||
}));
|
||||
|
||||
if (item.value.isOk()) {
|
||||
queuePricesFetch()
|
||||
}
|
||||
})
|
||||
if (item.value.isOk()) {
|
||||
queuePricesFetch();
|
||||
}
|
||||
});
|
||||
|
||||
function handleIdentification (identified: ParsedItem) {
|
||||
item.value = ok(identified)
|
||||
}
|
||||
function handleIdentification(identified: ParsedItem) {
|
||||
item.value = ok(identified);
|
||||
}
|
||||
|
||||
MainProcess.onEvent('MAIN->OVERLAY::hide-exclusive-widget', () => {
|
||||
wm.hide(props.config.wmId)
|
||||
})
|
||||
MainProcess.onEvent('MAIN->OVERLAY::hide-exclusive-widget', () => {
|
||||
wm.hide(props.config.wmId);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.config.wmWants,
|
||||
(state) => {
|
||||
if (state === 'hide') {
|
||||
closeBrowser()
|
||||
}
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.config.wmWants,
|
||||
(state) => {
|
||||
if (state === 'hide') {
|
||||
closeBrowser();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const leagues = useLeagues()
|
||||
const title = computed(() => leagues.selectedId.value || 'Exiled Exchange 2')
|
||||
const stableOrbCost = computed(() => (xchgRate.value) ? Math.round(xchgRate.value) : null)
|
||||
const isBrowserShown = computed(() => props.config.wmFlags.includes('has-browser'))
|
||||
const overlayKey = computed(() => AppConfig().overlayKey)
|
||||
const showCheckPos = computed(() => wm.active.value && props.config.showCursor)
|
||||
const isLeagueSelected = computed(() => Boolean(leagues.selectedId.value))
|
||||
const clickPosition = computed(() => {
|
||||
if (isBrowserShown.value) {
|
||||
return 'inventory'
|
||||
} else {
|
||||
return checkPosition.value.x > (window.screenX + window.innerWidth / 2)
|
||||
? 'inventory'
|
||||
: 'stash'
|
||||
// or {chat, vendor, center of screen}
|
||||
}
|
||||
})
|
||||
const leagues = useLeagues();
|
||||
const title = computed(
|
||||
() => leagues.selectedId.value || 'Exalted PoE2 Trade'
|
||||
);
|
||||
const stableOrbCost = computed(() =>
|
||||
xchgRate.value ? Math.round(xchgRate.value) : null
|
||||
);
|
||||
const isBrowserShown = computed(() =>
|
||||
props.config.wmFlags.includes('has-browser')
|
||||
);
|
||||
const overlayKey = computed(() => AppConfig().overlayKey);
|
||||
const showCheckPos = computed(
|
||||
() => wm.active.value && props.config.showCursor
|
||||
);
|
||||
const isLeagueSelected = computed(() => Boolean(leagues.selectedId.value));
|
||||
const clickPosition = computed(() => {
|
||||
if (isBrowserShown.value) {
|
||||
return 'inventory';
|
||||
} else {
|
||||
return checkPosition.value.x > window.screenX + window.innerWidth / 2
|
||||
? 'inventory'
|
||||
: 'stash';
|
||||
// or {chat, vendor, center of screen}
|
||||
}
|
||||
});
|
||||
|
||||
watch(isBrowserShown, (isShown) => {
|
||||
if (isShown) {
|
||||
wm.setFlag(props.config.wmId, 'hide-on-blur', false)
|
||||
wm.setFlag(props.config.wmId, 'invisible-on-blur', true)
|
||||
} else {
|
||||
wm.setFlag(props.config.wmId, 'invisible-on-blur', false)
|
||||
wm.setFlag(props.config.wmId, 'hide-on-blur', true)
|
||||
}
|
||||
})
|
||||
watch(isBrowserShown, (isShown) => {
|
||||
if (isShown) {
|
||||
wm.setFlag(props.config.wmId, 'hide-on-blur', false);
|
||||
wm.setFlag(props.config.wmId, 'invisible-on-blur', true);
|
||||
} else {
|
||||
wm.setFlag(props.config.wmId, 'invisible-on-blur', false);
|
||||
wm.setFlag(props.config.wmId, 'hide-on-blur', true);
|
||||
}
|
||||
});
|
||||
|
||||
function closePriceCheck () {
|
||||
if (isBrowserShown.value || !Host.isElectron) {
|
||||
wm.hide(props.config.wmId)
|
||||
} else {
|
||||
Host.sendEvent({ name: 'OVERLAY->MAIN::focus-game', payload: undefined })
|
||||
}
|
||||
}
|
||||
function closePriceCheck() {
|
||||
if (isBrowserShown.value || !Host.isElectron) {
|
||||
wm.hide(props.config.wmId);
|
||||
} else {
|
||||
Host.sendEvent({
|
||||
name: 'OVERLAY->MAIN::focus-game',
|
||||
payload: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function openLeagueSelection () {
|
||||
const settings = wm.widgets.value.find(w => w.wmType === 'settings')!
|
||||
wm.setFlag(settings.wmId, `settings:widget:${props.config.wmId}`, true)
|
||||
wm.show(settings.wmId)
|
||||
}
|
||||
function openLeagueSelection() {
|
||||
const settings = wm.widgets.value.find((w) => w.wmType === 'settings')!;
|
||||
wm.setFlag(settings.wmId, `settings:widget:${props.config.wmId}`, true);
|
||||
wm.show(settings.wmId);
|
||||
}
|
||||
|
||||
const iframeEl = shallowRef<HTMLIFrameElement | null>(null)
|
||||
const iframeEl = shallowRef<HTMLIFrameElement | null>(null);
|
||||
|
||||
function showBrowser (url: string) {
|
||||
wm.setFlag(props.config.wmId, 'has-browser', true)
|
||||
nextTick(() => {
|
||||
iframeEl.value!.src = url
|
||||
})
|
||||
}
|
||||
function showBrowser(url: string) {
|
||||
wm.setFlag(props.config.wmId, 'has-browser', true);
|
||||
nextTick(() => {
|
||||
iframeEl.value!.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
function closeBrowser () {
|
||||
wm.setFlag(props.config.wmId, 'has-browser', false)
|
||||
}
|
||||
function closeBrowser() {
|
||||
wm.setFlag(props.config.wmId, 'has-browser', false);
|
||||
}
|
||||
|
||||
provide<(url: string) => void>('builtin-browser', showBrowser)
|
||||
provide<(url: string) => void>('builtin-browser', showBrowser);
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t } = useI18n();
|
||||
|
||||
return {
|
||||
t,
|
||||
clickPosition,
|
||||
isBrowserShown,
|
||||
iframeEl,
|
||||
closePriceCheck,
|
||||
title,
|
||||
stableOrbCost,
|
||||
xchgRateLoading,
|
||||
showCheckPos,
|
||||
checkPosition,
|
||||
item,
|
||||
advancedCheck,
|
||||
handleIdentification,
|
||||
overlayKey,
|
||||
isLeagueSelected,
|
||||
openLeagueSelection
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
t,
|
||||
clickPosition,
|
||||
isBrowserShown,
|
||||
iframeEl,
|
||||
closePriceCheck,
|
||||
title,
|
||||
stableOrbCost,
|
||||
xchgRateLoading,
|
||||
showCheckPos,
|
||||
checkPosition,
|
||||
item,
|
||||
advancedCheck,
|
||||
handleIdentification,
|
||||
overlayKey,
|
||||
isLeagueSelected,
|
||||
openLeagueSelection,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -63,7 +63,7 @@ const stats = computed(() => {
|
||||
if (!parsed.roll) {
|
||||
return {
|
||||
text: parsed.translation.string,
|
||||
contribution,
|
||||
contribution: contribution,
|
||||
contributes: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export function createFilters (
|
||||
}
|
||||
filters.searchRelaxed = {
|
||||
category: item.category,
|
||||
disabled
|
||||
disabled: disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ export function calculatedStatToFilter (
|
||||
? FilterTag.Enchant
|
||||
: FilterTag.Variant,
|
||||
oils: decodeOils(calc),
|
||||
sources,
|
||||
sources: sources,
|
||||
option: {
|
||||
value: sources[0].contributes!.value
|
||||
},
|
||||
@@ -197,7 +197,7 @@ export function calculatedStatToFilter (
|
||||
text: translation.string,
|
||||
tag: (type as unknown) as FilterTag,
|
||||
oils: decodeOils(calc),
|
||||
sources,
|
||||
sources: sources,
|
||||
roll: undefined,
|
||||
disabled: true
|
||||
}
|
||||
@@ -288,7 +288,7 @@ export function calculatedStatToFilter (
|
||||
bounds: (item.rarity === ItemRarity.Unique && roll.min !== roll.max && calc.stat.better !== StatBetter.NotComparable)
|
||||
? filterBounds
|
||||
: undefined,
|
||||
dp,
|
||||
dp: dp,
|
||||
isNegated: false,
|
||||
tradeInvert: calc.stat.trade.inverted
|
||||
}
|
||||
@@ -427,10 +427,10 @@ function applyClusterJewelRules (filters: StatFilter[]) {
|
||||
// 4 is [_, 5]
|
||||
if (filter.roll!.value === 4) {
|
||||
filter.roll!.max = 5
|
||||
// 5 is [5, 5]
|
||||
// 5 is [5, 5]
|
||||
} else if (filter.roll!.value === 5) {
|
||||
filter.roll!.min = filter.roll!.default.min
|
||||
// 3, 6, 10, 11, 12 are [n, _]
|
||||
// 3, 6, 10, 11, 12 are [n, _]
|
||||
} else if (
|
||||
filter.roll!.value === 3 ||
|
||||
filter.roll!.value === 6 ||
|
||||
|
||||
@@ -335,7 +335,7 @@ export function filterPseudo (ctx: FiltersCreationContext) {
|
||||
const filter = calculatedStatToFilter({
|
||||
stat: STAT_BY_REF(rule.pseudo)!,
|
||||
type: ModifierType.Pseudo,
|
||||
sources
|
||||
sources: sources
|
||||
}, ctx.searchInRange, ctx.item)
|
||||
|
||||
filter.disabled = rule.disabled ?? true
|
||||
|
||||
@@ -257,7 +257,7 @@ function propToFilter (opts: {
|
||||
better: StatBetter.PositiveRoll
|
||||
}
|
||||
const filter = calculatedStatToFilter({
|
||||
stat,
|
||||
stat: stat,
|
||||
type: ModifierType.Pseudo,
|
||||
sources: [{
|
||||
modifier: {
|
||||
@@ -265,7 +265,7 @@ function propToFilter (opts: {
|
||||
stats: []
|
||||
},
|
||||
stat: {
|
||||
stat,
|
||||
stat: stat,
|
||||
translation: stat.matchers[0],
|
||||
roll: {
|
||||
dp: opts.dp ?? false,
|
||||
|
||||
@@ -1,144 +1,144 @@
|
||||
import { ParsedItem } from '@/parser'
|
||||
import { useLeagues } from '@/web/background/Leagues'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { Cache } from '../trade/Cache'
|
||||
import { usePoeninja } from '@/web/background/Prices'
|
||||
import { ParsedItem } from '@/parser';
|
||||
import { useLeagues } from '@/web/background/Leagues';
|
||||
import { Host } from '@/web/background/IPC';
|
||||
import { Cache } from '../trade/Cache';
|
||||
import { usePoeninja } from '@/web/background/Prices';
|
||||
|
||||
const cache = new Cache()
|
||||
const cache = new Cache();
|
||||
|
||||
interface PoepricesApiResponse {
|
||||
/* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt'
|
||||
error: number
|
||||
error_msg: string
|
||||
warning_msg: string
|
||||
max: number
|
||||
min: number
|
||||
pred_confidence_score: number
|
||||
pred_explanation: Array<[string, number]>
|
||||
/* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt';
|
||||
error: number;
|
||||
error_msg: string;
|
||||
warning_msg: string;
|
||||
max: number;
|
||||
min: number;
|
||||
pred_confidence_score: number;
|
||||
pred_explanation: Array<[string, number]>;
|
||||
}
|
||||
|
||||
export interface RareItemPrice {
|
||||
max: number
|
||||
min: number
|
||||
confidence: number
|
||||
currency: 'chaos' | 'div'
|
||||
explanation: Array<{
|
||||
name: string
|
||||
contrib: number
|
||||
}>
|
||||
max: number;
|
||||
min: number;
|
||||
confidence: number;
|
||||
currency: 'chaos' | 'div';
|
||||
explanation: Array<{
|
||||
name: string;
|
||||
contrib: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export async function requestPoeprices (
|
||||
item: ParsedItem
|
||||
export async function requestPoeprices(
|
||||
item: ParsedItem
|
||||
): Promise<RareItemPrice> {
|
||||
const query = querystring({
|
||||
i: utf8ToBase64(transformItemText(item.rawText)),
|
||||
l: useLeagues().selectedId.value,
|
||||
s: 'awakened-poe-trade' // might be required name here
|
||||
})
|
||||
const query = querystring({
|
||||
i: utf8ToBase64(transformItemText(item.rawText)),
|
||||
l: useLeagues().selectedId.value,
|
||||
s: 'awakened-poe-trade', // might be required name here
|
||||
});
|
||||
|
||||
let data = cache.get<PoepricesApiResponse>(query)
|
||||
if (!data) {
|
||||
const response = await Host.proxy(`www.poeprices.info/api?${query}`)
|
||||
try {
|
||||
data = (await response.json()) as PoepricesApiResponse
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`${response.status}, poeprices.info API is under load or down.`
|
||||
)
|
||||
}
|
||||
let data = cache.get<PoepricesApiResponse>(query);
|
||||
if (!data) {
|
||||
const response = await Host.proxy(`www.poeprices.info/api?${query}`);
|
||||
try {
|
||||
data = (await response.json()) as PoepricesApiResponse;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`${response.status}, poeprices.info API is under load or down.`
|
||||
);
|
||||
}
|
||||
|
||||
if (data.error !== 0) {
|
||||
throw new Error(data.error_msg)
|
||||
}
|
||||
if (data.error !== 0) {
|
||||
throw new Error(data.error_msg);
|
||||
}
|
||||
|
||||
cache.set<PoepricesApiResponse>(query, data, 300)
|
||||
}
|
||||
cache.set<PoepricesApiResponse>(query, data, 300);
|
||||
}
|
||||
|
||||
if (data.currency === 'exalt') {
|
||||
const { findPriceByQuery, autoCurrency } = usePoeninja()
|
||||
const xchgExalted = findPriceByQuery({
|
||||
ns: 'ITEM',
|
||||
name: 'Exalted Orb',
|
||||
variant: undefined
|
||||
})
|
||||
if (!xchgExalted) {
|
||||
throw new Error('poeprices.info gave the price in Exalted Orbs.')
|
||||
}
|
||||
const converted = autoCurrency([
|
||||
data.min * xchgExalted.chaos,
|
||||
data.max * xchgExalted.chaos
|
||||
])
|
||||
data.min = converted.min
|
||||
data.max = converted.max
|
||||
data.currency = converted.currency === 'div' ? 'divine' : 'chaos'
|
||||
} else if (data.currency !== 'divine' && data.currency !== 'chaos') {
|
||||
throw new Error('poeprices.info gave the price in unknown currency.')
|
||||
}
|
||||
if (data.currency === 'exalt') {
|
||||
const { findPriceByQuery, autoCurrency } = usePoeninja();
|
||||
const xchgExalted = findPriceByQuery({
|
||||
ns: 'ITEM',
|
||||
name: 'Exalted Orb',
|
||||
variant: undefined,
|
||||
});
|
||||
if (!xchgExalted) {
|
||||
throw new Error('poeprices.info gave the price in Exalted Orbs.');
|
||||
}
|
||||
const converted = autoCurrency([
|
||||
data.min * xchgExalted.chaos,
|
||||
data.max * xchgExalted.chaos,
|
||||
]);
|
||||
data.min = converted.min;
|
||||
data.max = converted.max;
|
||||
data.currency = converted.currency === 'div' ? 'divine' : 'chaos';
|
||||
} else if (data.currency !== 'divine' && data.currency !== 'chaos') {
|
||||
throw new Error('poeprices.info gave the price in unknown currency.');
|
||||
}
|
||||
|
||||
return {
|
||||
currency: data.currency === 'divine' ? 'div' : 'chaos',
|
||||
min: data.min,
|
||||
max: data.max,
|
||||
confidence: Math.round(data.pred_confidence_score),
|
||||
explanation: data.pred_explanation.map((expl) => ({
|
||||
name: expl[0],
|
||||
contrib: Math.round(expl[1] * 100)
|
||||
}))
|
||||
}
|
||||
return {
|
||||
currency: data.currency === 'divine' ? 'div' : 'chaos',
|
||||
min: data.min,
|
||||
max: data.max,
|
||||
confidence: Math.round(data.pred_confidence_score),
|
||||
explanation: data.pred_explanation.map((expl) => ({
|
||||
name: expl[0],
|
||||
contrib: Math.round(expl[1] * 100),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function getExternalLink (item: ParsedItem): string {
|
||||
const query = querystring({
|
||||
i: utf8ToBase64(transformItemText(item.rawText)),
|
||||
l: useLeagues().selectedId.value,
|
||||
s: 'awakened-poe-trade',
|
||||
w: 1
|
||||
})
|
||||
return `https://www.poeprices.info/api?${query}`
|
||||
export function getExternalLink(item: ParsedItem): string {
|
||||
const query = querystring({
|
||||
i: utf8ToBase64(transformItemText(item.rawText)),
|
||||
l: useLeagues().selectedId.value,
|
||||
s: 'awakened-poe-trade',
|
||||
w: 1,
|
||||
});
|
||||
return `https://www.poeprices.info/api?${query}`;
|
||||
}
|
||||
|
||||
export async function sendFeedback (
|
||||
feedback: { text: string, option: 'fair' | 'low' | 'high' },
|
||||
prediction: Pick<PoepricesApiResponse, 'min' | 'max' | 'currency'>,
|
||||
item: ParsedItem
|
||||
export async function sendFeedback(
|
||||
feedback: { text: string; option: 'fair' | 'low' | 'high' },
|
||||
prediction: Pick<PoepricesApiResponse, 'min' | 'max' | 'currency'>,
|
||||
item: ParsedItem
|
||||
): Promise<void> {
|
||||
const body = new FormData()
|
||||
body.append('selector', feedback.option)
|
||||
body.append('feedbacktxt', feedback.text)
|
||||
body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText)))
|
||||
body.append('source', 'awakened-poe-trade')
|
||||
body.append('min', String(prediction.min))
|
||||
body.append('max', String(prediction.max))
|
||||
body.append('currency', prediction.currency)
|
||||
body.append('league', useLeagues().selectedId.value!)
|
||||
// body.append('debug', String(1))
|
||||
const body = new FormData();
|
||||
body.append('selector', feedback.option);
|
||||
body.append('feedbacktxt', feedback.text);
|
||||
body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText)));
|
||||
body.append('source', 'awakened-poe-trade');
|
||||
body.append('min', String(prediction.min));
|
||||
body.append('max', String(prediction.max));
|
||||
body.append('currency', prediction.currency);
|
||||
body.append('league', useLeagues().selectedId.value!);
|
||||
// body.append('debug', String(1))
|
||||
|
||||
const response = await Host.proxy('www.poeprices.info/send_feedback', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const text = await response.text()
|
||||
// console.assert(text === `"${feedback.option}"`)
|
||||
const response = await Host.proxy('www.poeprices.info/send_feedback', {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const text = await response.text();
|
||||
// console.assert(text === `"${feedback.option}"`)
|
||||
}
|
||||
|
||||
function utf8ToBase64 (value: string) {
|
||||
return btoa(unescape(encodeURIComponent(value)))
|
||||
function utf8ToBase64(value: string) {
|
||||
return btoa(unescape(encodeURIComponent(value)));
|
||||
}
|
||||
|
||||
function querystring (q: Record<string, any>) {
|
||||
return Object.entries(q)
|
||||
.map((pair) => pair.map(encodeURIComponent).join('='))
|
||||
.join('&')
|
||||
function querystring(q: Record<string, any>) {
|
||||
return Object.entries(q)
|
||||
.map((pair) => pair.map(encodeURIComponent).join('='))
|
||||
.join('&');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO blocked by poeprices.info not supporting advanced text
|
||||
*/
|
||||
function transformItemText (rawText: string) {
|
||||
// this may not account for all cases
|
||||
return rawText
|
||||
.replace(/(?<=\d)(\([^)]+\))/gm, '')
|
||||
.replace(/^\{.+\}$\n/gm, '')
|
||||
function transformItemText(rawText: string) {
|
||||
// this may not account for all cases
|
||||
return rawText
|
||||
.replace(/(?<=\d)(\([^)]+\))/gm, '')
|
||||
.replace(/^\{.+\}$\n/gm, '');
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ import TradeLinks from './TradeLinks.vue'
|
||||
|
||||
const slowdown = artificialSlowdown(900)
|
||||
|
||||
function useBulkApi () {
|
||||
function useBulkApi() {
|
||||
type BulkSearchExtended = Record<'xchgChaos' | 'xchgStable', {
|
||||
listed: Ref<BulkSearch | null>
|
||||
listedLazy: ComputedRef<PricingResult[]>
|
||||
@@ -121,7 +121,7 @@ function useBulkApi () {
|
||||
const error = shallowRef<string | null>(null)
|
||||
const result = shallowRef<BulkSearchExtended | null>(null)
|
||||
|
||||
async function search (item: ParsedItem, filters: ItemFilters) {
|
||||
async function search(item: ParsedItem, filters: ItemFilters) {
|
||||
try {
|
||||
searchId += 1
|
||||
error.value = null
|
||||
@@ -133,8 +133,8 @@ function useBulkApi () {
|
||||
const have = (item.info.refName === 'Chaos Orb')
|
||||
? ['divine']
|
||||
: (item.info.refName === 'Divine Orb')
|
||||
? ['chaos']
|
||||
: ['divine', 'chaos']
|
||||
? ['chaos']
|
||||
: ['divine', 'chaos']
|
||||
|
||||
const optimisticSearch = await execBulkSearch(
|
||||
item, filters, have, { accountName: AppConfig().accountName })
|
||||
@@ -149,7 +149,7 @@ function useBulkApi () {
|
||||
}
|
||||
}
|
||||
|
||||
function getResultsByHave (
|
||||
function getResultsByHave(
|
||||
item: ParsedItem,
|
||||
filters: ItemFilters,
|
||||
preloaded: Array<BulkSearch | null>,
|
||||
@@ -203,7 +203,7 @@ export default defineComponent({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const widget = computed(() => AppConfig<PriceCheckWidget>('price-check')!)
|
||||
const { error, result, search } = useBulkApi()
|
||||
|
||||
@@ -236,7 +236,7 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
function makeTradeLink (_have?: string[]) {
|
||||
function makeTradeLink(_have?: string[]) {
|
||||
const have = _have ?? ((selectedCurr.value === 'xchgStable') ? ['divine'] : ['chaos'])
|
||||
const httpPostBody = createTradeRequest(props.filters, props.item, have)
|
||||
const httpGetQuery = { exchange: httpPostBody.query }
|
||||
@@ -254,7 +254,7 @@ export default defineComponent({
|
||||
execSearch: () => { search(props.item, props.filters) },
|
||||
showSeller: computed(() => widget.value.showSeller),
|
||||
makeTradeLink,
|
||||
openTradeLink () {
|
||||
openTradeLink() {
|
||||
showBrowser(makeTradeLink(['mirror']))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ const API_FETCH_LIMIT = 100
|
||||
const MIN_NOT_GROUPED = 7
|
||||
const MIN_GROUPED = 10
|
||||
|
||||
function useTradeApi () {
|
||||
function useTradeApi() {
|
||||
let searchId = 0
|
||||
const error = shallowRef<string | null>(null)
|
||||
const searchResult = shallowRef<SearchResult | null>(null)
|
||||
@@ -143,7 +143,7 @@ function useTradeApi () {
|
||||
return out
|
||||
})
|
||||
|
||||
async function search (filters: ItemFilters, stats: StatFilter[], item: ParsedItem) {
|
||||
async function search(filters: ItemFilters, stats: StatFilter[], item: ParsedItem) {
|
||||
try {
|
||||
searchId += 1
|
||||
error.value = null
|
||||
@@ -174,7 +174,7 @@ function useTradeApi () {
|
||||
}
|
||||
|
||||
let fetched = 20
|
||||
async function fetchMore (): Promise<void> {
|
||||
async function fetchMore(): Promise<void> {
|
||||
if (_searchId !== searchId) return
|
||||
const totalGrouped = groupedResults.value.length
|
||||
const totalNotGrouped = groupedResults.value.reduce((len, res) =>
|
||||
@@ -215,7 +215,7 @@ export default defineComponent({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const widget = computed(() => AppConfig<PriceCheckWidget>('price-check')!)
|
||||
|
||||
watch(() => props.item, (item) => {
|
||||
@@ -226,7 +226,7 @@ export default defineComponent({
|
||||
|
||||
const showBrowser = inject<(url: string) => void>('builtin-browser')!
|
||||
|
||||
function makeTradeLink () {
|
||||
function makeTradeLink() {
|
||||
return (searchResult.value)
|
||||
? `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}/${searchResult.value.id}`
|
||||
: `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}?q=${JSON.stringify(createTradeRequest(props.filters, props.stats, props.item))}`
|
||||
@@ -253,7 +253,7 @@ export default defineComponent({
|
||||
error,
|
||||
showSeller: computed(() => widget.value.showSeller),
|
||||
makeTradeLink,
|
||||
openTradeLink () {
|
||||
openTradeLink() {
|
||||
showBrowser(makeTradeLink())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,225 +1,225 @@
|
||||
import { DateTime } from 'luxon'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { DateTime } from 'luxon';
|
||||
import { Host } from '@/web/background/IPC';
|
||||
import {
|
||||
TradeResponse,
|
||||
Account,
|
||||
getTradeEndpoint,
|
||||
RATE_LIMIT_RULES,
|
||||
adjustRateLimits,
|
||||
tradeTag,
|
||||
preventQueueCreation
|
||||
} from './common'
|
||||
import { RateLimiter } from './RateLimiter'
|
||||
import { ItemFilters } from '../filters/interfaces'
|
||||
import { ParsedItem } from '@/parser'
|
||||
import { Cache } from './Cache'
|
||||
TradeResponse,
|
||||
Account,
|
||||
getTradeEndpoint,
|
||||
RATE_LIMIT_RULES,
|
||||
adjustRateLimits,
|
||||
tradeTag,
|
||||
preventQueueCreation,
|
||||
} from './common';
|
||||
import { RateLimiter } from './RateLimiter';
|
||||
import { ItemFilters } from '../filters/interfaces';
|
||||
import { ParsedItem } from '@/parser';
|
||||
import { Cache } from './Cache';
|
||||
|
||||
interface TradeRequest {
|
||||
/* eslint-disable camelcase */ engine: 'new'
|
||||
query: {
|
||||
status: { option: 'online' | 'onlineleague' | 'any' }
|
||||
have: string[]
|
||||
want: string[]
|
||||
minimum?: number
|
||||
fulfillable?: null
|
||||
}
|
||||
sort: { have: 'asc' }
|
||||
/* eslint-disable camelcase */ engine: 'new';
|
||||
query: {
|
||||
status: { option: 'online' | 'onlineleague' | 'any' };
|
||||
have: string[];
|
||||
want: string[];
|
||||
minimum?: number;
|
||||
fulfillable?: null;
|
||||
};
|
||||
sort: { have: 'asc' };
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
id: string
|
||||
result: Record<string, FetchResult>
|
||||
total: number
|
||||
id: string;
|
||||
result: Record<string, FetchResult>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface FetchResult {
|
||||
id: string
|
||||
listing: {
|
||||
indexed: string
|
||||
offers: Array<{
|
||||
exchange: {
|
||||
currency: string
|
||||
amount: number
|
||||
}
|
||||
item: {
|
||||
amount: number
|
||||
stock: number
|
||||
}
|
||||
}>
|
||||
account: Account
|
||||
}
|
||||
id: string;
|
||||
listing: {
|
||||
indexed: string;
|
||||
offers: Array<{
|
||||
exchange: {
|
||||
currency: string;
|
||||
amount: number;
|
||||
};
|
||||
item: {
|
||||
amount: number;
|
||||
stock: number;
|
||||
};
|
||||
}>;
|
||||
account: Account;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PricingResult {
|
||||
id: string
|
||||
relativeDate: string
|
||||
exchangeAmount: number
|
||||
itemAmount: number
|
||||
stock: number
|
||||
accountStatus: 'offline' | 'online' | 'afk'
|
||||
isMine: boolean
|
||||
accountName: string
|
||||
ign: string
|
||||
id: string;
|
||||
relativeDate: string;
|
||||
exchangeAmount: number;
|
||||
itemAmount: number;
|
||||
stock: number;
|
||||
accountStatus: 'offline' | 'online' | 'afk';
|
||||
isMine: boolean;
|
||||
accountName: string;
|
||||
ign: string;
|
||||
}
|
||||
|
||||
const cache = new Cache()
|
||||
const cache = new Cache();
|
||||
|
||||
async function requestTradeResultList (
|
||||
body: TradeRequest,
|
||||
leagueId: string
|
||||
async function requestTradeResultList(
|
||||
body: TradeRequest,
|
||||
leagueId: string
|
||||
): Promise<SearchResult> {
|
||||
let data = cache.get<SearchResult>([body, leagueId])
|
||||
let data = cache.get<SearchResult>([body, leagueId]);
|
||||
|
||||
if (!data) {
|
||||
preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }])
|
||||
if (!data) {
|
||||
preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }]);
|
||||
|
||||
await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE)
|
||||
await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE);
|
||||
|
||||
const response = await Host.proxy(
|
||||
`${getTradeEndpoint()}/api/trade2/exchange/${leagueId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
)
|
||||
adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers)
|
||||
const response = await Host.proxy(
|
||||
`${getTradeEndpoint()}/api/trade2/exchange/${leagueId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
}
|
||||
);
|
||||
adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers);
|
||||
|
||||
const _data = (await response.json()) as TradeResponse<SearchResult>
|
||||
if (_data.error) {
|
||||
throw new Error(_data.error.message)
|
||||
} else {
|
||||
data = _data
|
||||
}
|
||||
const _data = (await response.json()) as TradeResponse<SearchResult>;
|
||||
if (_data.error) {
|
||||
throw new Error(_data.error.message);
|
||||
} else {
|
||||
data = _data;
|
||||
}
|
||||
|
||||
cache.set<SearchResult>(
|
||||
[body, leagueId],
|
||||
data,
|
||||
Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE)
|
||||
)
|
||||
}
|
||||
cache.set<SearchResult>(
|
||||
[body, leagueId],
|
||||
data,
|
||||
Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE)
|
||||
);
|
||||
}
|
||||
|
||||
return data
|
||||
return data;
|
||||
}
|
||||
|
||||
function toPricingResult (
|
||||
result: FetchResult,
|
||||
opts: { accountName: string },
|
||||
offer: number
|
||||
function toPricingResult(
|
||||
result: FetchResult,
|
||||
opts: { accountName: string },
|
||||
offer: number
|
||||
): PricingResult {
|
||||
return {
|
||||
id: result.id,
|
||||
relativeDate:
|
||||
DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ??
|
||||
'',
|
||||
exchangeAmount: result.listing.offers[offer].exchange.amount,
|
||||
itemAmount: result.listing.offers[offer].item.amount,
|
||||
stock: result.listing.offers[offer].item.stock,
|
||||
isMine: result.listing.account.name === opts.accountName,
|
||||
ign: result.listing.account.lastCharacterName,
|
||||
accountName: result.listing.account.name,
|
||||
accountStatus: result.listing.account.online
|
||||
? result.listing.account.online.status === 'afk'
|
||||
? 'afk'
|
||||
: 'online'
|
||||
: 'offline'
|
||||
}
|
||||
return {
|
||||
id: result.id,
|
||||
relativeDate:
|
||||
DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ??
|
||||
'',
|
||||
exchangeAmount: result.listing.offers[offer].exchange.amount,
|
||||
itemAmount: result.listing.offers[offer].item.amount,
|
||||
stock: result.listing.offers[offer].item.stock,
|
||||
isMine: result.listing.account.name === opts.accountName,
|
||||
ign: result.listing.account.lastCharacterName,
|
||||
accountName: result.listing.account.name,
|
||||
accountStatus: result.listing.account.online
|
||||
? result.listing.account.online.status === 'afk'
|
||||
? 'afk'
|
||||
: 'online'
|
||||
: 'offline',
|
||||
};
|
||||
}
|
||||
|
||||
export interface BulkSearch {
|
||||
queryId: string
|
||||
haveTag: string
|
||||
total: number
|
||||
listed: PricingResult[]
|
||||
queryId: string;
|
||||
haveTag: string;
|
||||
total: number;
|
||||
listed: PricingResult[];
|
||||
}
|
||||
|
||||
export function createTradeRequest (
|
||||
filters: ItemFilters,
|
||||
item: ParsedItem,
|
||||
have: string[]
|
||||
export function createTradeRequest(
|
||||
filters: ItemFilters,
|
||||
item: ParsedItem,
|
||||
have: string[]
|
||||
): TradeRequest {
|
||||
return {
|
||||
engine: 'new',
|
||||
query: {
|
||||
have,
|
||||
want: [tradeTag(item)!],
|
||||
status: {
|
||||
option: filters.trade.offline
|
||||
? 'any'
|
||||
: filters.trade.onlineInLeague
|
||||
? 'onlineleague'
|
||||
: 'online'
|
||||
},
|
||||
minimum:
|
||||
filters.stackSize && !filters.stackSize.disabled
|
||||
? filters.stackSize.value
|
||||
: undefined
|
||||
// fulfillable: null
|
||||
},
|
||||
sort: { have: 'asc' }
|
||||
}
|
||||
return {
|
||||
engine: 'new',
|
||||
query: {
|
||||
have: have,
|
||||
want: [tradeTag(item)!],
|
||||
status: {
|
||||
option: filters.trade.offline
|
||||
? 'any'
|
||||
: filters.trade.onlineInLeague
|
||||
? 'onlineleague'
|
||||
: 'online',
|
||||
},
|
||||
minimum:
|
||||
filters.stackSize && !filters.stackSize.disabled
|
||||
? filters.stackSize.value
|
||||
: undefined,
|
||||
// fulfillable: null
|
||||
},
|
||||
sort: { have: 'asc' },
|
||||
};
|
||||
}
|
||||
|
||||
const SHOW_RESULTS = 20
|
||||
const API_FETCH_LIMIT = 100
|
||||
const SHOW_RESULTS = 20;
|
||||
const API_FETCH_LIMIT = 100;
|
||||
|
||||
export async function execBulkSearch (
|
||||
item: ParsedItem,
|
||||
filters: ItemFilters,
|
||||
have: string[],
|
||||
opts: { accountName: string }
|
||||
export async function execBulkSearch(
|
||||
item: ParsedItem,
|
||||
filters: ItemFilters,
|
||||
have: string[],
|
||||
opts: { accountName: string }
|
||||
): Promise<Array<BulkSearch | null>> {
|
||||
const query = await requestTradeResultList(
|
||||
createTradeRequest(filters, item, have),
|
||||
filters.trade.league
|
||||
)
|
||||
const query = await requestTradeResultList(
|
||||
createTradeRequest(filters, item, have),
|
||||
filters.trade.league
|
||||
);
|
||||
|
||||
const offer = 0
|
||||
const results = Object.values(query.result).filter(
|
||||
(result) => result.listing.offers.length === 1
|
||||
)
|
||||
const offer = 0;
|
||||
const results = Object.values(query.result).filter(
|
||||
(result) => result.listing.offers.length === 1
|
||||
);
|
||||
|
||||
const resultByHave = have.map((tradeTag) => {
|
||||
const resultsTag = results.filter(
|
||||
(result) => result.listing.offers[offer].exchange.currency === tradeTag
|
||||
)
|
||||
const resultByHave = have.map((tradeTag) => {
|
||||
const resultsTag = results.filter(
|
||||
(result) => result.listing.offers[offer].exchange.currency === tradeTag
|
||||
);
|
||||
|
||||
const loadedOnDemand =
|
||||
tradeTag === 'chaos' &&
|
||||
resultsTag.length < SHOW_RESULTS &&
|
||||
query.total > API_FETCH_LIMIT
|
||||
if (loadedOnDemand) return null
|
||||
const loadedOnDemand =
|
||||
tradeTag === 'chaos' &&
|
||||
resultsTag.length < SHOW_RESULTS &&
|
||||
query.total > API_FETCH_LIMIT;
|
||||
if (loadedOnDemand) return null;
|
||||
|
||||
const listed = resultsTag
|
||||
.sort(
|
||||
(a, b) =>
|
||||
a.listing.offers[offer].exchange.amount /
|
||||
a.listing.offers[offer].item.amount -
|
||||
b.listing.offers[offer].exchange.amount /
|
||||
b.listing.offers[offer].item.amount
|
||||
)
|
||||
.slice(0, SHOW_RESULTS)
|
||||
.map((result) => toPricingResult(result, opts, offer))
|
||||
const listed = resultsTag
|
||||
.sort(
|
||||
(a, b) =>
|
||||
a.listing.offers[offer].exchange.amount /
|
||||
a.listing.offers[offer].item.amount -
|
||||
b.listing.offers[offer].exchange.amount /
|
||||
b.listing.offers[offer].item.amount
|
||||
)
|
||||
.slice(0, SHOW_RESULTS)
|
||||
.map((result) => toPricingResult(result, opts, offer));
|
||||
|
||||
const chaosIsLoaded =
|
||||
tradeTag === 'divine' &&
|
||||
resultsTag.length < results.length &&
|
||||
(results.length - resultsTag.length >= SHOW_RESULTS ||
|
||||
query.total <= API_FETCH_LIMIT)
|
||||
const chaosIsLoaded =
|
||||
tradeTag === 'divine' &&
|
||||
resultsTag.length < results.length &&
|
||||
(results.length - resultsTag.length >= SHOW_RESULTS ||
|
||||
query.total <= API_FETCH_LIMIT);
|
||||
|
||||
return {
|
||||
queryId: query.id,
|
||||
haveTag: tradeTag,
|
||||
// this is a best guess when making request with multiple `have` currencies
|
||||
total: chaosIsLoaded
|
||||
? resultsTag.length
|
||||
: query.total - (results.length - resultsTag.length),
|
||||
listed
|
||||
}
|
||||
})
|
||||
return {
|
||||
queryId: query.id,
|
||||
haveTag: tradeTag,
|
||||
// this is a best guess when making request with multiple `have` currencies
|
||||
total: chaosIsLoaded
|
||||
? resultsTag.length
|
||||
: query.total - (results.length - resultsTag.length),
|
||||
listed: listed,
|
||||
};
|
||||
});
|
||||
|
||||
return resultByHave
|
||||
return resultByHave;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -104,7 +104,7 @@ export default defineComponent({
|
||||
: autoCurrency(trend.chaos)
|
||||
|
||||
return {
|
||||
price,
|
||||
price: price,
|
||||
change: deltaFromGraph(trend.graph),
|
||||
url: trend.url
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default defineComponent({
|
||||
default: null
|
||||
}
|
||||
},
|
||||
setup (props, ctx) {
|
||||
setup(props, ctx) {
|
||||
const identifiedVariants = computed(() => {
|
||||
const baseType = props.item!.info.refName
|
||||
const possible: BaseType[] = []
|
||||
@@ -55,10 +55,10 @@ export default defineComponent({
|
||||
!props.item.info.unique
|
||||
})
|
||||
|
||||
function select (info: BaseType) {
|
||||
function select(info: BaseType) {
|
||||
const newItem: ParsedItem = {
|
||||
...props.item!,
|
||||
info
|
||||
info: info
|
||||
}
|
||||
ctx.emit('identify', newItem)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ import SettingsStopwatch from './stopwatch.vue'
|
||||
import SettingsItemSearch from '../item-search/settings-item-search.vue'
|
||||
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
|
||||
|
||||
function shuffle<T> (array: T[]): T[] {
|
||||
function shuffle<T>(array: T[]): T[] {
|
||||
let currentIndex = array.length
|
||||
while (currentIndex !== 0) {
|
||||
const randomIndex = Math.floor(Math.random() * currentIndex)
|
||||
@@ -80,7 +80,7 @@ function shuffle<T> (array: T[]): T[] {
|
||||
return array
|
||||
}
|
||||
|
||||
function quit () {
|
||||
function quit() {
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'quit' }
|
||||
@@ -95,7 +95,7 @@ export default defineComponent({
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const wm = inject<WidgetManager>('wm')!
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -143,7 +143,7 @@ export default defineComponent({
|
||||
menuByType(configWidget.value?.wmType)
|
||||
.map(group => group.map(component => ({
|
||||
name: t(component.name!),
|
||||
select () { selectedComponent.value = component },
|
||||
select() { selectedComponent.value = component },
|
||||
isSelected: (selectedComponent.value === component),
|
||||
type: 'menu-item' as const
|
||||
}))),
|
||||
@@ -152,14 +152,14 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
t,
|
||||
save () {
|
||||
save() {
|
||||
updateConfig(configClone.value!)
|
||||
saveConfig()
|
||||
pushHostConfig()
|
||||
|
||||
wm.hide(props.config.wmId)
|
||||
},
|
||||
cancel () {
|
||||
cancel() {
|
||||
wm.hide(props.config.wmId)
|
||||
},
|
||||
quit,
|
||||
@@ -178,13 +178,13 @@ export default defineComponent({
|
||||
})
|
||||
}),
|
||||
podiumVisible,
|
||||
showPodium () { podiumVisible.value = true },
|
||||
hidePodium () { podiumVisible.value = false }
|
||||
showPodium() { podiumVisible.value = true },
|
||||
hidePodium() { podiumVisible.value = false }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function menuByType (type?: string) {
|
||||
function menuByType(type?: string) {
|
||||
switch (type) {
|
||||
case 'stash-search':
|
||||
return [[SettingsStashSearch]]
|
||||
@@ -206,7 +206,7 @@ function menuByType (type?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
function flatJoin<T, J>(arr: T[][], joinEl: () => J) {
|
||||
const out: Array<T | J> = []
|
||||
for (const nested of arr) {
|
||||
out.push(...nested)
|
||||
|
||||
@@ -1,124 +1,152 @@
|
||||
<template>
|
||||
<div class="p-2 flex flex-col h-full items-center">
|
||||
<div class="flex flex-col items-center p-2 mb-4">
|
||||
<img class="w-12 h-12" src="/images/TransferOrb.png">
|
||||
<p class="text-base">Exiled Exchange 2</p>
|
||||
<p class="">{{ t('app.version', [version]) }}</p>
|
||||
<div class="flex gap-2">
|
||||
<a class="border-b" href="https://github.com/Kvan7/exiled-exchange-2/releases" target="_blank">{{
|
||||
t('app.release_notes') }}</a>
|
||||
<a class="border-b" href="https://github.com/Kvan7/exiled-exchange-2/issues" target="_blank">{{
|
||||
t('app.report_bug') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border border-gray-600 rounded p-2 whitespace-nowrap min-w-min w-72">
|
||||
<p>{{ info.str1 }}</p>
|
||||
<p>{{ info.str2 }}</p>
|
||||
<button v-if="info.action" @click="info.action" class="btn w-full mt-1">{{ info.actionText }}</button>
|
||||
</div>
|
||||
<div class="text-center mt-auto py-8">
|
||||
<p>{{ t('app.contact_me') }} <br><span
|
||||
class="font-sans text-gray-500 select-all"><@295216259795124225></span></p>
|
||||
<ul class="flex gap-4">
|
||||
<li><img class="rounded inline" src="/images/dc_tft.gif"> <a class="border-b" href="https://discord.gg/tftrove"
|
||||
target="_blank">The Forbidden Trove</a></li>
|
||||
<li><img class="rounded inline" src="/images/dc_reddit.png"> <a class="border-b"
|
||||
href="https://discord.gg/pathofexile" target="_blank">r/pathofexile</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-2 flex flex-col h-full items-center">
|
||||
<div class="flex flex-col items-center p-2 mb-4">
|
||||
<img class="w-12 h-12" src="/images/exa.png" />
|
||||
<p class="text-base">Exalted PoE2 Trade</p>
|
||||
<p class="">{{ t('app.version', [version]) }}</p>
|
||||
<div class="flex gap-2">
|
||||
<a
|
||||
class="border-b"
|
||||
href="https://github.com/Kvan7/exalted-poe2-trade/releases"
|
||||
target="_blank"
|
||||
>{{ t('app.release_notes') }}</a
|
||||
>
|
||||
<a
|
||||
class="border-b"
|
||||
href="https://github.com/Kvan7/exalted-poe2-trade/issues"
|
||||
target="_blank"
|
||||
>{{ t('app.report_bug') }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="border border-gray-600 rounded p-2 whitespace-nowrap min-w-min w-72"
|
||||
>
|
||||
<p>{{ info.str1 }}</p>
|
||||
<p>{{ info.str2 }}</p>
|
||||
<button v-if="info.action" @click="info.action" class="btn w-full mt-1">
|
||||
{{ info.actionText }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-center mt-auto py-8">
|
||||
<p>
|
||||
{{ t('app.contact_me') }} <br /><span
|
||||
class="font-sans text-gray-500 select-all"
|
||||
><@295216259795124225></span
|
||||
>
|
||||
</p>
|
||||
<ul class="flex gap-4">
|
||||
<li>
|
||||
<img class="rounded inline" src="/images/dc_tft.gif" />
|
||||
<a class="border-b" href="https://discord.gg/tftrove" target="_blank"
|
||||
>The Forbidden Trove</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<img class="rounded inline" src="/images/dc_reddit.png" />
|
||||
<a
|
||||
class="border-b"
|
||||
href="https://discord.gg/pathofexile"
|
||||
target="_blank"
|
||||
>r/pathofexile</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { DateTime } from 'luxon'
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Host } from '@/web/background/IPC';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
function checkForUpdates () {
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'check-for-update' }
|
||||
})
|
||||
function checkForUpdates() {
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'check-for-update' },
|
||||
});
|
||||
}
|
||||
|
||||
function openDownloadPage () {
|
||||
window.open('https://github.com/Kvan7/exiled-exchange-2/releases')
|
||||
function openDownloadPage() {
|
||||
// don't have this setup yet
|
||||
window.open('https://kvan7.github.io/exalted-poe2-trade/download');
|
||||
}
|
||||
|
||||
function quitAndInstall () {
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'update-and-restart' }
|
||||
})
|
||||
function quitAndInstall() {
|
||||
Host.sendEvent({
|
||||
name: 'CLIENT->MAIN::user-action',
|
||||
payload: { action: 'update-and-restart' },
|
||||
});
|
||||
}
|
||||
|
||||
function fmtTime (millis: number) {
|
||||
return DateTime.fromMillis(millis).toRelative({ style: 'long' }) ?? 'n/a'
|
||||
function fmtTime(millis: number) {
|
||||
return DateTime.fromMillis(millis).toRelative({ style: 'long' }) ?? 'n/a';
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'settings.about',
|
||||
inheritAttrs: false,
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
name: 'settings.about',
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
const { t } = useI18n();
|
||||
|
||||
const info = computed(() => {
|
||||
const rawInfo = Host.updateInfo.value
|
||||
switch (rawInfo.state) {
|
||||
case 'initial':
|
||||
return {
|
||||
str1: t('updates.maybe_outdated'),
|
||||
str2: t('updates.never_checked'),
|
||||
action: checkForUpdates,
|
||||
actionText: t('updates.check_now')
|
||||
}
|
||||
case 'checking-for-update':
|
||||
return { str1: t('updates.checking'), str2: t('please_wait') }
|
||||
case 'update-not-available':
|
||||
return {
|
||||
str1: t('updates.latest'),
|
||||
str2: t('updates.last_checked', [fmtTime(rawInfo.checkedAt)]),
|
||||
action: checkForUpdates,
|
||||
actionText: t('updates.check_now')
|
||||
}
|
||||
case 'error':
|
||||
return {
|
||||
str1: t('updates.maybe_outdated'),
|
||||
str2: t('updates.error'),
|
||||
action: openDownloadPage,
|
||||
actionText: t('updates.downloads_page')
|
||||
}
|
||||
case 'update-downloaded':
|
||||
return {
|
||||
str1: t('updates.available', [rawInfo.version]),
|
||||
str2: t('updates.installed_on_exit'),
|
||||
action: quitAndInstall,
|
||||
actionText: t('updates.install_now')
|
||||
}
|
||||
case 'update-available':
|
||||
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')
|
||||
}
|
||||
}
|
||||
})
|
||||
const info = computed(() => {
|
||||
const rawInfo = Host.updateInfo.value;
|
||||
switch (rawInfo.state) {
|
||||
case 'initial':
|
||||
return {
|
||||
str1: t('updates.maybe_outdated'),
|
||||
str2: t('updates.never_checked'),
|
||||
action: checkForUpdates,
|
||||
actionText: t('updates.check_now'),
|
||||
};
|
||||
case 'checking-for-update':
|
||||
return { str1: t('updates.checking'), str2: t('please_wait') };
|
||||
case 'update-not-available':
|
||||
return {
|
||||
str1: t('updates.latest'),
|
||||
str2: t('updates.last_checked', [fmtTime(rawInfo.checkedAt)]),
|
||||
action: checkForUpdates,
|
||||
actionText: t('updates.check_now'),
|
||||
};
|
||||
case 'error':
|
||||
return {
|
||||
str1: t('updates.maybe_outdated'),
|
||||
str2: t('updates.error'),
|
||||
action: openDownloadPage,
|
||||
actionText: t('updates.downloads_page'),
|
||||
};
|
||||
case 'update-downloaded':
|
||||
return {
|
||||
str1: t('updates.available', [rawInfo.version]),
|
||||
str2: t('updates.installed_on_exit'),
|
||||
action: quitAndInstall,
|
||||
actionText: t('updates.install_now'),
|
||||
};
|
||||
case 'update-available':
|
||||
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'),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
t,
|
||||
info,
|
||||
version: Host.version
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
t,
|
||||
info,
|
||||
version: Host.version,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="mb-4">
|
||||
<div class="flex-1 mb-1">{{ t(':poe_cfg_file') }}</div>
|
||||
<input v-model.trim="gameConfig"
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/My Games/Path of Exile 2/poe2_production_Config.ini">
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/My Games/Path of Exile 2/production_Config.ini">
|
||||
</div>
|
||||
<hr class="mb-4 mx-8 border-gray-700">
|
||||
<div class="mb-2">
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user