Compare commits
35 Commits
fix/dumpTa
...
v0.0.11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b8e2ae8cf | ||
|
|
3450f39381 | ||
|
|
854887056e | ||
|
|
292e2de2dd | ||
|
|
3cda3a74b8 | ||
|
|
32215e5c03 | ||
|
|
fd4917ca7f | ||
|
|
c3d6c69d9d | ||
|
|
0774814912 | ||
|
|
20b695b1b3 | ||
|
|
e0a3b34c22 | ||
|
|
ed2a93a82f | ||
|
|
14ecde0c71 | ||
|
|
dd91dea7b1 | ||
|
|
a6ace8e234 | ||
|
|
68ee7016d1 | ||
|
|
46e70400b6 | ||
|
|
4a37528500 | ||
|
|
b36954d0a9 | ||
|
|
932ebd3b44 | ||
|
|
e18ddda81a | ||
|
|
ef592e0cf4 | ||
|
|
de5444fa04 | ||
|
|
2cf0cfa347 | ||
|
|
2f694ff0f7 | ||
|
|
3eef0397fa | ||
|
|
e8c08a18a7 | ||
|
|
5f61646cdf | ||
|
|
0f5bb3eaf1 | ||
|
|
a44e14398d | ||
|
|
592d99f645 | ||
|
|
bc4579b9f0 | ||
|
|
789e10eac4 | ||
|
|
066364e962 | ||
|
|
17955b452e |
24
.github/ISSUE_TEMPLATE/something-broken-in-poe2.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Something Broken in PoE2
|
||||
about: Use this for things that worked in PoE 1 and do not in PoE 2
|
||||
title: "[PoE2]"
|
||||
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
@@ -34,7 +34,7 @@ jobs:
|
||||
needs: renderer
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2019, ubuntu-20.04, macos-12]
|
||||
os: [windows-2019] # ubuntu-20.04, macos-14 is a missing runner
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -43,11 +43,11 @@ jobs:
|
||||
with:
|
||||
name: renderer-dist
|
||||
path: ./renderer/dist
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: npm ci
|
||||
working-directory: ./main
|
||||
- run: yarn build
|
||||
- run: npm run build
|
||||
working-directory: ./main
|
||||
- run: yarn package -p onTagOrDraft
|
||||
- run: npm run package "--" -p onTagOrDraft
|
||||
working-directory: ./main
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -59,4 +59,4 @@ jobs:
|
||||
run: cat ./main/dist/latest-linux.yml
|
||||
- name: Hash
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
run: cat ./main/dist/latest-mac.yml
|
||||
run: cat ./main/dist/latest-mac.yml
|
||||
@@ -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/SnosMe/awakened-poe-trade/blob/master/.github/workflows/main.yml)
|
||||
[.github/workflows/main.yml](https://github.com/Kvan7/exiled-exchange-2/blob/master/.github/workflows/main.yml)
|
||||
|
||||
Here's what that looks like as of 2023-12-03.
|
||||
|
||||
```shell
|
||||
cd renderer
|
||||
yarn install
|
||||
yarn make-index-files
|
||||
yarn dev
|
||||
npm install
|
||||
npm run make-index-files
|
||||
npm run dev
|
||||
|
||||
# In a second shell
|
||||
cd main
|
||||
yarn install
|
||||
yarn dev
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
# How to build
|
||||
|
||||
```shell
|
||||
cd renderer
|
||||
yarn install
|
||||
yarn make-index-files
|
||||
yarn build
|
||||
npm install
|
||||
npm run make-index-files
|
||||
npm run build
|
||||
|
||||
cd ../main
|
||||
yarn build
|
||||
npm run 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
|
||||
|
||||
26
README.md
@@ -1,16 +1,27 @@
|
||||
#  Awakened PoE Trade
|
||||
#  Exiled Exchange 2
|
||||
|
||||
[](https://somsubhra.github.io/github-release-stats/?username=SnosMe&repository=awakened-poe-trade)
|
||||
[](https://patreon.com/awakened_poe_trade)
|
||||
[](https://github.com/SnosMe/awakened-poe-trade/issues/22)
|
||||
## Moving from POE1/Awakened PoE Trade
|
||||
|
||||
1. Download latest release from [releases](https://github.com/Kvan7/exiled-exchange-2/releases)
|
||||
- Currently only Windows is supported
|
||||
- Only available as pre-release right now
|
||||
2. Run installer
|
||||
3. Run Exiled Exchange 2
|
||||
4. Launch PoE2 to generate correct files
|
||||
5. Copy `apt-data` from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exiled-exchange-2` to copy your previous settings
|
||||
- Resulting directory structure should look like this:
|
||||
- `%APPDATA%\exiled-exchange-2\apt-data\`
|
||||
- `config.json`
|
||||
6. Restart Exiled Exchange 2
|
||||
|
||||
➡ [Download for Windows & Linux](https://snosme.github.io/awakened-poe-trade/download) ⬅
|
||||
#### Updating from 0.0.1 -> 0.0.10
|
||||
|
||||
Follow same steps as tranfering from PoE1, instead of copying from `%APPDATA%\awakened-poe-trade` to `%APPDATA%\exiled-exchange-2` instead copy from `%APPDATA%\awakened-poe2-trade` or `%APPDATA%\awakened-poe2-trade2` to `%APPDATA%\exiled-exchange-2`. After copying files feel free to run the uninstaller for `awakened-poe2-trade` to remove old executables, or delete `%APPDATA%\awakened-poe2-trade` and `Local\Programs\awakened-poe2-trade`
|
||||
|
||||
## Tool showcase
|
||||
|
||||
| Gem | Rare | Unique | Currency |
|
||||
|-----|------|--------|----------|
|
||||
| Gem | Rare | Unique | Currency |
|
||||
| ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ |
|
||||
|  |  |  |  |
|
||||
|
||||
### Development
|
||||
@@ -19,6 +30,7 @@ See [DEVELOPING.md](./DEVELOPING.md)
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
- [awakened-poe-trade](https://github.com/SnosMe/awakened-poe-trade)
|
||||
- [libuiohook](https://github.com/kwhat/libuiohook)
|
||||
- [RePoE](https://github.com/brather1ng/RePoE)
|
||||
- [poeprices.info](https://www.poeprices.info/)
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "renderer",
|
||||
"path": "renderer"
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"path": "main"
|
||||
},
|
||||
{
|
||||
"name": "docs",
|
||||
"path": "docs"
|
||||
},
|
||||
{
|
||||
"name": "root",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.exclude": {
|
||||
"main/": true,
|
||||
"renderer/": true,
|
||||
"docs/": true,
|
||||
"dist/": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
const BASE = '/awakened-poe-trade/'
|
||||
const BASE = '/exiled-exchange-2/'
|
||||
|
||||
export default defineConfig({
|
||||
title: 'Awakened PoE Trade',
|
||||
description: 'App for price-checking items in Path of Exile',
|
||||
title: 'Exiled Exchange 2',
|
||||
description: 'App for price-checking items in Path of Exile 2',
|
||||
base: BASE,
|
||||
mpa: true,
|
||||
head: [
|
||||
@@ -22,14 +22,9 @@ export default defineConfig({
|
||||
// logo: 'TODO', https://github.com/vuejs/vitepress/issues/1401
|
||||
appVersion: '3.25.101',
|
||||
github: {
|
||||
releasesUrl: 'https://github.com/SnosMe/awakened-poe-trade/releases'
|
||||
releasesUrl: 'https://github.com/Kvan7/exiled-exchange-2/releases'
|
||||
},
|
||||
socialLinks: [
|
||||
{
|
||||
text: 'Discord',
|
||||
color: '#7289DA',
|
||||
link: 'https://github.com/SnosMe/awakened-poe-trade/issues/22'
|
||||
},
|
||||
{
|
||||
text: 'Patreon',
|
||||
color: '#FF424D',
|
||||
@@ -38,7 +33,7 @@ export default defineConfig({
|
||||
{
|
||||
text: 'GitHub',
|
||||
color: '#181717',
|
||||
link: 'https://github.com/SnosMe/awakened-poe-trade'
|
||||
link: 'https://github.com/Kvan7/exiled-exchange-2'
|
||||
}
|
||||
],
|
||||
sidebar: [
|
||||
|
||||
@@ -8,15 +8,15 @@ import { useData } from 'vitepress'
|
||||
const { theme } = useData()
|
||||
</script>
|
||||
|
||||
You can download Awakened Poe Trade here. Any other mirrors are not known
|
||||
You can download Exalted Poe Trade here. Any other mirrors are not known
|
||||
to the developer, downloading from them may be unsafe.
|
||||
|
||||
| Download link | Automatic updates | Startup time |
|
||||
|---------------|-------------------|--------------|
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-Setup-${theme.appVersion}.exe`">Windows 10+ (installer)</a> | ✔ | Fast |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}.exe`">Windows 10+ (portable)</a> | ❌ | Slower |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}.AppImage`">Linux (AppImage)</a> | ✔ | n/a |
|
||||
| <a :href="`${theme.github.releasesUrl}/download/v${theme.appVersion}/Awakened-PoE-Trade-${theme.appVersion}-universal.dmg`">macOS (dmg)</a> | ❌ | n/a |
|
||||
| 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 |
|
||||
|
||||
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 Awakened PoE Trade to have access to the PoE window, it must be started with Administrator rights.**
|
||||
In order for Exiled Exchange 2 to have access to the PoE window, it must be started with Administrator rights.**
|
||||
|
||||
❌ **Not compatible with "GeForce Now" or any other cloud gaming service that do not forward clipboard data.**
|
||||
|
||||
@@ -4,7 +4,7 @@ title: FAQ
|
||||
|
||||
- **Where can I change settings, league?**
|
||||
|
||||
Open Path of Exile and press overlay key `Shift + Space`. Click on the button with cog icon there.
|
||||
Open Path of Exile 2 and press overlay key `Shift + Space`. Click on the button with cog icon there.
|
||||

|
||||
|
||||
- **Where can I find the logs?**
|
||||
|
||||
@@ -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%\awakened-poe-trade`
|
||||
4. Delete `%appdata%\exiled-exchange-2`
|
||||
|
||||
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 Awakened PoE Trade.
|
||||
6. Restart Exiled Exchange 2.
|
||||
|
||||
*(don't forget to quit first, otherwise launching second instance will do nothing).*
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ title: Quick Start
|
||||
|
||||
#### First of all, how does it work? {:style="margin-top: 0;"}
|
||||
|
||||
When you press `Ctrl + C` Path of Exile copies the item's text (under cursor, if any) to the clipboard.
|
||||
All that remains is to parse text in Awakened PoE Trade and show to you in a fancy way.
|
||||
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.
|
||||
|
||||
### Usage
|
||||
|
||||
|
||||
33
exiled-exchange-2.code-workspace
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "renderer",
|
||||
"path": "renderer"
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"path": "main"
|
||||
},
|
||||
{
|
||||
"name": "docs",
|
||||
"path": "docs"
|
||||
},
|
||||
{
|
||||
"name": "root",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.exclude": {
|
||||
"main/": true,
|
||||
"renderer/": true,
|
||||
"docs/": true,
|
||||
"dist/": true
|
||||
},
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"conventionalCommits.scopes": [
|
||||
"Update to 2"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 841 KiB After Width: | Height: | Size: 712 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 66 KiB |
BIN
main/build/icons/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
@@ -1,6 +1,6 @@
|
||||
publish:
|
||||
- "github"
|
||||
productName: "Awakened PoE Trade"
|
||||
productName: "Exiled Exchange 2"
|
||||
npmRebuild: false
|
||||
files:
|
||||
- "package.json"
|
||||
|
||||
5656
main/package-lock.json
generated
Normal file
@@ -1,18 +1,20 @@
|
||||
{
|
||||
"name": "awakened-poe-trade",
|
||||
"version": "3.25.102",
|
||||
"name": "exiled-exchange-2",
|
||||
"version": "0.0.11",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node build/script.mjs",
|
||||
"build": "tsc --noEmit && node build/script.mjs --prod",
|
||||
"package": "electron-builder build"
|
||||
"package": "electron-builder build",
|
||||
"lint": "eslint src",
|
||||
"fix": "eslint src --fix"
|
||||
},
|
||||
"author": {
|
||||
"name": "Alexander Drozdov"
|
||||
"name": "Garrett Parker"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SnosMe/awakened-poe-trade.git"
|
||||
"url": "https://github.com/Kvan7/exiled-exchange-2.git"
|
||||
},
|
||||
"main": "dist/main.js",
|
||||
"dependencies": {
|
||||
@@ -25,15 +27,12 @@
|
||||
"@types/ws": "^8.5.3",
|
||||
"@wokwi/bmp-ts": "^3.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
"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",
|
||||
"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",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,70 @@
|
||||
import path from 'path'
|
||||
import { app, Tray, Menu, shell, nativeImage, dialog } from 'electron'
|
||||
import type { ServerEvents } from './server'
|
||||
import path from "path";
|
||||
import { app, Tray, Menu, shell, nativeImage, dialog } from "electron";
|
||||
import type { ServerEvents } from "./server";
|
||||
|
||||
export class AppTray {
|
||||
public overlayKey = 'Shift + Space'
|
||||
private tray: Tray
|
||||
serverPort = 0
|
||||
public overlayKey = "Shift + Space";
|
||||
private tray: Tray;
|
||||
serverPort = 0;
|
||||
|
||||
constructor (server: ServerEvents) {
|
||||
constructor(server: ServerEvents) {
|
||||
let trayImage = nativeImage.createFromPath(
|
||||
path.join(
|
||||
__dirname,
|
||||
process.env.STATIC!,
|
||||
process.platform === "win32" ? "icon.ico" : "icon.png"
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.platform === "darwin") {
|
||||
// Mac image size needs to be smaller, or else it looks huge. Size
|
||||
// guideline is from https://iconhandbook.co.uk/reference/chart/osx/
|
||||
trayImage = trayImage.resize({ width: 22, height: 22 })
|
||||
trayImage = trayImage.resize({ width: 22, height: 22 });
|
||||
}
|
||||
|
||||
this.tray = new Tray(trayImage)
|
||||
this.tray.setToolTip(`Awakened PoE Trade v${app.getVersion()}`)
|
||||
this.rebuildMenu()
|
||||
this.tray = new Tray(trayImage);
|
||||
this.tray.setToolTip(`Exiled Exchange 2 v${app.getVersion()}`);
|
||||
this.rebuildMenu();
|
||||
|
||||
server.onEventAnyClient('CLIENT->MAIN::user-action', ({ action }) => {
|
||||
if (action === 'quit') {
|
||||
app.quit()
|
||||
server.onEventAnyClient("CLIENT->MAIN::user-action", ({ action }) => {
|
||||
if (action === "quit") {
|
||||
app.quit();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
rebuildMenu () {
|
||||
rebuildMenu() {
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Settings/League',
|
||||
label: "Settings/League",
|
||||
click: () => {
|
||||
dialog.showMessageBox({
|
||||
title: 'Settings',
|
||||
message: `Open Path of Exile and press "${this.overlayKey}". Click on the button with cog icon there.`
|
||||
})
|
||||
}
|
||||
title: "Settings",
|
||||
message: `Open Path of Exile 2 and press "${this.overlayKey}". Click on the button with cog icon there.`,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open in Browser',
|
||||
label: "Open in Browser",
|
||||
click: () => {
|
||||
shell.openExternal(`http://localhost:${this.serverPort}`)
|
||||
}
|
||||
shell.openExternal(`http://localhost:${this.serverPort}`);
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: 'Open config folder',
|
||||
label: "Open config folder",
|
||||
click: () => {
|
||||
shell.openPath(path.join(app.getPath('userData'), 'apt-data'))
|
||||
}
|
||||
shell.openPath(path.join(app.getPath("userData"), "apt-data"));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
label: "Quit",
|
||||
click: () => {
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
])
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
this.tray.setContextMenu(contextMenu)
|
||||
this.tray.setContextMenu(contextMenu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +1,131 @@
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import ini from 'ini'
|
||||
import { app } from 'electron'
|
||||
import { hotkeyToString, CodeToKey } from '../../../ipc/KeyToCode'
|
||||
import { guessFileLocation } from './utils'
|
||||
import type { Logger } from '../RemoteLogger'
|
||||
import type { ServerEvents } from '../server'
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import ini from "ini";
|
||||
import { app } from "electron";
|
||||
import { hotkeyToString, CodeToKey } from "../../../ipc/KeyToCode";
|
||||
import { guessFileLocation } from "./utils";
|
||||
import type { Logger } from "../RemoteLogger";
|
||||
import type { ServerEvents } from "../server";
|
||||
|
||||
const POSSIBLE_PATH =
|
||||
(process.platform === 'win32') ? [
|
||||
path.join(app.getPath('documents'), 'My Games\\Path of Exile\\production_Config.ini')
|
||||
] : (process.platform === 'linux') ? [
|
||||
path.join(app.getPath('documents'), 'My Games/Path of Exile/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/production_Config.ini')
|
||||
] : (process.platform === 'darwin') ? [
|
||||
path.join(app.getPath('appData'), 'Path of Exile/Preferences/production_Config.ini')
|
||||
] : []
|
||||
process.platform === "win32"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("documents"),
|
||||
"My Games\\Path of Exile 2\\poe2_production_Config.ini"
|
||||
),
|
||||
]
|
||||
: process.platform === "linux"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("documents"),
|
||||
"My Games/Path of Exile 2/poe2_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"
|
||||
),
|
||||
]
|
||||
: process.platform === "darwin"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("appData"),
|
||||
"Path of Exile 2/Preferences/poe2_production_Config.ini"
|
||||
),
|
||||
]
|
||||
: [];
|
||||
|
||||
export class GameConfig {
|
||||
private _wantedPath: string | null = null
|
||||
private _actualPath: string | null = null
|
||||
get actualPath () { return this._actualPath }
|
||||
private _wantedPath: string | null = null;
|
||||
private _actualPath: string | null = null;
|
||||
get actualPath() {
|
||||
return this._actualPath;
|
||||
}
|
||||
|
||||
private _showModsKey: string | null = null
|
||||
get showModsKeyNullable () { return this._showModsKey }
|
||||
get showModsKey () { return this._showModsKey ?? 'Alt' }
|
||||
private _showModsKey: string | null = null;
|
||||
get showModsKeyNullable() {
|
||||
return this._showModsKey;
|
||||
}
|
||||
get showModsKey() {
|
||||
return this._showModsKey ?? "Alt";
|
||||
}
|
||||
|
||||
constructor (
|
||||
private server: ServerEvents,
|
||||
private logger: Logger
|
||||
) {}
|
||||
constructor(private server: ServerEvents, private logger: Logger) {}
|
||||
|
||||
async readConfig (filePath: string) {
|
||||
async readConfig(filePath: string) {
|
||||
if (this._wantedPath !== filePath) {
|
||||
this._wantedPath = filePath
|
||||
this._actualPath = null
|
||||
this._wantedPath = filePath;
|
||||
this._actualPath = null;
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filePath.length) {
|
||||
const guessedPath = await guessFileLocation(POSSIBLE_PATH)
|
||||
const guessedPath = await guessFileLocation(POSSIBLE_PATH);
|
||||
if (guessedPath != null) {
|
||||
filePath = guessedPath
|
||||
filePath = guessedPath;
|
||||
} else {
|
||||
this.logger.write('error [GameConfig] Failed to find game configuration file in the default location.')
|
||||
return
|
||||
this.logger.write(
|
||||
"error [GameConfig] Failed to find game configuration file in the default location."
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let contents = await fs.readFile(filePath, { encoding: 'utf-8', flag: 'r' })
|
||||
contents = contents.trimStart() // remove BOM
|
||||
const parsed = ini.parse(contents)
|
||||
let contents = await fs.readFile(filePath, {
|
||||
encoding: "utf-8",
|
||||
flag: "r",
|
||||
});
|
||||
contents = contents.trimStart(); // remove BOM
|
||||
const parsed = ini.parse(contents);
|
||||
|
||||
this._showModsKey = this.parseConfigHotkey(
|
||||
parsed['ACTION_KEYS']?.['show_advanced_item_descriptions'])
|
||||
parsed["ACTION_KEYS"]?.["show_advanced_item_descriptions"]
|
||||
);
|
||||
|
||||
this._actualPath = filePath
|
||||
this._actualPath = filePath;
|
||||
} catch {
|
||||
this.logger.write('error [GameConfig] Failed to read game configuration file.')
|
||||
this.logger.write(
|
||||
"error [GameConfig] Failed to read game configuration file."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private parseConfigHotkey (cfgKey?: string): string | null {
|
||||
if (!cfgKey) return null
|
||||
private parseConfigHotkey(cfgKey?: string): string | null {
|
||||
if (!cfgKey) return null;
|
||||
|
||||
const [keyMain, keyMod] = cfgKey.split(' ')
|
||||
const [keyMain, keyMod] = cfgKey.split(" ");
|
||||
|
||||
let key1: string
|
||||
let key1: string;
|
||||
if (CodeToKey[keyMain]) {
|
||||
key1 = CodeToKey[keyMain]
|
||||
key1 = CodeToKey[keyMain];
|
||||
} else {
|
||||
this.logger.write(`error [GameConfig] Failed to read key: ${cfgKey}.`)
|
||||
return null
|
||||
this.logger.write(`error [GameConfig] Failed to read key: ${cfgKey}.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
let key2: string | undefined
|
||||
let key2: string | undefined;
|
||||
if (keyMod) {
|
||||
if (keyMod === '1') {
|
||||
key2 = 'Shift'
|
||||
} else if (keyMod === '2') {
|
||||
key2 = 'Ctrl'
|
||||
} else if (keyMod === '3') {
|
||||
key2 = 'Alt'
|
||||
if (keyMod === "1") {
|
||||
key2 = "Shift";
|
||||
} else if (keyMod === "2") {
|
||||
key2 = "Ctrl";
|
||||
} else if (keyMod === "3") {
|
||||
key2 = "Alt";
|
||||
} else {
|
||||
this.logger.write(`error [GameConfig] Failed to read modifier key: ${cfgKey}.`)
|
||||
return null
|
||||
this.logger.write(
|
||||
`error [GameConfig] Failed to read modifier key: ${cfgKey}.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return hotkeyToString(
|
||||
[key1],
|
||||
key2 === 'Ctrl',
|
||||
key2 === 'Shift',
|
||||
key2 === 'Alt'
|
||||
)
|
||||
key2 === "Ctrl",
|
||||
key2 === "Shift",
|
||||
key2 === "Alt"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +1,118 @@
|
||||
import { promises as fs, watchFile, unwatchFile } from 'fs'
|
||||
import path from 'path'
|
||||
import { app } from 'electron'
|
||||
import { guessFileLocation } from './utils'
|
||||
import { ServerEvents } from '../server'
|
||||
import { Logger } from '../RemoteLogger'
|
||||
import { promises as fs, watchFile, unwatchFile } from "fs";
|
||||
import path from "path";
|
||||
import { app } from "electron";
|
||||
import { guessFileLocation } from "./utils";
|
||||
import { ServerEvents } from "../server";
|
||||
import { Logger } from "../RemoteLogger";
|
||||
|
||||
const POSSIBLE_PATH =
|
||||
(process.platform === 'win32') ? [
|
||||
'C:\\Program Files (x86)\\Grinding Gear Games\\Path of Exile\\logs\\Client.txt',
|
||||
'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Path of Exile\\logs\\Client.txt'
|
||||
] : (process.platform === 'linux') ? [
|
||||
path.join(app.getPath('home'), '.wine/drive_c/Program Files (x86)/Grinding Gear Games/Path of Exile/logs/Client.txt'),
|
||||
path.join(app.getPath('home'), '.local/share/Steam/steamapps/common/Path of Exile/logs/Client.txt')
|
||||
] : (process.platform === 'darwin') ? [
|
||||
path.join(app.getPath('home'), 'Library/Caches/com.GGG.PathOfExile/Logs/Client.txt')
|
||||
] : []
|
||||
process.platform === "win32"
|
||||
? [
|
||||
"C:\\Program Files (x86)\\Grinding Gear Games\\Path of Exile 2\\logs\\Client.txt",
|
||||
"C:\\Program Files (x86)\\Steam\\steamapps\\common\\Path of Exile 2\\logs\\Client.txt",
|
||||
]
|
||||
: process.platform === "linux"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("home"),
|
||||
".wine/drive_c/Program Files (x86)/Grinding Gear Games/Path of Exile 2/logs/Client.txt"
|
||||
),
|
||||
path.join(
|
||||
app.getPath("home"),
|
||||
".local/share/Steam/steamapps/common/Path of Exile 2/logs/Client.txt"
|
||||
),
|
||||
]
|
||||
: process.platform === "darwin"
|
||||
? [
|
||||
path.join(
|
||||
app.getPath("home"),
|
||||
"Library/Caches/com.GGG.PathOfExile/Logs/Client.txt"
|
||||
),
|
||||
]
|
||||
: [];
|
||||
|
||||
export class GameLogWatcher {
|
||||
private _wantedPath: string | null = null
|
||||
get actualPath () { return this._state?.path ?? null }
|
||||
private _wantedPath: string | null = null;
|
||||
get actualPath() {
|
||||
return this._state?.path ?? null;
|
||||
}
|
||||
private _state: {
|
||||
offset: number
|
||||
path: string
|
||||
file: fs.FileHandle
|
||||
isReading: boolean
|
||||
readBuff: Buffer
|
||||
} | null = null
|
||||
offset: number;
|
||||
path: string;
|
||||
file: fs.FileHandle;
|
||||
isReading: boolean;
|
||||
readBuff: Buffer;
|
||||
} | null = null;
|
||||
|
||||
constructor (
|
||||
private server: ServerEvents,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
constructor(private server: ServerEvents, private logger: Logger) {}
|
||||
|
||||
async restart (logFile: string) {
|
||||
async restart(logFile: string) {
|
||||
if (this._wantedPath !== logFile) {
|
||||
this._wantedPath = logFile
|
||||
this._wantedPath = logFile;
|
||||
if (this._state) {
|
||||
unwatchFile(this._state.path)
|
||||
await this._state.file.close()
|
||||
this._state = null
|
||||
unwatchFile(this._state.path);
|
||||
await this._state.file.close();
|
||||
this._state = null;
|
||||
}
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (!logFile.length) {
|
||||
const guessedPath = await guessFileLocation(POSSIBLE_PATH)
|
||||
const guessedPath = await guessFileLocation(POSSIBLE_PATH);
|
||||
if (guessedPath != null) {
|
||||
logFile = guessedPath
|
||||
logFile = guessedPath;
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const file = await fs.open(logFile, 'r')
|
||||
const stats = await file.stat()
|
||||
watchFile(logFile, { interval: 450 }, this.handleFileChange.bind(this))
|
||||
const file = await fs.open(logFile, "r");
|
||||
const stats = await file.stat();
|
||||
watchFile(logFile, { interval: 450 }, this.handleFileChange.bind(this));
|
||||
this._state = {
|
||||
path: logFile,
|
||||
file: file,
|
||||
offset: stats.size,
|
||||
isReading: false,
|
||||
readBuff: Buffer.allocUnsafe(64 * 1024),
|
||||
}
|
||||
};
|
||||
} catch {
|
||||
this.logger.write('error [GameLogWatcher] Failed to watch file.')
|
||||
this.logger.write("error [GameLogWatcher] Failed to watch file.");
|
||||
}
|
||||
}
|
||||
|
||||
private handleFileChange () {
|
||||
private handleFileChange() {
|
||||
if (this._state && !this._state.isReading) {
|
||||
this._state.isReading = true
|
||||
this.readToEOF()
|
||||
this._state.isReading = true;
|
||||
this.readToEOF();
|
||||
}
|
||||
}
|
||||
|
||||
private async readToEOF () {
|
||||
if (!this._state) return
|
||||
private async readToEOF() {
|
||||
if (!this._state) return;
|
||||
|
||||
const { file, readBuff, offset } = this._state
|
||||
const { bytesRead } = await file.read(readBuff, 0, readBuff.length, offset)
|
||||
const { file, readBuff, offset } = this._state;
|
||||
const { bytesRead } = await file.read(readBuff, 0, readBuff.length, offset);
|
||||
|
||||
if (bytesRead) {
|
||||
const str = readBuff.toString('utf8', 0, bytesRead)
|
||||
const lines = str.split('\n').map(line => line.trim()).filter(line => line.length)
|
||||
this.server.sendEventTo('broadcast', {
|
||||
name: 'MAIN->CLIENT::game-log',
|
||||
payload: { lines }
|
||||
})
|
||||
const str = readBuff.toString("utf8", 0, bytesRead);
|
||||
const lines = str
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line.length);
|
||||
this.server.sendEventTo("broadcast", {
|
||||
name: "MAIN->CLIENT::game-log",
|
||||
payload: { lines },
|
||||
});
|
||||
}
|
||||
|
||||
if (bytesRead) {
|
||||
this._state.offset += bytesRead
|
||||
this.readToEOF()
|
||||
this._state.offset += bytesRead;
|
||||
this.readToEOF();
|
||||
} else {
|
||||
this._state.isReading = false
|
||||
this._state.isReading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +40,6 @@ 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)
|
||||
@@ -49,6 +48,7 @@ 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 })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,172 +1,188 @@
|
||||
import path from 'path'
|
||||
import { BrowserWindow, dialog, shell, Menu, WebContents } from 'electron'
|
||||
import { OverlayController, OVERLAY_WINDOW_OPTS } from 'electron-overlay-window'
|
||||
import type { ServerEvents } from '../server'
|
||||
import type { Logger } from '../RemoteLogger'
|
||||
import type { GameWindow } from './GameWindow'
|
||||
import path from "path";
|
||||
import { BrowserWindow, dialog, shell, Menu, WebContents } from "electron";
|
||||
import {
|
||||
OverlayController,
|
||||
OVERLAY_WINDOW_OPTS,
|
||||
} from "electron-overlay-window";
|
||||
import type { ServerEvents } from "../server";
|
||||
import type { Logger } from "../RemoteLogger";
|
||||
import type { GameWindow } from "./GameWindow";
|
||||
|
||||
export class OverlayWindow {
|
||||
public isInteractable = false
|
||||
public wasUsedRecently = true
|
||||
private window?: BrowserWindow
|
||||
private overlayKey: string = 'Shift + Space'
|
||||
private isOverlayKeyUsed = false
|
||||
public isInteractable = false;
|
||||
public wasUsedRecently = true;
|
||||
private window?: BrowserWindow;
|
||||
private overlayKey: string = "Shift + Space";
|
||||
private isOverlayKeyUsed = false;
|
||||
|
||||
constructor (
|
||||
constructor(
|
||||
private server: ServerEvents,
|
||||
private logger: Logger,
|
||||
private poeWindow: GameWindow,
|
||||
private poeWindow: GameWindow
|
||||
) {
|
||||
this.server.onEventAnyClient('OVERLAY->MAIN::focus-game', this.assertGameActive)
|
||||
this.poeWindow.on('active-change', this.handlePoeWindowActiveChange)
|
||||
this.poeWindow.onAttach(this.handleOverlayAttached)
|
||||
this.server.onEventAnyClient(
|
||||
"OVERLAY->MAIN::focus-game",
|
||||
this.assertGameActive
|
||||
);
|
||||
this.poeWindow.on("active-change", this.handlePoeWindowActiveChange);
|
||||
this.poeWindow.onAttach(this.handleOverlayAttached);
|
||||
|
||||
this.server.onEventAnyClient('CLIENT->MAIN::used-recently', (e) => {
|
||||
this.wasUsedRecently = e.isOverlay
|
||||
})
|
||||
this.server.onEventAnyClient("CLIENT->MAIN::used-recently", (e) => {
|
||||
this.wasUsedRecently = e.isOverlay;
|
||||
});
|
||||
|
||||
if (process.argv.includes('--no-overlay')) return
|
||||
if (process.argv.includes("--no-overlay")) return;
|
||||
|
||||
this.window = new BrowserWindow({
|
||||
icon: path.join(__dirname, process.env.STATIC!, 'icon.png'),
|
||||
icon: path.join(__dirname, process.env.STATIC!, "icon.png"),
|
||||
...OVERLAY_WINDOW_OPTS,
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
allowRunningInsecureContent: false,
|
||||
webviewTag: true,
|
||||
spellcheck: false
|
||||
spellcheck: false,
|
||||
},
|
||||
});
|
||||
|
||||
this.window.setMenu(
|
||||
Menu.buildFromTemplate([
|
||||
{ role: "editMenu" },
|
||||
{ role: "reload" },
|
||||
{ role: "toggleDevTools" },
|
||||
])
|
||||
);
|
||||
|
||||
this.window.webContents.on("before-input-event", this.handleExtraCommands);
|
||||
this.window.webContents.on(
|
||||
"did-attach-webview",
|
||||
(_, webviewWebContents) => {
|
||||
webviewWebContents.on("before-input-event", this.handleExtraCommands);
|
||||
}
|
||||
})
|
||||
|
||||
this.window.setMenu(Menu.buildFromTemplate([
|
||||
{ role: 'editMenu' },
|
||||
{ role: 'reload' },
|
||||
{ role: 'toggleDevTools' }
|
||||
]))
|
||||
|
||||
this.window.webContents.on('before-input-event', this.handleExtraCommands)
|
||||
this.window.webContents.on('did-attach-webview', (_, webviewWebContents) => {
|
||||
webviewWebContents.on('before-input-event', this.handleExtraCommands)
|
||||
})
|
||||
);
|
||||
|
||||
this.window.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
shell.openExternal(details.url);
|
||||
return { action: "deny" };
|
||||
});
|
||||
}
|
||||
|
||||
loadAppPage (port: number) {
|
||||
const url = process.env.VITE_DEV_SERVER_URL ||
|
||||
`http://localhost:${port}/index.html`
|
||||
loadAppPage(port: number) {
|
||||
const url =
|
||||
process.env.VITE_DEV_SERVER_URL || `http://localhost:${port}/index.html`;
|
||||
|
||||
if (!this.window) {
|
||||
shell.openExternal(url)
|
||||
return
|
||||
shell.openExternal(url);
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
this.window.loadURL(url)
|
||||
this.window.webContents.openDevTools({ mode: 'detach', activate: false })
|
||||
this.window.loadURL(url);
|
||||
this.window.webContents.openDevTools({ mode: "detach", activate: false });
|
||||
} else {
|
||||
this.window.loadURL(url)
|
||||
this.window.loadURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
assertOverlayActive = () => {
|
||||
if (!this.isInteractable) {
|
||||
this.isInteractable = true
|
||||
OverlayController.activateOverlay()
|
||||
this.poeWindow.isActive = false
|
||||
this.isInteractable = true;
|
||||
OverlayController.activateOverlay();
|
||||
this.poeWindow.isActive = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assertGameActive = () => {
|
||||
if (this.isInteractable) {
|
||||
this.isInteractable = false
|
||||
OverlayController.focusTarget()
|
||||
this.poeWindow.isActive = true
|
||||
this.isInteractable = false;
|
||||
OverlayController.focusTarget();
|
||||
this.poeWindow.isActive = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
toggleActiveState = () => {
|
||||
this.isOverlayKeyUsed = true
|
||||
this.isOverlayKeyUsed = true;
|
||||
if (this.isInteractable) {
|
||||
this.assertGameActive()
|
||||
this.assertGameActive();
|
||||
} else {
|
||||
this.assertOverlayActive()
|
||||
this.assertOverlayActive();
|
||||
}
|
||||
};
|
||||
|
||||
updateOpts(overlayKey: string, windowTitle: string) {
|
||||
this.overlayKey = overlayKey;
|
||||
this.poeWindow.attach(this.window, windowTitle);
|
||||
}
|
||||
|
||||
updateOpts (overlayKey: string, windowTitle: string) {
|
||||
this.overlayKey = overlayKey
|
||||
this.poeWindow.attach(this.window, windowTitle)
|
||||
}
|
||||
private handleExtraCommands = (
|
||||
event: Electron.Event,
|
||||
input: Electron.Input
|
||||
) => {
|
||||
if (input.type !== "keyDown") return;
|
||||
|
||||
private handleExtraCommands = (event: Electron.Event, input: Electron.Input) => {
|
||||
if (input.type !== 'keyDown') return
|
||||
let { code, control: ctrlKey, shift: shiftKey, alt: altKey } = input;
|
||||
|
||||
let { code, control: ctrlKey, shift: shiftKey, alt: altKey } = input
|
||||
|
||||
if (code.startsWith('Key')) {
|
||||
code = code.slice('Key'.length)
|
||||
} else if (code.startsWith('Digit')) {
|
||||
code = code.slice('Digit'.length)
|
||||
if (code.startsWith("Key")) {
|
||||
code = code.slice("Key".length);
|
||||
} else if (code.startsWith("Digit")) {
|
||||
code = code.slice("Digit".length);
|
||||
}
|
||||
|
||||
if (shiftKey && altKey) code = `Shift + Alt + ${code}`
|
||||
else if (ctrlKey && shiftKey) code = `Ctrl + Shift + ${code}`
|
||||
else if (ctrlKey && altKey) code = `Ctrl + Alt + ${code}`
|
||||
else if (altKey) code = `Alt + ${code}`
|
||||
else if (ctrlKey) code = `Ctrl + ${code}`
|
||||
else if (shiftKey) code = `Shift + ${code}`
|
||||
if (shiftKey && altKey) code = `Shift + Alt + ${code}`;
|
||||
else if (ctrlKey && shiftKey) code = `Ctrl + Shift + ${code}`;
|
||||
else if (ctrlKey && altKey) code = `Ctrl + Alt + ${code}`;
|
||||
else if (altKey) code = `Alt + ${code}`;
|
||||
else if (ctrlKey) code = `Ctrl + ${code}`;
|
||||
else if (shiftKey) code = `Shift + ${code}`;
|
||||
|
||||
switch (code) {
|
||||
case 'Escape':
|
||||
case 'Ctrl + W': {
|
||||
event.preventDefault()
|
||||
process.nextTick(this.assertGameActive)
|
||||
break
|
||||
case "Escape":
|
||||
case "Ctrl + W": {
|
||||
event.preventDefault();
|
||||
process.nextTick(this.assertGameActive);
|
||||
break;
|
||||
}
|
||||
case this.overlayKey: {
|
||||
event.preventDefault()
|
||||
process.nextTick(this.toggleActiveState)
|
||||
break
|
||||
event.preventDefault();
|
||||
process.nextTick(this.toggleActiveState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleOverlayAttached = (hasAccess?: boolean) => {
|
||||
if (hasAccess === false) {
|
||||
this.logger.write('error [Overlay] PoE is running with administrator rights')
|
||||
this.logger.write(
|
||||
"error [Overlay] PoE is running with administrator rights"
|
||||
);
|
||||
|
||||
dialog.showErrorBox(
|
||||
'PoE window - No access',
|
||||
"PoE window - No access",
|
||||
// ----------------------
|
||||
'Path of Exile is running with administrator rights.\n' +
|
||||
'\n' +
|
||||
'You need to restart Awakened PoE Trade with administrator rights.'
|
||||
)
|
||||
"Path of Exile 2 is running with administrator rights.\n" +
|
||||
"\n" +
|
||||
"You need to restart Exiled Exchange 2 with administrator rights."
|
||||
);
|
||||
} else {
|
||||
this.server.sendEventTo('broadcast', {
|
||||
name: 'MAIN->OVERLAY::overlay-attached',
|
||||
payload: undefined
|
||||
})
|
||||
this.server.sendEventTo("broadcast", {
|
||||
name: "MAIN->OVERLAY::overlay-attached",
|
||||
payload: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handlePoeWindowActiveChange = (isActive: boolean) => {
|
||||
if (isActive && this.isInteractable) {
|
||||
this.isInteractable = false
|
||||
this.isInteractable = false;
|
||||
}
|
||||
this.server.sendEventTo('broadcast', {
|
||||
name: 'MAIN->OVERLAY::focus-change',
|
||||
this.server.sendEventTo("broadcast", {
|
||||
name: "MAIN->OVERLAY::focus-change",
|
||||
payload: {
|
||||
game: isActive,
|
||||
overlay: this.isInteractable,
|
||||
usingHotkey: this.isOverlayKeyUsed
|
||||
}
|
||||
})
|
||||
this.isOverlayKeyUsed = false
|
||||
}
|
||||
usingHotkey: this.isOverlayKeyUsed,
|
||||
},
|
||||
});
|
||||
this.isOverlayKeyUsed = false;
|
||||
};
|
||||
}
|
||||
|
||||
1956
main/yarn.lock
@@ -4,12 +4,12 @@ module.exports = {
|
||||
node: true
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint'
|
||||
'@typescript-eslint',
|
||||
// 'only-warn'
|
||||
],
|
||||
extends: [
|
||||
'plugin:vue/base',
|
||||
'standard-with-typescript'
|
||||
'standard-with-typescript',
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
@@ -36,7 +36,7 @@ module.exports = {
|
||||
'import/no-duplicates': 'off',
|
||||
'func-call-spacing': 'off',
|
||||
// TODO: refactor IPC and enable
|
||||
'@typescript-eslint/consistent-type-assertions': 'off'
|
||||
'@typescript-eslint/consistent-type-assertions': 'off',
|
||||
},
|
||||
overrides: [{
|
||||
files: ['src/main/**/*'],
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<!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>Awakened PoE Trade</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>Exiled Exchange 2</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
12786
renderer/package-lock.json
generated
@@ -1,62 +1,63 @@
|
||||
{
|
||||
"name": "awakened-poe-trade",
|
||||
"version": "0.0.0",
|
||||
"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",
|
||||
"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": "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"
|
||||
]
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
"league": "League",
|
||||
"realm": "Realm",
|
||||
"realm_intl": "International",
|
||||
|
||||
"app": {
|
||||
"leagues_loading": "Loading leagues\u2026",
|
||||
"leagues_failed": "Failed to load leagues",
|
||||
@@ -21,12 +20,10 @@
|
||||
"report_bug": "Report a bug on GitHub",
|
||||
"quit": "Quit"
|
||||
},
|
||||
|
||||
"map.mods.heist": "heist",
|
||||
"map.mods.uber": "T17",
|
||||
"map.mods.outdated": "outdated",
|
||||
"Support development on": "Support development\u00A0on",
|
||||
|
||||
"Blighted": "Blighted",
|
||||
"Blight-ravaged": "Blight-ravaged",
|
||||
"Magic": "Magic",
|
||||
@@ -40,7 +37,6 @@
|
||||
"Anomalous": "Anomalous",
|
||||
"Divergent": "Divergent",
|
||||
"Phantasmal": "Phantasmal",
|
||||
|
||||
"item": {
|
||||
"prop_quality": "Q {0}%",
|
||||
"base_percentile": "Base Percentile: {0}%",
|
||||
@@ -153,7 +149,6 @@
|
||||
"hide_anointment": "Buyer will likely change anointment",
|
||||
"hide_for_crafting": "Select only if price-checking as base item for crafting",
|
||||
"hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers",
|
||||
|
||||
"tag_implicit": "implicit",
|
||||
"tag_fractured": "fractured",
|
||||
"tag_crafted": "crafted",
|
||||
@@ -254,15 +249,15 @@
|
||||
"new_mods_icon": "Show icon for new mods"
|
||||
},
|
||||
"trade_result": {
|
||||
"error" :"Trade site request failed",
|
||||
"matched" :"Matched: {0}",
|
||||
"trade" :"Trade",
|
||||
"price" :"Price",
|
||||
"bulk" :"bulk",
|
||||
"stock" :"Stock",
|
||||
"fulfill" :"Fulfill",
|
||||
"listed" :"Listed",
|
||||
"seller" :"Seller",
|
||||
"error": "Trade site request failed",
|
||||
"matched": "Matched: {0}",
|
||||
"trade": "Trade",
|
||||
"price": "Price",
|
||||
"bulk": "bulk",
|
||||
"stock": "Stock",
|
||||
"fulfill": "Fulfill",
|
||||
"listed": "Listed",
|
||||
"seller": "Seller",
|
||||
"item_level": "iLvl",
|
||||
"gem_level": "Level",
|
||||
"quality": "Quality",
|
||||
@@ -274,7 +269,7 @@
|
||||
"stack": "Stack"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings - Awakened PoE Trade",
|
||||
"title": "Settings - Exiled Exchange 2",
|
||||
"language": "Language",
|
||||
"private_league": "or Private League",
|
||||
"account_name": "Account name",
|
||||
@@ -283,9 +278,9 @@
|
||||
"chat_cmd_send": "press Enter",
|
||||
"no_key": "Not Set",
|
||||
"clear_hotkey": "You can clear hotkey by pressing Backspace",
|
||||
"overlay" :"Overlay",
|
||||
"stash_scroll" :"Stash tab scrolling",
|
||||
"delve_grid" :"Grid for Delve Chart",
|
||||
"overlay": "Overlay",
|
||||
"stash_scroll": "Stash tab scrolling",
|
||||
"delve_grid": "Grid for Delve Chart",
|
||||
"window_title": "PoE window title",
|
||||
"thank_you": "App development continues thanks to:",
|
||||
"hotkeys": "Hotkeys",
|
||||
@@ -293,20 +288,20 @@
|
||||
"general": "General",
|
||||
"debug": "Debug",
|
||||
"about": "About",
|
||||
"font_size" :"Font size",
|
||||
"overlay_bg" :"Background when the Overlay is clickable",
|
||||
"overlay_bg_none" :"Transparent",
|
||||
"overlay_bg_focus_game" :"Clicking on the background focuses the game",
|
||||
"poe_log_file" :"PoE log file",
|
||||
"poe_cfg_file" :"PoE config file",
|
||||
"restore_clipboard" :"Restore clipboard",
|
||||
"show_overlay_ready" :"Show a notification when the Overlay detects a PoE window",
|
||||
"font_size": "Font size",
|
||||
"overlay_bg": "Background when the Overlay is clickable",
|
||||
"overlay_bg_none": "Transparent",
|
||||
"overlay_bg_focus_game": "Clicking on the background focuses the game",
|
||||
"poe_log_file": "PoE log file",
|
||||
"poe_cfg_file": "PoE config file",
|
||||
"restore_clipboard": "Restore clipboard",
|
||||
"show_overlay_ready": "Show a notification when the Overlay detects a PoE window",
|
||||
"debug_hotkeys": "Record all key presses"
|
||||
},
|
||||
"price_check": {
|
||||
"name": "Price check",
|
||||
"hotkey" :"Auto-hide Mode",
|
||||
"hotkey_locked" :"Open without auto-hide",
|
||||
"hotkey": "Auto-hide Mode",
|
||||
"hotkey_locked": "Open without auto-hide",
|
||||
"enable_browser": "Enable builtin browser",
|
||||
"builtin_browser_warning": "I am aware that future releases can potentially contain malicious code that can steal my POESESSID.",
|
||||
"highlight_hint": "Your items will be highlighted even if this setting is off",
|
||||
@@ -322,4 +317,4 @@
|
||||
"show_prediction": "Show price prediction",
|
||||
"remember_currency": "Remember the Buyout Currency filter"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
"reopen_settings": "{0}을 눌러서 편집을 계속하세요.",
|
||||
"seconds": "초",
|
||||
"league": "리그",
|
||||
|
||||
"app": {
|
||||
"leagues_loading": "리그를 불러오는 중\u2026",
|
||||
"leagues_failed": "리그 불러오기를 실패하였습니다",
|
||||
@@ -19,11 +18,9 @@
|
||||
"report_bug": "버그 발생 시 GitHub에 보고해주세요",
|
||||
"quit": "종료"
|
||||
},
|
||||
|
||||
"map.mods.heist": "강탈",
|
||||
"map.mods.outdated": "사용X",
|
||||
"Support development on": "개발을 지원하세요",
|
||||
|
||||
"Blighted": "역병 걸린",
|
||||
"Blight-ravaged": "역병에 유린당한",
|
||||
"Magic": "매직",
|
||||
@@ -37,7 +34,6 @@
|
||||
"Anomalous": "기묘한",
|
||||
"Divergent": "상이한",
|
||||
"Phantasmal": "몽환적인",
|
||||
|
||||
"item": {
|
||||
"prop_quality": "{0}% 퀄리티",
|
||||
"base_percentile": "기본 백분위수: {0}%",
|
||||
@@ -150,7 +146,6 @@
|
||||
"hide_anointment": "Buyer will likely change anointment",
|
||||
"hide_for_crafting": "Select only if price-checking as base item for crafting",
|
||||
"hide_empty_mod": "Select only if item has 6 modifiers (1 of which is crafted) or if it has 5 modifiers",
|
||||
|
||||
"tag_implicit": "고정 속성",
|
||||
"tag_fractured": "분열된",
|
||||
"tag_crafted": "제작된",
|
||||
@@ -271,7 +266,7 @@
|
||||
"stack": "스택"
|
||||
},
|
||||
"settings": {
|
||||
"title": "세팅 - Awakened PoE Trade",
|
||||
"title": "세팅 - Exiled Exchange 2",
|
||||
"language": "언어",
|
||||
"private_league": "개인리그",
|
||||
"account_name": "계정명",
|
||||
@@ -302,8 +297,8 @@
|
||||
},
|
||||
"price_check": {
|
||||
"name": "Price check",
|
||||
"hotkey" :"Auto-hide Mode",
|
||||
"hotkey_locked" :"Open without auto-hide",
|
||||
"hotkey": "Auto-hide Mode",
|
||||
"hotkey_locked": "Open without auto-hide",
|
||||
"enable_browser": "Enable builtin browser",
|
||||
"builtin_browser_warning": "I am aware that future releases can potentially contain malicious code that can steal my POESESSID.",
|
||||
"highlight_hint": "Your items will be highlighted even if this setting is off",
|
||||
|
||||
@@ -19,14 +19,12 @@
|
||||
"Select": "Выбрать",
|
||||
"Not recognized modifier": "Нераспознанное свойство",
|
||||
"Refresh": "Обновить",
|
||||
|
||||
"please_wait": "Пожалуйста, подождите\u2026",
|
||||
"choose_file": "Выберите файл",
|
||||
"app_is_ready": "Запущен и работает в фоновом режиме",
|
||||
"reopen_settings": "Нажмите {0} чтобы продолжить редактирование.",
|
||||
"seconds": "секунды",
|
||||
"league": "Лига",
|
||||
|
||||
"app": {
|
||||
"leagues_loading": "Загрузка лиг\u2026",
|
||||
"leagues_failed": "Не удалось загрузить лиги",
|
||||
@@ -40,11 +38,9 @@
|
||||
"report_bug": "Сообщить о баге на GitHub",
|
||||
"quit": "Выход"
|
||||
},
|
||||
|
||||
"map.mods.heist": "кража",
|
||||
"map.mods.outdated": "устарело",
|
||||
"Support development on": "Поддержите разработку\u00A0на",
|
||||
|
||||
"Blighted": "Заражённая",
|
||||
"Blight-ravaged": "Разорённая Скверной",
|
||||
"Shaper": "Создатель",
|
||||
@@ -57,7 +53,6 @@
|
||||
"Anomalous": "Аномальный",
|
||||
"Divergent": "Искривлённый",
|
||||
"Phantasmal": "Фантомный",
|
||||
|
||||
"item": {
|
||||
"prop_quality": "К-во: {0}%",
|
||||
"base_percentile": "Ролл значений базы: {0}%",
|
||||
@@ -168,7 +163,6 @@
|
||||
"hide_anointment": "Покупатель, скорее всего, поменяет зачарование",
|
||||
"hide_for_crafting": "Отмечайте, если проверяете цену в качестве базового предмета для крафта",
|
||||
"hide_empty_mod": "Выбирайте, только если у предмета 6 свойств (1 из которых ремесленное) или если у него 5 свойств",
|
||||
|
||||
"tag_crafted": "мастер",
|
||||
"tag_fractured": "расколотый",
|
||||
"tag_scourge": "преображен",
|
||||
@@ -268,15 +262,15 @@
|
||||
"new_mods_icon": "Иконка у новых модов"
|
||||
},
|
||||
"trade_result": {
|
||||
"error" :"Запрос к сайту не удался",
|
||||
"matched" :"Найдено: {0}",
|
||||
"trade" :"Трейд",
|
||||
"price" :"Цена",
|
||||
"bulk" :"опт",
|
||||
"stock" :"Запас",
|
||||
"fulfill" :"Сделки",
|
||||
"listed" :"Выставлен",
|
||||
"seller" :"Продавец",
|
||||
"error": "Запрос к сайту не удался",
|
||||
"matched": "Найдено: {0}",
|
||||
"trade": "Трейд",
|
||||
"price": "Цена",
|
||||
"bulk": "опт",
|
||||
"stock": "Запас",
|
||||
"fulfill": "Сделки",
|
||||
"listed": "Выставлен",
|
||||
"seller": "Продавец",
|
||||
"item_level": "Ур.",
|
||||
"gem_level": "Уровень",
|
||||
"quality": "К-во",
|
||||
@@ -288,7 +282,7 @@
|
||||
"stack": "Стак"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Настройки - Awakened PoE Trade",
|
||||
"title": "Настройки - Exiled Exchange 2",
|
||||
"language": "Язык",
|
||||
"private_league": "или Приватная лига",
|
||||
"account_name": "Имя учетной записи",
|
||||
@@ -297,9 +291,9 @@
|
||||
"chat_cmd_send": "нажимать Enter",
|
||||
"no_key": "Не назначено",
|
||||
"clear_hotkey": "Вы можете отключить сочетание, нажав клавишу Backspace",
|
||||
"overlay" :"Оверлей",
|
||||
"stash_scroll" :"Прокрутка вкладок тайника",
|
||||
"delve_grid" :"Сетка для \"Спуска\"",
|
||||
"overlay": "Оверлей",
|
||||
"stash_scroll": "Прокрутка вкладок тайника",
|
||||
"delve_grid": "Сетка для \"Спуска\"",
|
||||
"window_title": "Заголовок окна игры",
|
||||
"thank_you": "Разработка приложения продолжается благодаря:",
|
||||
"hotkeys": "Быстрые клавиши",
|
||||
@@ -307,20 +301,20 @@
|
||||
"general": "Общие",
|
||||
"debug": "Debug",
|
||||
"about": "О программе",
|
||||
"font_size" :"Размер шрифта",
|
||||
"overlay_bg" :"Фон, когда окно APT кликабельно",
|
||||
"overlay_bg_none" :"Прозрачный",
|
||||
"overlay_bg_focus_game" :"Нажатие по фону активирует окно игры",
|
||||
"poe_log_file" :"Файл логов PoE",
|
||||
"poe_cfg_file" :"Файл настроек PoE",
|
||||
"restore_clipboard" :"Восстанавливать буфер обмена",
|
||||
"show_overlay_ready" :"Показывать уведомление при открытии PoE",
|
||||
"font_size": "Размер шрифта",
|
||||
"overlay_bg": "Фон, когда окно APT кликабельно",
|
||||
"overlay_bg_none": "Прозрачный",
|
||||
"overlay_bg_focus_game": "Нажатие по фону активирует окно игры",
|
||||
"poe_log_file": "Файл логов PoE",
|
||||
"poe_cfg_file": "Файл настроек PoE",
|
||||
"restore_clipboard": "Восстанавливать буфер обмена",
|
||||
"show_overlay_ready": "Показывать уведомление при открытии PoE",
|
||||
"debug_hotkeys": "Запись всех нажатий клавиш"
|
||||
},
|
||||
"price_check": {
|
||||
"name": "Прайс-чек",
|
||||
"hotkey" :"Режим авто-скрытия",
|
||||
"hotkey_locked" :"Открыть без авто-скрытия",
|
||||
"hotkey": "Режим авто-скрытия",
|
||||
"hotkey_locked": "Открыть без авто-скрытия",
|
||||
"enable_browser": "Включить встроенный браузер",
|
||||
"builtin_browser_warning": "Я осознаю, что в будущие релизы могут потенциально содержать вредоносный код, который может украсть мой POESESSID.",
|
||||
"highlight_hint": "Ваши предметы будут подсвечены, даже если эта настройка выключена",
|
||||
@@ -336,4 +330,4 @@
|
||||
"show_prediction": "Показывать приблизительную цену",
|
||||
"remember_currency": "Запоминать фильтр \"Валюты выкупа\""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 14 KiB |
@@ -299,8 +299,8 @@ function parseNamePlate (section: string[]) {
|
||||
const item: ParserState = {
|
||||
rarity: undefined,
|
||||
category: undefined,
|
||||
name: name,
|
||||
baseType: baseType,
|
||||
name,
|
||||
baseType,
|
||||
isUnidentified: false,
|
||||
isCorrupted: false,
|
||||
newMods: [],
|
||||
|
||||
@@ -47,6 +47,7 @@ export enum ItemCategory {
|
||||
SanctumRelic = 'Sanctum Relic',
|
||||
Tincture = 'Tincture',
|
||||
Charm = 'Charm',
|
||||
Crossbow = 'Crossbow',
|
||||
}
|
||||
|
||||
export const WEAPON_ONE_HANDED_MELEE = new Set([
|
||||
|
||||
@@ -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: dp }
|
||||
return { string: translation.string, negate: translation.negate || false, dp }
|
||||
}
|
||||
|
||||
export enum ModifierType {
|
||||
|
||||
@@ -16,7 +16,7 @@ export function AppConfig (type?: string) {
|
||||
if (!type) {
|
||||
return _config.value!
|
||||
} else {
|
||||
return _config.value!.widgets.find(w => w.wmType === type)
|
||||
return _config.value!.widgets.find((w) => w.wmType === type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ export function updateConfig (updates: Config) {
|
||||
|
||||
export function saveConfig (opts?: { isTemporary: boolean }) {
|
||||
const rawConfig = toRaw(_config.value!)
|
||||
if (rawConfig.widgets.some(w => w.wmZorder === 'exclusive' && w.wmWants === 'show')) {
|
||||
if (
|
||||
rawConfig.widgets.some(
|
||||
(w) => w.wmZorder === 'exclusive' && w.wmWants === 'show'
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -72,9 +76,9 @@ export async function initConfig () {
|
||||
|
||||
// TODO
|
||||
// dialog.showErrorBox(
|
||||
// 'Awakened PoE Trade - Incompatible configuration',
|
||||
// 'Exiled Exchange 2 - Incompatible configuration',
|
||||
// // ----------------------
|
||||
// 'You are trying to use an older version of Awakened PoE Trade with a newer incompatible configuration file.\n' +
|
||||
// 'You are trying to use an older version of Exiled Exchange 2 with a newer incompatible configuration file.\n' +
|
||||
// 'You need to install the latest version to continue using it.'
|
||||
// )
|
||||
}
|
||||
@@ -85,12 +89,14 @@ export async function initConfig () {
|
||||
export function poeWebApi () {
|
||||
const { language, realm } = AppConfig()
|
||||
switch (language) {
|
||||
case 'en': return 'www.pathofexile.com'
|
||||
case 'ru': return 'ru.pathofexile.com'
|
||||
case 'cmn-Hant': return (realm === 'pc-garena')
|
||||
? 'pathofexile.tw'
|
||||
: 'www.pathofexile.com'
|
||||
case 'ko': return 'poe.game.daum.net'
|
||||
case 'en':
|
||||
return 'www.pathofexile.com'
|
||||
case 'ru':
|
||||
return 'ru.pathofexile.com'
|
||||
case 'cmn-Hant':
|
||||
return realm === 'pc-garena' ? 'pathofexile.tw' : 'www.pathofexile.com'
|
||||
case 'ko':
|
||||
return 'poe.game.daum.net'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,34 +132,41 @@ export const defaultConfig = (): Config => ({
|
||||
overlayBackgroundClose: true,
|
||||
restoreClipboard: false,
|
||||
showAttachNotification: true,
|
||||
commands: [{
|
||||
text: '/hideout',
|
||||
hotkey: 'F5',
|
||||
send: true
|
||||
}, {
|
||||
text: '/exit',
|
||||
hotkey: 'F9',
|
||||
send: true
|
||||
}, {
|
||||
text: '@last ty',
|
||||
hotkey: null,
|
||||
send: true
|
||||
}, {
|
||||
text: '/invite @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
}, {
|
||||
text: '/tradewith @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
}, {
|
||||
text: '/hideout @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
}],
|
||||
commands: [
|
||||
{
|
||||
text: '/hideout',
|
||||
hotkey: 'F5',
|
||||
send: true
|
||||
},
|
||||
{
|
||||
text: '/exit',
|
||||
hotkey: 'F9',
|
||||
send: true
|
||||
},
|
||||
{
|
||||
text: '@last ty',
|
||||
hotkey: null,
|
||||
send: true
|
||||
},
|
||||
{
|
||||
text: '/invite @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
},
|
||||
{
|
||||
text: '/tradewith @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
},
|
||||
{
|
||||
text: '/hideout @last',
|
||||
hotkey: null,
|
||||
send: true
|
||||
}
|
||||
],
|
||||
clientLog: null,
|
||||
gameConfig: null,
|
||||
windowTitle: 'Path of Exile',
|
||||
windowTitle: 'Path of Exile 2',
|
||||
logKeys: false,
|
||||
accountName: '',
|
||||
stashScroll: true,
|
||||
@@ -301,7 +314,12 @@ export const defaultConfig = (): Config => ({
|
||||
{ id: 2, name: '', text: '"Divination Card"', hotkey: null },
|
||||
{ id: 3, name: '', text: 'Fossil', hotkey: null },
|
||||
{ id: 4, name: '', text: '"Map Tier"', hotkey: null },
|
||||
{ id: 5, name: '', text: '"Map Device" "Rarity: Normal"', hotkey: null },
|
||||
{
|
||||
id: 5,
|
||||
name: '',
|
||||
text: '"Map Device" "Rarity: Normal"',
|
||||
hotkey: null
|
||||
},
|
||||
{ id: 6, name: '', text: 'Tane Laboratory', hotkey: null }
|
||||
]
|
||||
} as StashSearchWidget,
|
||||
@@ -317,47 +335,47 @@ export const defaultConfig = (): Config => ({
|
||||
x: 50,
|
||||
y: 10
|
||||
},
|
||||
images: [
|
||||
{ id: 1, url: 'syndicate.jpg' }
|
||||
]
|
||||
images: [{ id: 1, url: 'syndicate.jpg' }]
|
||||
} as widget.ImageStripWidget
|
||||
]
|
||||
})
|
||||
|
||||
function upgradeConfig (_config: Config): Config {
|
||||
const config = _config as Omit<Config, 'widgets'> & { widgets: Array<Record<string, any>> }
|
||||
const config = _config as Omit<Config, 'widgets'> & {
|
||||
widgets: Array<Record<string, any>>
|
||||
}
|
||||
|
||||
if (config.configVersion < 3) {
|
||||
config.widgets.push({
|
||||
...defaultConfig().widgets.find(w => w.wmType === 'image-strip')!,
|
||||
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1,
|
||||
...defaultConfig().widgets.find((w) => w.wmType === 'image-strip')!,
|
||||
wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
|
||||
wmZorder: null
|
||||
})
|
||||
|
||||
config.widgets.push({
|
||||
...defaultConfig().widgets.find(w => w.wmType === 'delve-grid')!,
|
||||
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1,
|
||||
...defaultConfig().widgets.find((w) => w.wmType === 'delve-grid')!,
|
||||
wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1,
|
||||
wmZorder: null
|
||||
})
|
||||
|
||||
config.widgets.find(w => w.wmType === 'menu')!
|
||||
.alwaysShow = false
|
||||
config.widgets.find((w) => w.wmType === 'menu')!.alwaysShow = false
|
||||
|
||||
config.configVersion = 3
|
||||
}
|
||||
|
||||
if (config.configVersion < 4) {
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.chaosPriceThreshold = 0.05
|
||||
config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
)!.chaosPriceThreshold = 0.05
|
||||
|
||||
const mapCheck = config.widgets.find(w => w.wmType === 'map-check')!
|
||||
;(mapCheck as any).selectedStats.forEach((e: any) => {
|
||||
const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!;
|
||||
(mapCheck as any).selectedStats.forEach((e: any) => {
|
||||
e.matcher = e.matchRef
|
||||
e.matchRef = undefined
|
||||
})
|
||||
|
||||
{
|
||||
const widgets = config.widgets.filter(w => w.wmType === 'image-strip')!
|
||||
const widgets = config.widgets.filter((w) => w.wmType === 'image-strip')!
|
||||
widgets.forEach((imgStrip: any) => {
|
||||
imgStrip.images.forEach((e: any, idx: number) => {
|
||||
e.id = idx
|
||||
@@ -369,7 +387,7 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 5) {
|
||||
config.commands.forEach(cmd => {
|
||||
config.commands.forEach((cmd) => {
|
||||
cmd.send = true
|
||||
})
|
||||
|
||||
@@ -377,63 +395,68 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 6) {
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.showRateLimitState = ((config as any).logLevel === 'debug')
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.apiLatencySeconds = 2
|
||||
config.widgets.find((w) => w.wmType === 'price-check')!.showRateLimitState =
|
||||
(config as any).logLevel === 'debug'
|
||||
config.widgets.find((w) => w.wmType === 'price-check')!.apiLatencySeconds =
|
||||
2
|
||||
|
||||
config.configVersion = 6
|
||||
}
|
||||
|
||||
if (config.configVersion < 7) {
|
||||
const mapCheck = config.widgets.find(w => w.wmType === 'map-check')!
|
||||
const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!
|
||||
mapCheck.wmType = 'item-check'
|
||||
mapCheck.maps = { selectedStats: mapCheck.selectedStats }
|
||||
mapCheck.selectedStats = undefined
|
||||
|
||||
;(config as any).itemCheckKey = (config as any).mapCheckKey || null
|
||||
;(config as any).mapCheckKey = undefined
|
||||
mapCheck.selectedStats = undefined;
|
||||
(config as any).itemCheckKey = (config as any).mapCheckKey || null;
|
||||
(config as any).mapCheckKey = undefined
|
||||
|
||||
config.configVersion = 7
|
||||
}
|
||||
|
||||
if (config.configVersion < 8) {
|
||||
const itemCheck = config.widgets.find(w => w.wmType === 'item-check')!
|
||||
;(itemCheck as ItemCheckWidget).maps.showNewStats = false
|
||||
itemCheck.maps.selectedStats = (itemCheck as ItemCheckWidget).maps.selectedStats.map(entry => ({
|
||||
const itemCheck = config.widgets.find((w) => w.wmType === 'item-check')!;
|
||||
(itemCheck as ItemCheckWidget).maps.showNewStats = false
|
||||
itemCheck.maps.selectedStats = (
|
||||
itemCheck as ItemCheckWidget
|
||||
).maps.selectedStats.map((entry) => ({
|
||||
matcher: entry.matcher,
|
||||
decision:
|
||||
(entry as any).valueDanger ? 'danger'
|
||||
: (entry as any).valueWarning ? 'warning'
|
||||
: (entry as any).valueDesirable ? 'desirable'
|
||||
: 'seen'
|
||||
decision: (entry as any).valueDanger
|
||||
? 'danger'
|
||||
: (entry as any).valueWarning
|
||||
? 'warning'
|
||||
: (entry as any).valueDesirable
|
||||
? 'desirable'
|
||||
: 'seen'
|
||||
}))
|
||||
|
||||
config.configVersion = 8
|
||||
}
|
||||
|
||||
if (config.configVersion < 9) {
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.collapseListings = 'api'
|
||||
config.widgets.find((w) => w.wmType === 'price-check')!.collapseListings =
|
||||
'api'
|
||||
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.smartInitialSearch = true
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.lockedInitialSearch = true
|
||||
config.widgets.find((w) => w.wmType === 'price-check')!.smartInitialSearch =
|
||||
true
|
||||
config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
)!.lockedInitialSearch = true
|
||||
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.activateStockFilter = false
|
||||
config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
)!.activateStockFilter = false
|
||||
|
||||
config.configVersion = 9
|
||||
}
|
||||
|
||||
if (config.configVersion < 10) {
|
||||
config.widgets.push({
|
||||
...defaultConfig().widgets.find(w => w.wmType === 'settings')!,
|
||||
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1
|
||||
...defaultConfig().widgets.find((w) => w.wmType === 'settings')!,
|
||||
wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1
|
||||
})
|
||||
|
||||
const priceCheck = config.widgets.find(w => w.wmType === 'price-check')!
|
||||
const priceCheck = config.widgets.find((w) => w.wmType === 'price-check')!
|
||||
priceCheck.hotkey = (config as any).priceCheckKey
|
||||
priceCheck.hotkeyHold = (config as any).priceCheckKeyHold
|
||||
priceCheck.hotkeyLocked = (config as any).priceCheckLocked
|
||||
@@ -449,22 +472,25 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 11) {
|
||||
config.widgets.find(w => w.wmType === 'price-check')!
|
||||
.requestPricePrediction = false
|
||||
config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
)!.requestPricePrediction = false
|
||||
|
||||
config.configVersion = 11
|
||||
}
|
||||
|
||||
if (config.configVersion < 12) {
|
||||
const afterSettings = config.widgets.findIndex(w => w.wmType === 'settings')
|
||||
const afterSettings = config.widgets.findIndex(
|
||||
(w) => w.wmType === 'settings'
|
||||
)
|
||||
config.widgets.splice(afterSettings + 1, 0, {
|
||||
...defaultConfig().widgets.find(w => w.wmType === 'item-search')!,
|
||||
...defaultConfig().widgets.find((w) => w.wmType === 'item-search')!,
|
||||
wmWants: 'show',
|
||||
wmId: Math.max(0, ...config.widgets.map(_ => _.wmId)) + 1
|
||||
wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1
|
||||
})
|
||||
|
||||
config.realm = 'pc-ggg'
|
||||
if (config.language === 'zh_TW' as string) {
|
||||
if (config.language === ('zh_TW' as string)) {
|
||||
config.language = 'cmn-Hant'
|
||||
}
|
||||
|
||||
@@ -478,7 +504,9 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 14) {
|
||||
const imgWidgets = config.widgets.filter(w => w.wmType === 'image-strip') as widget.ImageStripWidget[]
|
||||
const imgWidgets = config.widgets.filter(
|
||||
(w) => w.wmType === 'image-strip'
|
||||
) as widget.ImageStripWidget[]
|
||||
imgWidgets.forEach((imgStrip) => {
|
||||
imgStrip.images.forEach((e) => {
|
||||
e.url = e.url.startsWith('app-file://')
|
||||
@@ -487,7 +515,9 @@ function upgradeConfig (_config: Config): Config {
|
||||
})
|
||||
})
|
||||
|
||||
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget
|
||||
const itemCheck = config.widgets.find(
|
||||
(w) => w.wmType === 'item-check'
|
||||
) as ItemCheckWidget
|
||||
itemCheck.wikiKey = (config as any).wikiKey
|
||||
itemCheck.poedbKey = null
|
||||
itemCheck.craftOfExileKey = (config as any).craftOfExileKey
|
||||
@@ -497,19 +527,29 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 15) {
|
||||
const priceCheck = config.widgets.find(w => w.wmType === 'price-check') as widget.PriceCheckWidget
|
||||
const priceCheck = config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
) as widget.PriceCheckWidget
|
||||
priceCheck.builtinBrowser = false
|
||||
|
||||
const itemSearch = config.widgets.find(w => w.wmType === 'item-search') as ItemSearchWidget
|
||||
const itemSearch = config.widgets.find(
|
||||
(w) => w.wmType === 'item-search'
|
||||
) as ItemSearchWidget
|
||||
itemSearch.ocrGemsKey = null
|
||||
|
||||
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget
|
||||
const itemCheck = config.widgets.find(
|
||||
(w) => w.wmType === 'item-check'
|
||||
) as ItemCheckWidget
|
||||
itemCheck.maps.profile = 1
|
||||
for (const stat of itemCheck.maps.selectedStats) {
|
||||
const p1decision =
|
||||
(stat.decision === 'danger') ? 'd'
|
||||
: (stat.decision === 'warning') ? 'w'
|
||||
: (stat.decision === 'desirable') ? 'g' : 's'
|
||||
stat.decision === 'danger'
|
||||
? 'd'
|
||||
: stat.decision === 'warning'
|
||||
? 'w'
|
||||
: stat.decision === 'desirable'
|
||||
? 'g'
|
||||
: 's'
|
||||
|
||||
stat.decision = `${p1decision}--`
|
||||
}
|
||||
@@ -518,10 +558,14 @@ function upgradeConfig (_config: Config): Config {
|
||||
}
|
||||
|
||||
if (config.configVersion < 16) {
|
||||
const delve = config.widgets.find(w => w.wmType === 'delve-grid') as widget.DelveGridWidget
|
||||
const delve = config.widgets.find(
|
||||
(w) => w.wmType === 'delve-grid'
|
||||
) as widget.DelveGridWidget
|
||||
delve.toggleKey = (config as any).delveGridKey
|
||||
|
||||
const itemCheck = config.widgets.find(w => w.wmType === 'item-check') as ItemCheckWidget
|
||||
const itemCheck = config.widgets.find(
|
||||
(w) => w.wmType === 'item-check'
|
||||
) as ItemCheckWidget
|
||||
itemCheck.hotkey = (config as any).itemCheckKey
|
||||
|
||||
if (itemCheck.maps.profile === undefined) {
|
||||
@@ -536,7 +580,9 @@ function upgradeConfig (_config: Config): Config {
|
||||
config.logKeys = false
|
||||
}
|
||||
|
||||
const priceCheck = config.widgets.find(w => w.wmType === 'price-check') as widget.PriceCheckWidget
|
||||
const priceCheck = config.widgets.find(
|
||||
(w) => w.wmType === 'price-check'
|
||||
) as widget.PriceCheckWidget
|
||||
if (priceCheck.rememberCurrency === undefined) {
|
||||
priceCheck.rememberCurrency = false
|
||||
}
|
||||
@@ -616,7 +662,11 @@ function getConfigForHost (): HostConfig {
|
||||
if (command.hotkey) {
|
||||
actions.push({
|
||||
shortcut: command.hotkey,
|
||||
action: { type: 'paste-in-chat', text: command.text, send: command.send }
|
||||
action: {
|
||||
type: 'paste-in-chat',
|
||||
text: command.text,
|
||||
send: command.send
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { computed, shallowRef, readonly } from 'vue'
|
||||
import { createGlobalState } from '@vueuse/core'
|
||||
import { AppConfig, poeWebApi } from '@/web/Config'
|
||||
import { Host } from './IPC'
|
||||
import { AppConfig } from '@/web/Config'
|
||||
|
||||
// pc-ggg, pc-garena
|
||||
// const PERMANENT_SC = ['Standard', '標準模式']
|
||||
@@ -23,11 +22,21 @@ export const useLeagues = createGlobalState(() => {
|
||||
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
|
||||
return tradeLeagues.value.length ? AppConfig().leagueId : undefined
|
||||
},
|
||||
set (id) {
|
||||
AppConfig().leagueId = id
|
||||
@@ -37,7 +46,7 @@ export const useLeagues = createGlobalState(() => {
|
||||
const selected = computed(() => {
|
||||
const { leagueId } = AppConfig()
|
||||
if (!tradeLeagues.value || !leagueId) return undefined
|
||||
const listed = tradeLeagues.value.find(league => league.id === leagueId)
|
||||
const listed = tradeLeagues.value.find((league) => league.id === leagueId)
|
||||
return {
|
||||
id: leagueId,
|
||||
realm: AppConfig().realm,
|
||||
@@ -50,19 +59,30 @@ export const useLeagues = createGlobalState(() => {
|
||||
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 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 => {
|
||||
.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)
|
||||
const leagueIsAlive = tradeLeagues.value.some(
|
||||
(league) => league.id === selectedId.value
|
||||
)
|
||||
if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) {
|
||||
if (tradeLeagues.value.length > 1) {
|
||||
const TMP_CHALLENGE = 1
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="bg-orange-400 text-gray-900 text-center text-sm font-bold text-shadow-lg">
|
||||
This is in BETA for POE2, things do not work as expected. Please report any issues.
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<Widget :config="{ ...config, anchor }" move-handles="none" :removable="false" :inline-edit="false">
|
||||
<template v-if="item">
|
||||
<MapCheck v-if="isMapLike"
|
||||
:item="item" :config="config.maps" />
|
||||
<ItemInfo v-else
|
||||
:item="item" />
|
||||
<ConversionWarningBanner />
|
||||
<MapCheck v-if="isMapLike" :item="item" :config="config.maps" />
|
||||
<ItemInfo v-else :item="item" />
|
||||
</template>
|
||||
</Widget>
|
||||
</template>
|
||||
@@ -20,6 +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'
|
||||
|
||||
const props = defineProps<{
|
||||
config: ItemCheckWidget
|
||||
|
||||
@@ -6,7 +6,14 @@ 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
|
||||
if (
|
||||
![
|
||||
'open-wiki',
|
||||
'open-craft-of-exile',
|
||||
'open-poedb',
|
||||
'search-similar'
|
||||
].includes(e.target)
|
||||
) { return }
|
||||
const parsed = parseClipboard(e.clipboard)
|
||||
if (!parsed.isOk()) return
|
||||
|
||||
@@ -23,14 +30,18 @@ export function registerActions () {
|
||||
}
|
||||
|
||||
export function openWiki (item: ParsedItem) {
|
||||
window.open(`https://www.poewiki.net/wiki/${item.info.refName}`)
|
||||
window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`)
|
||||
}
|
||||
export function openPoedb (item: ParsedItem) {
|
||||
window.open(`https://poedb.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}`)
|
||||
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/?eimport=${encodedClipboard}`)
|
||||
window.open(
|
||||
`https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}`
|
||||
)
|
||||
}
|
||||
|
||||
export function findSimilarItems (item: ParsedItem) {
|
||||
|
||||
@@ -5,12 +5,8 @@
|
||||
<div class="flex-1 text-center">{{ mapName }}</div>
|
||||
<div class="ml-8 text-gray-400">{{ t('map_check.profile') }}</div>
|
||||
<div class="flex gap-0.5">
|
||||
<button
|
||||
v-for="profile in profiles" :key="profile.text"
|
||||
@click="profile.select"
|
||||
:class="{ 'border border-gray-600': profile.active }"
|
||||
class="w-6 bg-gray-800"
|
||||
>{{ profile.text }}</button>
|
||||
<button v-for="profile in profiles" :key="profile.text" @click="profile.select"
|
||||
:class="{ 'border border-gray-600': profile.active }" class="w-6 bg-gray-800">{{ profile.text }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<FullscreenImage v-if="image" :src="image" style="height: auto;" />
|
||||
@@ -18,10 +14,8 @@
|
||||
{{ t('map_check.no_mods') }}
|
||||
</div>
|
||||
<div v-else class="py-2 flex flex-col">
|
||||
<MapStatButton v-for="stat in mapStats" :key="stat.matcher"
|
||||
:stat="stat" :config="config" />
|
||||
<div v-for="stat of item.unknownModifiers" :key="stat.type + '/' + stat.text"
|
||||
class="py-1 px-8">
|
||||
<MapStatButton v-for="stat in mapStats" :key="stat.matcher" :stat="stat" :config="config" />
|
||||
<div v-for="stat of item.unknownModifiers" :key="stat.type + '/' + stat.text" class="py-1 px-8">
|
||||
<span class="text-orange-400">{{ t('Not recognized modifier') }} —</span> {{ stat.text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<transition
|
||||
enter-active-class="animate__animated animate__fadeIn"
|
||||
<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">Awakened PoE Trade</div>
|
||||
<div class="text-base">Exiled Exchange 2</div>
|
||||
<p>{{ t('app_is_ready') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +25,9 @@ const show = shallowRef(false)
|
||||
Host.onEvent('MAIN->OVERLAY::overlay-attached', () => {
|
||||
if (!show.value && AppConfig().showAttachNotification) {
|
||||
show.value = true
|
||||
setTimeout(() => { show.value = false }, 2500)
|
||||
setTimeout(() => {
|
||||
show.value = false
|
||||
}, 2500)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -52,7 +53,7 @@ Host.onEvent('MAIN->OVERLAY::overlay-attached', () => {
|
||||
.box::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: url('/images/TransferOrb.png') no-repeat top right/contain;
|
||||
background: url('/images/exa.png') no-repeat top right/contain;
|
||||
right: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<widget :config="config" :hideable="false" :removable="false" move-handles="corners" v-slot="{ isEditing }">
|
||||
<div class="widget-default-style">
|
||||
<ConversionWarningBanner />
|
||||
<div class="p-1 flex gap-1 items-center text-base">
|
||||
<template v-for="widget in widgets" :key="widget.wmId">
|
||||
<button @click="toggle(widget)"
|
||||
:class="widget.wmWants === 'show' ? 'border-gray-500' : 'border-gray-800'"
|
||||
class="bg-gray-800 rounded text-gray-100 p-2 leading-none whitespace-nowrap border"
|
||||
>
|
||||
<button @click="toggle(widget)" :class="widget.wmWants === 'show' ? 'border-gray-500' : 'border-gray-800'"
|
||||
class="bg-gray-800 rounded text-gray-100 p-2 leading-none whitespace-nowrap border">
|
||||
<i v-if="widget.wmType === 'settings'" class="fas fa-cog align-bottom" />
|
||||
<i v-else-if="widget.wmType === 'item-search'" class="fas fa-search align-bottom" />
|
||||
<template v-else>{{ widget.wmTitle || `#${widget.wmId}` }}</template>
|
||||
@@ -22,9 +21,12 @@
|
||||
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap">Screen saver</button> -->
|
||||
<!-- add widget -->
|
||||
<div class="text-gray-600 text-sm px-1 select-none whitespace-nowrap">{{ t(':add') }}</div>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('timer')">{{ t('stopwatch.name') }}</button>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('stash-search')">{{ t('stash_search.name') }}</button>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('image-strip')">{{ t('image_strip.name') }}</button>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
|
||||
@click="createOfType('timer')">{{ t('stopwatch.name') }}</button>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
|
||||
@click="createOfType('stash-search')">{{ t('stash_search.name') }}</button>
|
||||
<button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap"
|
||||
@click="createOfType('image-strip')">{{ t('image_strip.name') }}</button>
|
||||
<!-- <button class="text-left hover:bg-gray-400 rounded px-1 whitespace-nowrap" @click="createOfType('TODO')">Image</button> -->
|
||||
</div>
|
||||
</template>
|
||||
@@ -34,8 +36,7 @@
|
||||
<ui-toggle v-model="config.alwaysShow">{{ t(':always_show') }}</ui-toggle>
|
||||
</div>
|
||||
<div v-else class="px-1 pb-1">
|
||||
<textarea class="px-2 py-1.5 bg-gray-700 rounded resize-none block"
|
||||
rows="1" spellcheck="false"
|
||||
<textarea class="px-2 py-1.5 bg-gray-700 rounded resize-none block" rows="1" spellcheck="false"
|
||||
:placeholder="t(':price_check')" @input="handleItemPaste"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,9 +51,10 @@ import { Widget as IWidget, WidgetManager, WidgetMenu } from './interfaces'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import Widget from './Widget.vue'
|
||||
import { useI18nNs } from '@/web/i18n'
|
||||
import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { Widget, UiToggle, UiPopover },
|
||||
components: { Widget, UiToggle, UiPopover, ConversionWarningBanner },
|
||||
props: {
|
||||
config: {
|
||||
type: Object as PropType<WidgetMenu>,
|
||||
|
||||
@@ -1,42 +1,24 @@
|
||||
<template>
|
||||
<div v-if="show" class="p-4 layout-column min-h-0">
|
||||
<filter-name
|
||||
:filters="itemFilters"
|
||||
:item="item" />
|
||||
<price-prediction v-if="showPredictedPrice" class="mb-4"
|
||||
:item="item" />
|
||||
<price-trend v-else
|
||||
:item="item"
|
||||
:filters="itemFilters" />
|
||||
<filters-block
|
||||
ref="filtersComponent"
|
||||
:filters="itemFilters"
|
||||
:stats="itemStats"
|
||||
:item="item"
|
||||
:presets="presets"
|
||||
@preset="selectPreset"
|
||||
@submit="doSearch = true" />
|
||||
<trade-listing
|
||||
v-if="tradeAPI === 'trade' && doSearch"
|
||||
ref="tradeService"
|
||||
:filters="itemFilters"
|
||||
:stats="itemStats"
|
||||
:item="item" />
|
||||
<trade-bulk
|
||||
v-if="tradeAPI === 'bulk' && doSearch"
|
||||
ref="tradeService"
|
||||
:filters="itemFilters"
|
||||
<filter-name :filters="itemFilters" :item="item" />
|
||||
<price-prediction v-if="showPredictedPrice" class="mb-4" :item="item" />
|
||||
<price-trend v-else :item="item" :filters="itemFilters" />
|
||||
<filters-block ref="filtersComponent" :filters="itemFilters" :stats="itemStats" :item="item" :presets="presets"
|
||||
@preset="selectPreset" @submit="doSearch = true" />
|
||||
<trade-listing v-if="tradeAPI === 'trade' && doSearch" ref="tradeService" :filters="itemFilters" :stats="itemStats"
|
||||
:item="item" />
|
||||
<trade-bulk v-if="tradeAPI === 'bulk' && doSearch" ref="tradeService" :filters="itemFilters" :item="item" />
|
||||
<div v-if="!doSearch" class="flex justify-between items-center">
|
||||
<div class="flex w-40" @mouseenter="handleSearchMouseenter">
|
||||
<button class="btn" @click="doSearch = true" style="min-width: 5rem;">{{ t('Search') }}</button>
|
||||
</div>
|
||||
<trade-links v-if="tradeAPI === 'trade'"
|
||||
:get-link="makeTradeLink" />
|
||||
<trade-links v-if="tradeAPI === 'trade'" :get-link="makeTradeLink" />
|
||||
</div>
|
||||
<stack-value :filters="itemFilters" :item="item"/>
|
||||
<stack-value :filters="itemFilters" :item="item" />
|
||||
<div v-if="showSupportLinks" class="mt-auto border border-dashed p-2">
|
||||
<div class="mb-1">{{ t('Support development on') }} <a href="https://patreon.com/awakened_poe_trade" class="inline-flex align-middle animate__animated animate__fadeInRight" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a></div>
|
||||
<div class="mb-1">{{ t('Support development on') }} <a href="https://patreon.com/awakened_poe_trade"
|
||||
class="inline-flex align-middle animate__animated animate__fadeInRight" target="_blank"><img
|
||||
class="inline h-5" src="/images/Patreon.svg"></a></div>
|
||||
<i18n-t keypath="app.thanks_3rd_party" tag="div">
|
||||
<a href="https://poeprices.info" target="_blank" class="bg-gray-900 px-1 rounded">poeprices.info</a>
|
||||
<a href="https://poe.ninja/support" target="_blank" class="bg-gray-900 px-1 rounded">poe.ninja</a>
|
||||
@@ -120,7 +102,7 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
if ((!props.advancedCheck && !widget.value.smartInitialSearch) ||
|
||||
(props.advancedCheck && !widget.value.lockedInitialSearch)) {
|
||||
(props.advancedCheck && !widget.value.lockedInitialSearch)) {
|
||||
doSearch.value = false
|
||||
} else {
|
||||
doSearch.value = Boolean(
|
||||
@@ -174,8 +156,8 @@ export default defineComponent({
|
||||
|
||||
const showPredictedPrice = computed(() => {
|
||||
if (!widget.value.requestPricePrediction ||
|
||||
AppConfig().language !== 'en' ||
|
||||
!leagues.selected.value!.isPopular) return false
|
||||
AppConfig().language !== 'en' ||
|
||||
!leagues.selected.value!.isPopular) return false
|
||||
|
||||
if (presets.value.active === 'filters.preset_base_item') return false
|
||||
|
||||
@@ -238,7 +220,7 @@ export default defineComponent({
|
||||
presets.value.active = id
|
||||
},
|
||||
makeTradeLink () {
|
||||
return `https://${getTradeEndpoint()}/trade/search/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}`
|
||||
return `https://${getTradeEndpoint()}/trade2/search/poe2/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,52 @@
|
||||
<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;">
|
||||
<AppTitleBar @close="closePriceCheck" @click="openLeagueSelection" :title="title">
|
||||
<ui-popover v-if="stableOrbCost" trigger="click" boundary="#price-window">
|
||||
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>
|
||||
<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-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 class="pl-1">
|
||||
{{ i / 10 }} div ⇒ {{ Math.round((stableOrbCost * i) / 10) }} c
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ui-popover>
|
||||
@@ -29,37 +55,61 @@
|
||||
</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;" />
|
||||
<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>
|
||||
<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" />
|
||||
<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>
|
||||
<span class="bg-gray-400 text-gray-900 rounded px-1">{{
|
||||
overlayKey
|
||||
}}</span>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<webview v-if="isBrowserShown" ref="iframeEl"
|
||||
<webview
|
||||
v-if="isBrowserShown"
|
||||
ref="iframeEl"
|
||||
class="pointer-events-auto flex-1"
|
||||
width="100%" height="100%" />
|
||||
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" />
|
||||
<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>
|
||||
@@ -86,8 +136,13 @@ 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'] }
|
||||
type ParseError = {
|
||||
name: string;
|
||||
message: string;
|
||||
rawText: ParsedItem['rawText'];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -100,7 +155,8 @@ export default defineComponent({
|
||||
CheckPositionCircle,
|
||||
ItemQuickPrice,
|
||||
UiErrorBox,
|
||||
UiPopover
|
||||
UiPopover,
|
||||
ConversionWarningBanner
|
||||
},
|
||||
props: {
|
||||
config: {
|
||||
@@ -127,9 +183,13 @@ export default defineComponent({
|
||||
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
|
||||
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: {
|
||||
@@ -151,13 +211,18 @@ export default defineComponent({
|
||||
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 => ({
|
||||
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
|
||||
@@ -176,14 +241,17 @@ export default defineComponent({
|
||||
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 || 'Awakened PoE Trade')
|
||||
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)
|
||||
@@ -196,7 +264,7 @@ export default defineComponent({
|
||||
return checkPosition.value.x > (window.screenX + window.innerWidth / 2)
|
||||
? 'inventory'
|
||||
: 'stash'
|
||||
// or {chat, vendor, center of screen}
|
||||
// or {chat, vendor, center of screen}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -6,8 +6,8 @@ import { usePoeninja } from '@/web/background/Prices'
|
||||
|
||||
const cache = new Cache()
|
||||
|
||||
interface PoepricesApiResponse { /* eslint-disable camelcase */
|
||||
currency: 'chaos' | 'divine' | 'exalt'
|
||||
interface PoepricesApiResponse {
|
||||
/* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt'
|
||||
error: number
|
||||
error_msg: string
|
||||
warning_msg: string
|
||||
@@ -28,20 +28,24 @@ export interface RareItemPrice {
|
||||
}>
|
||||
}
|
||||
|
||||
export async function requestPoeprices (item: ParsedItem): Promise<RareItemPrice> {
|
||||
export async function requestPoeprices (
|
||||
item: ParsedItem
|
||||
): Promise<RareItemPrice> {
|
||||
const query = querystring({
|
||||
i: utf8ToBase64(transformItemText(item.rawText)),
|
||||
l: useLeagues().selectedId.value,
|
||||
s: 'awakened-poe-trade'
|
||||
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
|
||||
data = (await response.json()) as PoepricesApiResponse
|
||||
} catch (e) {
|
||||
throw new Error(`${response.status}, poeprices.info API is under load or down.`)
|
||||
throw new Error(
|
||||
`${response.status}, poeprices.info API is under load or down.`
|
||||
)
|
||||
}
|
||||
|
||||
if (data.error !== 0) {
|
||||
@@ -53,24 +57,31 @@ export async function requestPoeprices (item: ParsedItem): Promise<RareItemPrice
|
||||
|
||||
if (data.currency === 'exalt') {
|
||||
const { findPriceByQuery, autoCurrency } = usePoeninja()
|
||||
const xchgExalted = findPriceByQuery({ ns: 'ITEM', name: 'Exalted Orb', variant: undefined })
|
||||
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])
|
||||
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'
|
||||
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',
|
||||
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 => ({
|
||||
explanation: data.pred_explanation.map((expl) => ({
|
||||
name: expl[0],
|
||||
contrib: Math.round(expl[1] * 100)
|
||||
}))
|
||||
@@ -118,7 +129,7 @@ function utf8ToBase64 (value: string) {
|
||||
|
||||
function querystring (q: Record<string, any>) {
|
||||
return Object.entries(q)
|
||||
.map(pair => pair.map(encodeURIComponent).join('='))
|
||||
.map((pair) => pair.map(encodeURIComponent).join('='))
|
||||
.join('&')
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
<span class="mr-1">{{ t(':matched') }}</span>
|
||||
<span v-if="!result" class="text-gray-600">...</span>
|
||||
<div v-else class="flex items-center">
|
||||
<button class="btn flex items-center mr-1" :style="{ background: selectedCurr !== 'xchgChaos' ? 'transparent' : undefined }"
|
||||
<button class="btn flex items-center mr-1"
|
||||
:style="{ background: selectedCurr !== 'xchgChaos' ? 'transparent' : undefined }"
|
||||
@click="selectedCurr = 'xchgChaos'">
|
||||
<img src="/images/chaos.png" class="trade-bulk-currency-icon">
|
||||
<span>{{ result.xchgChaos.listed.value?.total ?? '?' }}</span>
|
||||
</button>
|
||||
<button class="btn flex items-center mr-1" :style="{ background: selectedCurr !== 'xchgStable' ? 'transparent' : undefined }"
|
||||
<button class="btn flex items-center mr-1"
|
||||
:style="{ background: selectedCurr !== 'xchgStable' ? 'transparent' : undefined }"
|
||||
@click="selectedCurr = 'xchgStable'">
|
||||
<img src="/images/divine.png" class="trade-bulk-currency-icon">
|
||||
<span>{{ result.xchgStable.listed.value?.total ?? '?' }}</span>
|
||||
@@ -19,8 +21,7 @@
|
||||
<span class="ml-1"><online-filter :filters="filters" /></span>
|
||||
</div>
|
||||
</div>
|
||||
<trade-links v-if="result"
|
||||
:get-link="makeTradeLink" />
|
||||
<trade-links v-if="result" :get-link="makeTradeLink" />
|
||||
</div>
|
||||
<div class="layout-column overflow-y-auto overflow-x-hidden">
|
||||
<table class="table-stripped w-full">
|
||||
@@ -30,7 +31,10 @@
|
||||
<div class="px-2">{{ t(':price') }}</div>
|
||||
</th>
|
||||
<th class="trade-table-heading">
|
||||
<div class="pl-1 pr-2 flex text-xs" style="line-height: 1.3125rem;"><span class="w-8 inline-block text-right -ml-px mr-px">{{ (selectedCurr === 'xchgChaos') ? 'chaos' : 'div' }}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ t(':bulk') }}</span></div>
|
||||
<div class="pl-1 pr-2 flex text-xs" style="line-height: 1.3125rem;"><span
|
||||
class="w-8 inline-block text-right -ml-px mr-px">{{ (selectedCurr === 'xchgChaos') ? 'chaos' : 'div'
|
||||
}}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ t(':bulk')
|
||||
}}</span></div>
|
||||
</th>
|
||||
<th class="trade-table-heading">
|
||||
<div class="px-1">{{ t(':stock') }}</div>
|
||||
@@ -55,19 +59,25 @@
|
||||
</tr>
|
||||
<tr v-else :key="result.id">
|
||||
<td class="px-2">{{ Number((result.exchangeAmount / result.itemAmount).toFixed(4)) }}</td>
|
||||
<td class="pl-1 whitespace-nowrap"><span class="w-8 inline-block text-right">{{ result.exchangeAmount }}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ result.itemAmount }}</span></td>
|
||||
<td class="pl-1 whitespace-nowrap"><span class="w-8 inline-block text-right">{{ result.exchangeAmount
|
||||
}}</span><span>{{ '\u2009' }}/{{ '\u2009' }}</span><span class="w-8 inline-block">{{ result.itemAmount
|
||||
}}</span></td>
|
||||
<td class="px-1 text-right">{{ result.stock }}</td>
|
||||
<td class="px-1 text-right"><i v-if="result.stock < result.itemAmount" class="fas fa-exclamation-triangle mr-1 text-gray-500"></i>{{ Math.floor(result.stock / result.itemAmount) }}</td>
|
||||
<td class="px-1 text-right"><i v-if="result.stock < result.itemAmount"
|
||||
class="fas fa-exclamation-triangle mr-1 text-gray-500"></i>{{ Math.floor(result.stock /
|
||||
result.itemAmount) }}</td>
|
||||
<td class="pr-2 pl-4 whitespace-nowrap">
|
||||
<div class="inline-flex items-center">
|
||||
<div class="account-status" :class="result.accountStatus"></div>
|
||||
<div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div>
|
||||
</div>
|
||||
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{ t('You') }}</span>
|
||||
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{
|
||||
t('You') }}</span>
|
||||
</td>
|
||||
<td v-if="showSeller" class="px-2 whitespace-nowrap">
|
||||
<span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span>
|
||||
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName }}</span>
|
||||
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName
|
||||
}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@@ -154,7 +164,7 @@ function useBulkApi () {
|
||||
|
||||
const listedLazy = computed(() => {
|
||||
if (!requested) {
|
||||
;(async function () {
|
||||
; (async function () {
|
||||
try {
|
||||
requested = true
|
||||
_result.value = shallowReactive((await execBulkSearch(
|
||||
@@ -230,7 +240,7 @@ export default defineComponent({
|
||||
const have = _have ?? ((selectedCurr.value === 'xchgStable') ? ['divine'] : ['chaos'])
|
||||
const httpPostBody = createTradeRequest(props.filters, props.item, have)
|
||||
const httpGetQuery = { exchange: httpPostBody.query }
|
||||
return `https://${getTradeEndpoint()}/trade/exchange/${props.filters.trade.league}?q=${JSON.stringify(httpGetQuery)}`
|
||||
return `https://${getTradeEndpoint()}/trade2/exchange/poe2/${props.filters.trade.league}?q=${JSON.stringify(httpGetQuery)}`
|
||||
}
|
||||
|
||||
const { t } = useI18nNs('trade_result')
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
</div>
|
||||
<online-filter v-if="list" :by-time="true" :filters="filters" />
|
||||
<div class="flex-1"></div>
|
||||
<trade-links v-if="list"
|
||||
:get-link="makeTradeLink" />
|
||||
<trade-links v-if="list" :get-link="makeTradeLink" />
|
||||
</div>
|
||||
<div class="layout-column overflow-y-auto overflow-x-hidden">
|
||||
<table class="table-stripped w-full">
|
||||
@@ -46,21 +45,28 @@
|
||||
<td colspan="100" class="text-transparent">***</td>
|
||||
</tr>
|
||||
<tr v-else :key="result.id">
|
||||
<td class="px-2 whitespace-nowrap"><span :class="{ 'line-through': result.priceCurrency === 'exalted' }">{{ result.priceAmount }} {{ result.priceCurrency }}</span> <span v-if="result.listedTimes > 2" class="rounded px-1 text-gray-800 bg-gray-400 -mr-2"><span class="font-sans">×</span> {{ result.listedTimes }}</span><i v-else-if="!result.hasNote" class="fas fa-question" /></td>
|
||||
<td class="px-2 whitespace-nowrap"><span
|
||||
:class="{ 'line-through': result.priceCurrency === 'exalted' }">{{ result.priceAmount }} {{
|
||||
result.priceCurrency }}</span> <span v-if="result.listedTimes > 2"
|
||||
class="rounded px-1 text-gray-800 bg-gray-400 -mr-2"><span class="font-sans">×</span> {{
|
||||
result.listedTimes }}</span><i v-else-if="!result.hasNote" class="fas fa-question" /></td>
|
||||
<td v-if="item.stackSize" class="px-2 text-right">{{ result.stackSize }}</td>
|
||||
<td v-if="filters.itemLevel" class="px-2 whitespace-nowrap text-right">{{ result.itemLevel }}</td>
|
||||
<td v-if="item.category === 'Gem'" class="pl-2 whitespace-nowrap">{{ result.level }}</td>
|
||||
<td v-if="filters.quality || item.category === 'Gem'" class="px-2 whitespace-nowrap text-blue-400 text-right">{{ result.quality }}</td>
|
||||
<td v-if="filters.quality || item.category === 'Gem'"
|
||||
class="px-2 whitespace-nowrap text-blue-400 text-right">{{ result.quality }}</td>
|
||||
<td class="pr-2 pl-4 whitespace-nowrap">
|
||||
<div class="inline-flex items-center">
|
||||
<div class="account-status" :class="result.accountStatus"></div>
|
||||
<div class="ml-1 font-sans text-xs">{{ result.relativeDate }}</div>
|
||||
</div>
|
||||
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{ t('You') }}</span>
|
||||
<span v-if="!showSeller && result.isMine" class="rounded px-1 text-gray-800 bg-gray-400 ml-1">{{
|
||||
t('You') }}</span>
|
||||
</td>
|
||||
<td v-if="showSeller" class="px-2 whitespace-nowrap">
|
||||
<span v-if="result.isMine" class="rounded px-1 text-gray-800 bg-gray-400">{{ t('You') }}</span>
|
||||
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName }}</span>
|
||||
<span v-else class="font-sans text-xs">{{ showSeller === 'ign' ? result.ign : result.accountName
|
||||
}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@@ -222,8 +228,8 @@ export default defineComponent({
|
||||
|
||||
function makeTradeLink () {
|
||||
return (searchResult.value)
|
||||
? `https://${getTradeEndpoint()}/trade/search/${props.filters.trade.league}/${searchResult.value.id}`
|
||||
: `https://${getTradeEndpoint()}/trade/search/${props.filters.trade.league}?q=${JSON.stringify(createTradeRequest(props.filters, props.stats, props.item))}`
|
||||
? `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))}`
|
||||
}
|
||||
|
||||
const { t } = useI18nNs('trade_result')
|
||||
@@ -261,7 +267,7 @@ export default defineComponent({
|
||||
@apply bg-gray-800;
|
||||
@apply p-0 m-0;
|
||||
|
||||
& > div {
|
||||
&>div {
|
||||
@apply border-b border-gray-700;
|
||||
}
|
||||
}
|
||||
@@ -271,10 +277,14 @@ export default defineComponent({
|
||||
height: 0.375rem;
|
||||
border-radius: 100%;
|
||||
|
||||
&.online { /* */ }
|
||||
&.online {
|
||||
/* */
|
||||
}
|
||||
|
||||
&.offline {
|
||||
@apply bg-red-600;
|
||||
}
|
||||
|
||||
&.afk {
|
||||
@apply bg-orange-500;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { DateTime } from 'luxon'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { TradeResponse, Account, getTradeEndpoint, RATE_LIMIT_RULES, adjustRateLimits, tradeTag, preventQueueCreation } from './common'
|
||||
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'
|
||||
|
||||
interface TradeRequest { /* eslint-disable camelcase */
|
||||
engine: 'new'
|
||||
interface TradeRequest {
|
||||
/* eslint-disable camelcase */ engine: 'new'
|
||||
query: {
|
||||
status: { option: 'online' | 'onlineleague' | 'any' }
|
||||
have: string[]
|
||||
@@ -56,34 +64,42 @@ export interface PricingResult {
|
||||
|
||||
const cache = new Cache()
|
||||
|
||||
async function requestTradeResultList (body: TradeRequest, leagueId: string): Promise<SearchResult> {
|
||||
async function requestTradeResultList (
|
||||
body: TradeRequest,
|
||||
leagueId: string
|
||||
): Promise<SearchResult> {
|
||||
let data = cache.get<SearchResult>([body, leagueId])
|
||||
|
||||
if (!data) {
|
||||
preventQueueCreation([
|
||||
{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }
|
||||
])
|
||||
preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }])
|
||||
|
||||
await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE)
|
||||
|
||||
const response = await Host.proxy(`${getTradeEndpoint()}/api/trade/exchange/${leagueId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
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>
|
||||
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
|
||||
@@ -96,15 +112,19 @@ function toPricingResult (
|
||||
): PricingResult {
|
||||
return {
|
||||
id: result.id,
|
||||
relativeDate: DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ?? '',
|
||||
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),
|
||||
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')
|
||||
? result.listing.account.online.status === 'afk'
|
||||
? 'afk'
|
||||
: 'online'
|
||||
: 'offline'
|
||||
}
|
||||
}
|
||||
@@ -116,18 +136,27 @@ export interface BulkSearch {
|
||||
listed: PricingResult[]
|
||||
}
|
||||
|
||||
export function createTradeRequest (filters: ItemFilters, item: ParsedItem, have: string[]): TradeRequest {
|
||||
export function createTradeRequest (
|
||||
filters: ItemFilters,
|
||||
item: ParsedItem,
|
||||
have: string[]
|
||||
): TradeRequest {
|
||||
return {
|
||||
engine: 'new',
|
||||
query: {
|
||||
have: have,
|
||||
have,
|
||||
want: [tradeTag(item)!],
|
||||
status: {
|
||||
option: filters.trade.offline
|
||||
? 'any'
|
||||
: (filters.trade.onlineInLeague ? 'onlineleague' : 'online')
|
||||
: filters.trade.onlineInLeague
|
||||
? 'onlineleague'
|
||||
: 'online'
|
||||
},
|
||||
minimum: (filters.stackSize && !filters.stackSize.disabled) ? filters.stackSize.value : undefined
|
||||
minimum:
|
||||
filters.stackSize && !filters.stackSize.disabled
|
||||
? filters.stackSize.value
|
||||
: undefined
|
||||
// fulfillable: null
|
||||
},
|
||||
sort: { have: 'asc' }
|
||||
@@ -149,40 +178,46 @@ export async function execBulkSearch (
|
||||
)
|
||||
|
||||
const offer = 0
|
||||
const results = Object.values(query.result)
|
||||
.filter(result => result.listing.offers.length === 1)
|
||||
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 = (
|
||||
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))
|
||||
.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))
|
||||
.map((result) => toPricingResult(result, opts, offer))
|
||||
|
||||
const chaosIsLoaded = (
|
||||
const chaosIsLoaded =
|
||||
tradeTag === 'divine' &&
|
||||
resultsTag.length < results.length &&
|
||||
((results.length - resultsTag.length) >= SHOW_RESULTS || query.total <= API_FETCH_LIMIT)
|
||||
)
|
||||
(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)
|
||||
total: chaosIsLoaded
|
||||
? resultsTag.length
|
||||
: (query.total - (results.length - resultsTag.length)),
|
||||
listed: listed
|
||||
: query.total - (results.length - resultsTag.length),
|
||||
listed
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { ItemInfluence, ItemCategory, ParsedItem, ItemRarity } from '@/parser'
|
||||
import { ItemFilters, StatFilter, INTERNAL_TRADE_IDS, InternalTradeId } from '../filters/interfaces'
|
||||
import {
|
||||
ItemFilters,
|
||||
StatFilter,
|
||||
INTERNAL_TRADE_IDS,
|
||||
InternalTradeId
|
||||
} from '../filters/interfaces'
|
||||
import { setProperty as propSet } from 'dot-prop'
|
||||
import { DateTime } from 'luxon'
|
||||
import { Host } from '@/web/background/IPC'
|
||||
import { TradeResponse, Account, getTradeEndpoint, adjustRateLimits, RATE_LIMIT_RULES, preventQueueCreation } from './common'
|
||||
import {
|
||||
TradeResponse,
|
||||
Account,
|
||||
getTradeEndpoint,
|
||||
adjustRateLimits,
|
||||
RATE_LIMIT_RULES,
|
||||
preventQueueCreation
|
||||
} from './common'
|
||||
import { STAT_BY_REF } from '@/assets/data'
|
||||
import { RateLimiter } from './RateLimiter'
|
||||
import { ModifierType } from '@/parser/modifiers'
|
||||
@@ -62,11 +74,7 @@ const TOTAL_MODS_TEXT = {
|
||||
'# Empty Prefix Modifiers',
|
||||
'# Empty Suffix Modifiers'
|
||||
],
|
||||
TOTAL_MODIFIERS: [
|
||||
'# Modifiers',
|
||||
'# Prefix Modifiers',
|
||||
'# Suffix Modifiers'
|
||||
]
|
||||
TOTAL_MODIFIERS: ['# Modifiers', '# Prefix Modifiers', '# Suffix Modifiers']
|
||||
}
|
||||
|
||||
const INFLUENCE_PSEUDO_TEXT = {
|
||||
@@ -78,10 +86,16 @@ const INFLUENCE_PSEUDO_TEXT = {
|
||||
[ItemInfluence.Warlord]: 'Has Warlord Influence'
|
||||
}
|
||||
|
||||
interface FilterBoolean { option?: 'true' | 'false' }
|
||||
interface FilterRange { min?: number, max?: number }
|
||||
interface FilterBoolean {
|
||||
option?: 'true' | 'false'
|
||||
}
|
||||
interface FilterRange {
|
||||
min?: number
|
||||
max?: number
|
||||
}
|
||||
|
||||
interface TradeRequest { /* eslint-disable camelcase */
|
||||
interface TradeRequest {
|
||||
/* eslint-disable camelcase */
|
||||
query: {
|
||||
status: { option: 'online' | 'onlineleague' | 'any' }
|
||||
name?: string | { discriminator: string, option: string }
|
||||
@@ -207,10 +221,10 @@ interface FetchResult {
|
||||
properties?: Array<{
|
||||
values: [[string, number]]
|
||||
type:
|
||||
78 | // Corpse Level (Filled Coffin)
|
||||
30 | // Spawns a Level %0 Monster when Harvested
|
||||
6 | // Quality
|
||||
5 // Level
|
||||
| 78 // Corpse Level (Filled Coffin)
|
||||
| 30 // Spawns a Level %0 Monster when Harvested
|
||||
| 6 // Quality
|
||||
| 5 // Level
|
||||
}>
|
||||
note?: string
|
||||
}
|
||||
@@ -242,17 +256,21 @@ export interface PricingResult {
|
||||
ign: string
|
||||
}
|
||||
|
||||
export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], item: ParsedItem) {
|
||||
export function createTradeRequest (
|
||||
filters: ItemFilters,
|
||||
stats: StatFilter[],
|
||||
item: ParsedItem
|
||||
) {
|
||||
const body: TradeRequest = {
|
||||
query: {
|
||||
status: {
|
||||
option: filters.trade.offline
|
||||
? 'any'
|
||||
: (filters.trade.onlineInLeague ? 'onlineleague' : 'online')
|
||||
: filters.trade.onlineInLeague
|
||||
? 'onlineleague'
|
||||
: 'online'
|
||||
},
|
||||
stats: [
|
||||
{ type: 'and', filters: [] }
|
||||
],
|
||||
stats: [{ type: 'and', filters: [] }],
|
||||
filters: {}
|
||||
},
|
||||
sort: {
|
||||
@@ -262,20 +280,33 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
const { query } = body
|
||||
|
||||
if (filters.trade.currency) {
|
||||
propSet(query.filters, 'trade_filters.filters.price.option', filters.trade.currency)
|
||||
propSet(
|
||||
query.filters,
|
||||
'trade_filters.filters.price.option',
|
||||
filters.trade.currency
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.trade.collapseListings === 'api') {
|
||||
propSet(query.filters, 'trade_filters.filters.collapse.option', String(true))
|
||||
propSet(
|
||||
query.filters,
|
||||
'trade_filters.filters.collapse.option',
|
||||
String(true)
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.trade.listed) {
|
||||
propSet(query.filters, 'trade_filters.filters.indexed.option', filters.trade.listed)
|
||||
propSet(
|
||||
query.filters,
|
||||
'trade_filters.filters.indexed.option',
|
||||
filters.trade.listed
|
||||
)
|
||||
}
|
||||
|
||||
const activeSearch = (filters.searchRelaxed && !filters.searchRelaxed.disabled)
|
||||
? filters.searchRelaxed
|
||||
: filters.searchExact
|
||||
const activeSearch =
|
||||
filters.searchRelaxed && !filters.searchRelaxed.disabled
|
||||
? filters.searchRelaxed
|
||||
: filters.searchExact
|
||||
|
||||
if (activeSearch.nameTrade) {
|
||||
query.name = nameToQuery(activeSearch.nameTrade, filters)
|
||||
@@ -292,7 +323,11 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
if (filters.foil && !filters.foil.disabled) {
|
||||
propSet(query.filters, 'type_filters.filters.rarity.option', 'uniquefoil')
|
||||
} else if (filters.rarity) {
|
||||
propSet(query.filters, 'type_filters.filters.rarity.option', filters.rarity.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'type_filters.filters.rarity.option',
|
||||
filters.rarity.value
|
||||
)
|
||||
}
|
||||
|
||||
if (activeSearch.category) {
|
||||
@@ -305,85 +340,166 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
}
|
||||
|
||||
if (filters.corrupted?.value === false || filters.corrupted?.exact) {
|
||||
propSet(query.filters, 'misc_filters.filters.corrupted.option', String(filters.corrupted.value))
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.corrupted.option',
|
||||
String(filters.corrupted.value)
|
||||
)
|
||||
}
|
||||
if (filters.fractured?.value === false) {
|
||||
propSet(query.filters, 'misc_filters.filters.fractured_item.option', String(false))
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.fractured_item.option',
|
||||
String(false)
|
||||
)
|
||||
}
|
||||
if (filters.mirrored) {
|
||||
if (filters.mirrored.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.mirrored.option', String(false))
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.mirrored.option',
|
||||
String(false)
|
||||
)
|
||||
}
|
||||
} else if (
|
||||
item.rarity === ItemRarity.Normal ||
|
||||
item.rarity === ItemRarity.Magic ||
|
||||
item.rarity === ItemRarity.Rare
|
||||
) {
|
||||
propSet(query.filters, 'misc_filters.filters.mirrored.option', String(false))
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.mirrored.option',
|
||||
String(false)
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.gemLevel && !filters.gemLevel.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.gem_level.min', filters.gemLevel.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.gem_level.min',
|
||||
filters.gemLevel.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.quality && !filters.quality.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.quality.min', filters.quality.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.quality.min',
|
||||
filters.quality.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.itemLevel && !filters.itemLevel.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.ilvl.min', filters.itemLevel.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.ilvl.min',
|
||||
filters.itemLevel.value
|
||||
)
|
||||
if (filters.itemLevel.max) {
|
||||
propSet(query.filters, 'misc_filters.filters.ilvl.max', filters.itemLevel.max)
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.ilvl.max',
|
||||
filters.itemLevel.max
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.stackSize && !filters.stackSize.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.stack_size.min', filters.stackSize.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.stack_size.min',
|
||||
filters.stackSize.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.linkedSockets && !filters.linkedSockets.disabled) {
|
||||
propSet(query.filters, 'socket_filters.filters.links.min', filters.linkedSockets.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'socket_filters.filters.links.min',
|
||||
filters.linkedSockets.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.whiteSockets && !filters.whiteSockets.disabled) {
|
||||
propSet(query.filters, 'socket_filters.filters.sockets.w', filters.whiteSockets.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'socket_filters.filters.sockets.w',
|
||||
filters.whiteSockets.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.mapTier && !filters.mapTier.disabled) {
|
||||
propSet(query.filters, 'map_filters.filters.map_tier.min', filters.mapTier.value)
|
||||
propSet(query.filters, 'map_filters.filters.map_tier.max', filters.mapTier.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'map_filters.filters.map_tier.min',
|
||||
filters.mapTier.value
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'map_filters.filters.map_tier.max',
|
||||
filters.mapTier.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.mapBlighted) {
|
||||
if (filters.mapBlighted.value === 'Blighted') {
|
||||
propSet(query.filters, 'map_filters.filters.map_blighted.option', String(true))
|
||||
propSet(
|
||||
query.filters,
|
||||
'map_filters.filters.map_blighted.option',
|
||||
String(true)
|
||||
)
|
||||
} else if (filters.mapBlighted.value === 'Blight-ravaged') {
|
||||
propSet(query.filters, 'map_filters.filters.map_uberblighted.option', String(true))
|
||||
propSet(
|
||||
query.filters,
|
||||
'map_filters.filters.map_uberblighted.option',
|
||||
String(true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.unidentified && !filters.unidentified.disabled) {
|
||||
propSet(query.filters, 'misc_filters.filters.identified.option', String(false))
|
||||
propSet(
|
||||
query.filters,
|
||||
'misc_filters.filters.identified.option',
|
||||
String(false)
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.areaLevel && !filters.areaLevel.disabled) {
|
||||
propSet(query.filters, 'map_filters.filters.area_level.min', filters.areaLevel.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'map_filters.filters.area_level.min',
|
||||
filters.areaLevel.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.heistWingsRevealed && !filters.heistWingsRevealed.disabled) {
|
||||
propSet(query.filters, 'heist_filters.filters.heist_wings.min', filters.heistWingsRevealed.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'heist_filters.filters.heist_wings.min',
|
||||
filters.heistWingsRevealed.value
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.sentinelCharge && !filters.sentinelCharge.disabled) {
|
||||
propSet(query.filters, 'sentinel_filters.filters.sentinel_durability.min', filters.sentinelCharge.value)
|
||||
propSet(
|
||||
query.filters,
|
||||
'sentinel_filters.filters.sentinel_durability.min',
|
||||
filters.sentinelCharge.value
|
||||
)
|
||||
}
|
||||
|
||||
for (const stat of stats) {
|
||||
if (stat.tradeId[0] === 'item.has_empty_modifier') {
|
||||
const TARGET_ID = {
|
||||
CRAFTED_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.CRAFTED_MODIFIERS[stat.option!.value])!.trade.ids[ModifierType.Pseudo][0],
|
||||
EMPTY_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.EMPTY_MODIFIERS[stat.option!.value])!.trade.ids[ModifierType.Pseudo][0],
|
||||
TOTAL_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.TOTAL_MODIFIERS[0])!.trade.ids[ModifierType.Pseudo][0]
|
||||
CRAFTED_MODIFIERS: STAT_BY_REF(
|
||||
TOTAL_MODS_TEXT.CRAFTED_MODIFIERS[stat.option!.value]
|
||||
)!.trade.ids[ModifierType.Pseudo][0],
|
||||
EMPTY_MODIFIERS: STAT_BY_REF(
|
||||
TOTAL_MODS_TEXT.EMPTY_MODIFIERS[stat.option!.value]
|
||||
)!.trade.ids[ModifierType.Pseudo][0],
|
||||
TOTAL_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.TOTAL_MODIFIERS[0])!.trade
|
||||
.ids[ModifierType.Pseudo][0]
|
||||
}
|
||||
|
||||
query.stats.push({
|
||||
@@ -391,8 +507,16 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
value: { min: 1, max: 1 },
|
||||
disabled: stat.disabled,
|
||||
filters: [
|
||||
{ id: TARGET_ID.EMPTY_MODIFIERS, value: { min: 1, max: 1 }, disabled: stat.disabled },
|
||||
{ id: TARGET_ID.CRAFTED_MODIFIERS, value: { min: 1, max: undefined }, disabled: stat.disabled }
|
||||
{
|
||||
id: TARGET_ID.EMPTY_MODIFIERS,
|
||||
value: { min: 1, max: 1 },
|
||||
disabled: stat.disabled
|
||||
},
|
||||
{
|
||||
id: TARGET_ID.CRAFTED_MODIFIERS,
|
||||
value: { min: 1, max: undefined },
|
||||
disabled: stat.disabled
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -401,22 +525,31 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
value: { min: 1, max: 1 },
|
||||
disabled: stat.disabled,
|
||||
filters: [
|
||||
{ id: TARGET_ID.EMPTY_MODIFIERS, value: { min: 1, max: 1 }, disabled: stat.disabled },
|
||||
{ id: TARGET_ID.TOTAL_MODIFIERS, value: { min: 6, max: undefined }, disabled: stat.disabled }
|
||||
{
|
||||
id: TARGET_ID.EMPTY_MODIFIERS,
|
||||
value: { min: 1, max: 1 },
|
||||
disabled: stat.disabled
|
||||
},
|
||||
{
|
||||
id: TARGET_ID.TOTAL_MODIFIERS,
|
||||
value: { min: 6, max: undefined },
|
||||
disabled: stat.disabled
|
||||
}
|
||||
]
|
||||
})
|
||||
} else if ( // https://github.com/SnosMe/awakened-poe-trade/issues/758
|
||||
} else if (
|
||||
// https://github.com/SnosMe/awakened-poe-trade/issues/758
|
||||
item.category === ItemCategory.Flask &&
|
||||
stat.statRef === '#% increased Charge Recovery' &&
|
||||
!stats.some(s => s.statRef === '#% increased effect')
|
||||
!stats.some((s) => s.statRef === '#% increased effect')
|
||||
) {
|
||||
const reducedEffectId = STAT_BY_REF('#% increased effect')!.trade.ids[ModifierType.Explicit][0]
|
||||
const reducedEffectId = STAT_BY_REF('#% increased effect')!.trade.ids[
|
||||
ModifierType.Explicit
|
||||
][0]
|
||||
query.stats.push({
|
||||
type: 'not',
|
||||
disabled: stat.disabled,
|
||||
filters: [
|
||||
{ id: reducedEffectId, disabled: stat.disabled }
|
||||
]
|
||||
filters: [{ id: reducedEffectId, disabled: stat.disabled }]
|
||||
})
|
||||
}
|
||||
|
||||
@@ -425,53 +558,143 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
const input = stat.roll!
|
||||
switch (stat.tradeId[0] as InternalTradeId) {
|
||||
case 'item.base_percentile':
|
||||
propSet(query.filters, 'armour_filters.filters.base_defence_percentile.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.base_defence_percentile.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.base_defence_percentile.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.base_defence_percentile.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.armour':
|
||||
propSet(query.filters, 'armour_filters.filters.ar.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.ar.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ar.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ar.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.evasion_rating':
|
||||
propSet(query.filters, 'armour_filters.filters.ev.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.ev.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ev.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ev.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.energy_shield':
|
||||
propSet(query.filters, 'armour_filters.filters.es.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.es.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.es.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.es.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.ward':
|
||||
propSet(query.filters, 'armour_filters.filters.ward.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.ward.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ward.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.ward.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.block':
|
||||
propSet(query.filters, 'armour_filters.filters.block.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'armour_filters.filters.block.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.block.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'armour_filters.filters.block.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.total_dps':
|
||||
propSet(query.filters, 'weapon_filters.filters.dps.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'weapon_filters.filters.dps.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.dps.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.dps.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.physical_dps':
|
||||
propSet(query.filters, 'weapon_filters.filters.pdps.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'weapon_filters.filters.pdps.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.pdps.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.pdps.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.elemental_dps':
|
||||
propSet(query.filters, 'weapon_filters.filters.edps.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'weapon_filters.filters.edps.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.edps.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.edps.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.crit':
|
||||
propSet(query.filters, 'weapon_filters.filters.crit.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'weapon_filters.filters.crit.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.crit.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.crit.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
case 'item.aps':
|
||||
propSet(query.filters, 'weapon_filters.filters.aps.min', typeof input.min === 'number' ? input.min : undefined)
|
||||
propSet(query.filters, 'weapon_filters.filters.aps.max', typeof input.max === 'number' ? input.max : undefined)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.aps.min',
|
||||
typeof input.min === 'number' ? input.min : undefined
|
||||
)
|
||||
propSet(
|
||||
query.filters,
|
||||
'weapon_filters.filters.aps.max',
|
||||
typeof input.max === 'number' ? input.max : undefined
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
stats = stats.filter(stat => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any))
|
||||
stats = stats.filter(
|
||||
(stat) => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any)
|
||||
)
|
||||
if (filters.veiled) {
|
||||
for (const statRef of filters.veiled.statRefs) {
|
||||
stats.push({
|
||||
@@ -493,7 +716,9 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
text: undefined!,
|
||||
tag: undefined!,
|
||||
sources: undefined!,
|
||||
tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[ModifierType.Pseudo]
|
||||
tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[
|
||||
ModifierType.Pseudo
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -507,7 +732,7 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
type: 'count',
|
||||
value: { min: 1 },
|
||||
disabled: stat.disabled,
|
||||
filters: stat.tradeId.map(id => tradeIdToQuery(id, stat))
|
||||
filters: stat.tradeId.map((id) => tradeIdToQuery(id, stat))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -517,7 +742,10 @@ export function createTradeRequest (filters: ItemFilters, stats: StatFilter[], i
|
||||
|
||||
const cache = new Cache()
|
||||
|
||||
export async function requestTradeResultList (body: TradeRequest, leagueId: string): Promise<SearchResult> {
|
||||
export async function requestTradeResultList (
|
||||
body: TradeRequest,
|
||||
leagueId: string
|
||||
): Promise<SearchResult> {
|
||||
let data = cache.get<SearchResult>([body, leagueId])
|
||||
|
||||
if (!data) {
|
||||
@@ -528,24 +756,31 @@ export async function requestTradeResultList (body: TradeRequest, leagueId: stri
|
||||
|
||||
await RateLimiter.waitMulti(RATE_LIMIT_RULES.SEARCH)
|
||||
|
||||
const response = await Host.proxy(`${getTradeEndpoint()}/api/trade/search/${leagueId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
const response = await Host.proxy(
|
||||
`${getTradeEndpoint()}/api/trade2/search/${leagueId}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
}
|
||||
)
|
||||
adjustRateLimits(RATE_LIMIT_RULES.SEARCH, response.headers)
|
||||
|
||||
const _data = await response.json() as TradeResponse<SearchResult>
|
||||
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.SEARCH, ...RATE_LIMIT_RULES.FETCH))
|
||||
cache.set<SearchResult>(
|
||||
[body, leagueId],
|
||||
data,
|
||||
Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH)
|
||||
)
|
||||
}
|
||||
|
||||
return data
|
||||
@@ -561,36 +796,53 @@ export async function requestResults (
|
||||
if (!data) {
|
||||
await RateLimiter.waitMulti(RATE_LIMIT_RULES.FETCH)
|
||||
|
||||
const response = await Host.proxy(`${getTradeEndpoint()}/api/trade/fetch/${resultIds.join(',')}?query=${queryId}`)
|
||||
const response = await Host.proxy(
|
||||
`${getTradeEndpoint()}/api/trade2/fetch/${resultIds.join(',')}?query=${queryId}`
|
||||
)
|
||||
adjustRateLimits(RATE_LIMIT_RULES.FETCH, response.headers)
|
||||
|
||||
const _data = await response.json() as TradeResponse<{ result: Array<FetchResult | null> }>
|
||||
const _data = (await response.json()) as TradeResponse<{
|
||||
result: Array<FetchResult | null>
|
||||
}>
|
||||
if (_data.error) {
|
||||
throw new Error(_data.error.message)
|
||||
} else {
|
||||
data = _data.result.filter(res => res != null)
|
||||
data = _data.result.filter((res) => res != null)
|
||||
}
|
||||
|
||||
cache.set<FetchResult[]>(resultIds, data, Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH))
|
||||
cache.set<FetchResult[]>(
|
||||
resultIds,
|
||||
data,
|
||||
Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH)
|
||||
)
|
||||
}
|
||||
|
||||
return data.map<PricingResult>(result => {
|
||||
return data.map<PricingResult>((result) => {
|
||||
return {
|
||||
id: result.id,
|
||||
itemLevel: result.item.properties?.find(prop => prop.type === 78)?.values[0][0] ?? String(result.item.ilvl),
|
||||
itemLevel:
|
||||
result.item.properties?.find((prop) => prop.type === 78)
|
||||
?.values[0][0] ?? String(result.item.ilvl),
|
||||
stackSize: result.item.stackSize,
|
||||
corrupted: result.item.corrupted,
|
||||
quality: result.item.properties?.find(prop => prop.type === 6)?.values[0][0],
|
||||
level: result.item.properties?.find(prop => prop.type === 5)?.values[0][0],
|
||||
relativeDate: DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ?? '',
|
||||
quality: result.item.properties?.find((prop) => prop.type === 6)
|
||||
?.values[0][0],
|
||||
level: result.item.properties?.find((prop) => prop.type === 5)
|
||||
?.values[0][0],
|
||||
relativeDate:
|
||||
DateTime.fromISO(result.listing.indexed).toRelative({
|
||||
style: 'short'
|
||||
}) ?? '',
|
||||
priceAmount: result.listing.price?.amount ?? 0,
|
||||
priceCurrency: result.listing.price?.currency ?? 'no price',
|
||||
hasNote: result.item.note != null,
|
||||
isMine: (result.listing.account.name === opts.accountName),
|
||||
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')
|
||||
? result.listing.account.online.status === 'afk'
|
||||
? 'afk'
|
||||
: 'online'
|
||||
: 'offline'
|
||||
}
|
||||
})
|
||||
@@ -619,28 +871,26 @@ function tradeIdToQuery (id: string, stat: StatFilter) {
|
||||
if (stat.roll?.value === 100) {
|
||||
roll = undefined // stat semantic type is flag
|
||||
}
|
||||
// fixes "Cannot be Poisoned" from Essence
|
||||
// fixes "Cannot be Poisoned" from Essence
|
||||
} else if (id === 'explicit.stat_3835551335') {
|
||||
if (stat.roll?.value === 100) {
|
||||
roll = undefined // stat semantic type is flag
|
||||
}
|
||||
// fixes "Instant Recovery" on Flasks
|
||||
// fixes "Instant Recovery" on Flasks
|
||||
} else if (id.endsWith('stat_1526933524')) {
|
||||
if (stat.roll?.value === 100) {
|
||||
roll = undefined // stat semantic type is flag
|
||||
}
|
||||
// fixes Delve "Reservation Efficiency of Skills"
|
||||
// fixes Delve "Reservation Efficiency of Skills"
|
||||
} else if (id.endsWith('stat_1269219558')) {
|
||||
roll = { ...roll!, tradeInvert: !(roll!.tradeInvert) }
|
||||
roll = { ...roll!, tradeInvert: !roll!.tradeInvert }
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
value: {
|
||||
...getMinMax(roll),
|
||||
option: stat.option != null
|
||||
? stat.option.value
|
||||
: undefined
|
||||
option: stat.option != null ? stat.option.value : undefined
|
||||
},
|
||||
disabled: stat.disabled
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ export default defineComponent({
|
||||
: autoCurrency(trend.chaos)
|
||||
|
||||
return {
|
||||
price: price,
|
||||
price,
|
||||
change: deltaFromGraph(trend.graph),
|
||||
url: trend.url
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export default defineComponent({
|
||||
function select (info: BaseType) {
|
||||
const newItem: ParsedItem = {
|
||||
...props.item!,
|
||||
info: info
|
||||
info
|
||||
}
|
||||
ctx.emit('identify', newItem)
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :class="$style.podium" v-if="podiumVisible">
|
||||
<div v-for="i in [2, 4, 5, 3, 1]">
|
||||
<div v-for="patron in patrons[i - 1]" :key="patron.from"
|
||||
:class="[$style.rating, $style[`rating-${patron.style}`]]"
|
||||
>{{ patron.from }}{{ (patron.months > 1) ? ` x${patron.months}` : null }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="[$style.patronsHorizontal, { 'invisible': podiumVisible }]" :onMouseenter="showPodium">
|
||||
<div class="bg-gray-800 rounded p-1 justify-center text-center w-44 shrink-0 flex items-center">
|
||||
{{ t('settings.thank_you') }}
|
||||
</div>
|
||||
<div class="overflow-x-hidden whitespace-nowrap p-1 text-base">
|
||||
<span :class="$style.patronsLine">{{ patronsString[0] }}</span><br>
|
||||
<span :class="$style.patronsLine">{{ patronsString[1] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.window" class="grow layout-column" :onMouseenter="hidePodium">
|
||||
<AppTitleBar @close="cancel" :title="t('settings.title')" />
|
||||
<div class="flex grow min-h-0">
|
||||
<div class="pl-2 pt-2 bg-gray-900 flex flex-col gap-1" style="min-width: 10rem;">
|
||||
<template v-for="item of menuItems">
|
||||
<button v-if="item.type === 'menu-item'"
|
||||
@click="item.select" :class="[$style['menu-item'], { [$style['active']]: item.isSelected }]">{{ item.name }}</button>
|
||||
<div v-else
|
||||
class="border-b mx-2 border-gray-800" />
|
||||
</template>
|
||||
<button v-if="menuItems.length >= 4"
|
||||
:class="$style['quit-btn']" @click="quit">{{ t('app.quit') }}</button>
|
||||
<div class="text-gray-400 text-center mt-auto pr-3 pt-4 pb-12" style="max-width: fit-content; min-width: 100%;">
|
||||
<img class="mx-auto mb-1" src="/images/peepoLove2x.webp">
|
||||
{{ t('Support development on') }}<br> <a href="https://patreon.com/awakened_poe_trade" class="inline-flex mt-1" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a>
|
||||
</div>
|
||||
<div>
|
||||
<div :class="$style.podium" v-if="podiumVisible">
|
||||
<div v-for="i in [2, 4, 5, 3, 1]">
|
||||
<div v-for="patron in patrons[i - 1]" :key="patron.from"
|
||||
:class="[$style.rating, $style[`rating-${patron.style}`]]">{{ patron.from }}{{ (patron.months > 1) ? `
|
||||
x${patron.months}` : null }}</div>
|
||||
</div>
|
||||
<div class="text-gray-100 grow layout-column bg-gray-900">
|
||||
<div class="grow overflow-y-auto bg-gray-800 rounded-tl">
|
||||
<component v-if="configClone"
|
||||
:is="selectedComponent" :config="configClone" :configWidget="configWidget" />
|
||||
</div>
|
||||
<div :class="[$style.patronsHorizontal, { 'invisible': podiumVisible }]" :onMouseenter="showPodium">
|
||||
<div class="bg-gray-800 rounded p-1 justify-center text-center w-44 shrink-0 flex items-center">
|
||||
{{ t('settings.thank_you') }}
|
||||
</div>
|
||||
<div class="overflow-x-hidden whitespace-nowrap p-1 text-base">
|
||||
<span :class="$style.patronsLine">{{ patronsString[0] }}</span><br>
|
||||
<span :class="$style.patronsLine">{{ patronsString[1] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.window" class="grow layout-column" :onMouseenter="hidePodium">
|
||||
<ConversionWarningBanner />
|
||||
<AppTitleBar @close="cancel" :title="t('settings.title')" />
|
||||
<div class="flex grow min-h-0">
|
||||
<div class="pl-2 pt-2 bg-gray-900 flex flex-col gap-1" style="min-width: 10rem;">
|
||||
<template v-for="item of menuItems">
|
||||
<button v-if="item.type === 'menu-item'" @click="item.select"
|
||||
:class="[$style['menu-item'], { [$style['active']]: item.isSelected }]">{{ item.name }}</button>
|
||||
<div v-else class="border-b mx-2 border-gray-800" />
|
||||
</template>
|
||||
<button v-if="menuItems.length >= 4" :class="$style['quit-btn']" @click="quit">{{ t('app.quit') }}</button>
|
||||
<div class="text-gray-400 text-center mt-auto pr-3 pt-4 pb-12"
|
||||
style="max-width: fit-content; min-width: 100%;">
|
||||
<img class="mx-auto mb-1" src="/images/peepoLove2x.webp">
|
||||
{{ t('Support development on') }}<br> <a href="https://patreon.com/awakened_poe_trade"
|
||||
class="inline-flex mt-1" target="_blank"><img class="inline h-5" src="/images/Patreon.svg"></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t bg-gray-900 border-gray-600 p-2 flex justify-end gap-x-2">
|
||||
<button @click="save" class="px-3 bg-gray-800 rounded">{{ t('Save') }}</button>
|
||||
<button @click="cancel" class="px-3">{{ t('Cancel') }}</button>
|
||||
<div class="text-gray-100 grow layout-column bg-gray-900">
|
||||
<div class="grow overflow-y-auto bg-gray-800 rounded-tl">
|
||||
<component v-if="configClone" :is="selectedComponent" :config="configClone" :configWidget="configWidget" />
|
||||
</div>
|
||||
<div class="border-t bg-gray-900 border-gray-600 p-2 flex justify-end gap-x-2">
|
||||
<button @click="save" class="px-3 bg-gray-800 rounded">{{ t('Save') }}</button>
|
||||
<button @click="cancel" class="px-3">{{ t('Cancel') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -67,6 +67,7 @@ import SettingsMaps from '../map-check/settings-maps.vue'
|
||||
import SettingsStashSearch from '../stash-search/stash-search-editor.vue'
|
||||
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[] {
|
||||
let currentIndex = array.length
|
||||
@@ -87,7 +88,7 @@ function quit () {
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { AppTitleBar },
|
||||
components: { AppTitleBar, ConversionWarningBanner },
|
||||
props: {
|
||||
config: {
|
||||
type: Object as PropType<Widget>,
|
||||
@@ -218,13 +219,17 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
<style lang="postcss" module>
|
||||
.window {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0; left: 0; right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
max-width: 50rem;
|
||||
max-height: 38rem;
|
||||
overflow: hidden;
|
||||
@apply bg-gray-800;
|
||||
@apply rounded-b;
|
||||
|
||||
&:global {
|
||||
animation-name: slideInDown;
|
||||
animation-duration: 1s;
|
||||
@@ -262,10 +267,13 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
.patronsHorizontal {
|
||||
@apply bg-gray-900 p-1 rounded gap-1;
|
||||
position: absolute;
|
||||
top: 40rem; left: 0; right: 0;
|
||||
top: 40rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
max-width: 50rem;
|
||||
display: flex;
|
||||
|
||||
&:global {
|
||||
animation-name: slideInDown;
|
||||
animation-duration: 1s;
|
||||
@@ -273,10 +281,19 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
}
|
||||
|
||||
@keyframes slide {
|
||||
0% { transform: translate(0%, 0); }
|
||||
4% { transform: translate(0%, 0); }
|
||||
100% { transform: translate(-99%, 0); }
|
||||
0% {
|
||||
transform: translate(0%, 0);
|
||||
}
|
||||
|
||||
4% {
|
||||
transform: translate(0%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-99%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.patronsLine {
|
||||
display: inline-block;
|
||||
animation: slide 64s linear infinite;
|
||||
@@ -291,22 +308,40 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
@apply gap-4 p-4;
|
||||
|
||||
&:global {
|
||||
animation-name: fadeIn;
|
||||
animation-duration: 1.5s;
|
||||
}
|
||||
}
|
||||
.podium > div {
|
||||
|
||||
.podium>div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
min-width: min-content;
|
||||
}
|
||||
.podium > div:nth-child(1) { max-width: 18rem; }
|
||||
.podium > div:nth-child(2) { max-width: 16rem; }
|
||||
.podium > div:nth-child(3) { flex-direction: column; align-items: center; }
|
||||
.podium > div:nth-child(4) { max-width: 24rem; }
|
||||
.podium > div:nth-child(5) { max-width: 18rem; }
|
||||
|
||||
.podium>div:nth-child(1) {
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.podium>div:nth-child(2) {
|
||||
max-width: 16rem;
|
||||
}
|
||||
|
||||
.podium>div:nth-child(3) {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.podium>div:nth-child(4) {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.podium>div:nth-child(5) {
|
||||
max-width: 18rem;
|
||||
}
|
||||
|
||||
.rating {
|
||||
min-width: 3rem;
|
||||
@@ -314,30 +349,35 @@ function flatJoin<T, J> (arr: T[][], joinEl: () => J) {
|
||||
white-space: nowrap;
|
||||
@apply px-1 border;
|
||||
}
|
||||
|
||||
.rating-1 {
|
||||
background-color: rgb(0, 0, 0);
|
||||
color: rgb(190, 178, 135);
|
||||
border-color: currentColor;
|
||||
@apply text-base;
|
||||
}
|
||||
|
||||
.rating-2 {
|
||||
background-color: rgb(210, 178, 135);
|
||||
color: rgb(0, 0, 0);
|
||||
border-color: currentColor;
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.rating-3 {
|
||||
background-color: rgb(213, 159, 0);
|
||||
color: rgb(0, 0, 0);
|
||||
border-color: currentColor;
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.rating-4 {
|
||||
background-color: rgb(240, 90, 35);
|
||||
color: rgb(255, 255, 255);
|
||||
border-color: currentColor;
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
.rating-5 {
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgb(255, 0, 0);
|
||||
|
||||
@@ -2,24 +2,28 @@
|
||||
<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">Awakened PoE Trade</p>
|
||||
<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/SnosMe/awakened-poe-trade/releases" target="_blank">{{ t('app.release_notes') }}</a>
|
||||
<a class="border-b" href="https://github.com/SnosMe/awakened-poe-trade/issues" target="_blank">{{ t('app.report_bug') }}</a>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -39,7 +43,7 @@ function checkForUpdates () {
|
||||
}
|
||||
|
||||
function openDownloadPage () {
|
||||
window.open('https://snosme.github.io/awakened-poe-trade/download')
|
||||
window.open('https://github.com/Kvan7/exiled-exchange-2/releases')
|
||||
}
|
||||
|
||||
function quitAndInstall () {
|
||||
@@ -63,19 +67,50 @@ export default defineComponent({
|
||||
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') }
|
||||
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') }
|
||||
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') }
|
||||
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') }
|
||||
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 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')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
<div class="mb-2">
|
||||
<div class="flex-1 mb-1">{{ t(':poe_log_file') }}</div>
|
||||
<input v-model.trim="clientLog"
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/Grinding Gear Games/Path of Exile/logs/Client.txt">
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/Grinding Gear Games/Path of Exile 2/logs/Client.txt">
|
||||
</div>
|
||||
<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/production_Config.ini">
|
||||
class="rounded bg-gray-900 px-1 block w-full font-sans" placeholder="...?/My Games/Path of Exile 2/poe2_production_Config.ini">
|
||||
</div>
|
||||
<hr class="mb-4 mx-8 border-gray-700">
|
||||
<div class="mb-2">
|
||||
|
||||
22
testUpdate.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Remove ./renderer/dist if it exists
|
||||
rm -rf ./renderer/dist
|
||||
|
||||
# Remove ./main/dist if it exists
|
||||
rm -rf ./main/dist
|
||||
|
||||
cd ./renderer
|
||||
|
||||
npm install
|
||||
npm run make-index-files
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
cd ./main
|
||||
|
||||
npm install
|
||||
npm run build
|
||||
npm run package
|
||||
|
||||
|
||||