diff --git a/manifest.json b/manifest.json
index 2bb99379..e62735c4 100644
--- a/manifest.json
+++ b/manifest.json
@@ -19,7 +19,8 @@
"GameInfo",
"GameControl",
"Clipboard",
- "VideoCaptureSettings"
+ "VideoCaptureSettings",
+ "FileSystem"
],
"data": {
"start_window": "background",
@@ -136,6 +137,19 @@
"width": 1212,
"height": 699
}
+ },
+ "trade": {
+ "file": "dist/poe-overlay-overwolf/index.html",
+ "in_game_only": true,
+ "block_top_window_navigation": true,
+ "disable_restore_animation": true,
+ "disable_rightclick": true,
+ "transparent": true,
+ "resizable": true,
+ "size": {
+ "width": 1212,
+ "height": 699
+ }
}
},
"game_targeting": {
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 04b0567e..9c55a8c9 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -9,6 +9,7 @@
+
Could not match window with name: {{window.name}}
\ No newline at end of file
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 93b72b3c..8adc547c 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -6,6 +6,7 @@ import { EvaluateModule } from '@modules/evaluate/evaluate.module';
import { InspectModule } from '@modules/inspect/inspect.module';
import { MarketModule } from '@modules/market/market.module';
import { ReplayModule } from '@modules/replay/replay.module';
+import { TradeModule } from '@modules/trade/trade.module';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { AppErrorHandler } from './app-error-handler';
import { AppTranslationsLoader } from './app-translation-loader';
@@ -29,6 +30,7 @@ import { LayoutModule } from './layout/layout.module';
EvaluateModule,
MarketModule,
InspectModule,
+ TradeModule,
CommandsModule,
ReplayModule,
BookmarksModule
diff --git a/src/app/core/config/window-name.ts b/src/app/core/config/window-name.ts
index bec3646a..10885610 100644
--- a/src/app/core/config/window-name.ts
+++ b/src/app/core/config/window-name.ts
@@ -7,5 +7,6 @@ export enum WindowName {
Market = 'market',
Replay = 'replay',
Launcher = 'launcher',
- Annotation = 'annotation'
+ Annotation = 'annotation',
+ Trade = 'trade'
}
diff --git a/src/app/core/event/event-emitter.ts b/src/app/core/event/event-emitter.ts
index 5489d2b8..fea01c5e 100644
--- a/src/app/core/event/event-emitter.ts
+++ b/src/app/core/event/event-emitter.ts
@@ -11,6 +11,10 @@ export class EventEmitter {
private latest: TEvent;
private counter = 0;
+ constructor(first?: TEvent) {
+ this.latest = first;
+ }
+
public get(): TEvent {
return this.latest;
}
diff --git a/src/app/core/feature/feature-module.ts b/src/app/core/feature/feature-module.ts
index b163680f..580884ff 100644
--- a/src/app/core/feature/feature-module.ts
+++ b/src/app/core/feature/feature-module.ts
@@ -7,8 +7,9 @@ import { FeatureSettings } from './feature-settings';
export interface FeatureModule {
getConfig(): FeatureConfig;
getFeatures(): Feature[];
- onKeyPressed(hotkey: Hotkey, settings: TSettings): void;
- onSettingsChange(settings: TSettings): void;
- onGameEvent(event: GameEvent | InfoUpdatesEvent, settings: TSettings): void;
- onInfo(info: RunningGameInfo, settings: TSettings): void;
+ onKeyPressed?(hotkey: Hotkey, settings: TSettings): void;
+ onSettingsChange?(settings: TSettings): void;
+ onGameEvent?(event: GameEvent | InfoUpdatesEvent, settings: TSettings): void;
+ onInfo?(info: RunningGameInfo, settings: TSettings): void;
+ onLogLineAdd?(line: string): void;
}
diff --git a/src/app/core/odk/index.ts b/src/app/core/odk/index.ts
index d489a165..4cca0c95 100644
--- a/src/app/core/odk/index.ts
+++ b/src/app/core/odk/index.ts
@@ -1,3 +1,4 @@
+export * from './ow-file-listener';
export * from './ow-game-listener';
export * from './ow-games';
export * from './ow-games-events';
diff --git a/src/app/core/odk/ow-file-listener.ts b/src/app/core/odk/ow-file-listener.ts
new file mode 100644
index 00000000..b2245b56
--- /dev/null
+++ b/src/app/core/odk/ow-file-listener.ts
@@ -0,0 +1,52 @@
+
+export interface OWFileListenerDelegate {
+ onLineAdd(line: string): void;
+ onLineRead?(line: string): void;
+ onError(error?: string): void;
+}
+
+interface OWFileListenerResult extends overwolf.Result {
+ content: string;
+ info: string;
+}
+
+export class OWFileListener {
+ constructor(
+ private readonly id: string,
+ private readonly delegate: OWFileListenerDelegate) { }
+
+ public start(path: string, skipToEnd = true): void {
+ overwolf.io.listenOnFile(this.id, path, { skipToEnd }, this.onListenOnFile);
+ }
+
+ public stop(): void {
+ overwolf.io.stopFileListener(this.id);
+ }
+
+ private onListenOnFile = (event: OWFileListenerResult): void => {
+ if (!event.success || event.error) {
+ return this.delegate.onError(event.error);
+ }
+
+ if (!event.info?.length) {
+ return;
+ }
+
+ let info: {
+ isNew: boolean
+ };
+ try {
+ info = JSON.parse(event.info);
+ } catch (error) {
+ return this.delegate.onError(error);
+ }
+
+ if (info.isNew) {
+ this.delegate.onLineAdd(event.content);
+ } else {
+ if (this.delegate.onLineRead) {
+ this.delegate.onLineRead(event.content);
+ }
+ }
+ }
+}
diff --git a/src/app/layout/window/background-window/background-window.component.ts b/src/app/layout/window/background-window/background-window.component.ts
index 76279cfd..425ef388 100644
--- a/src/app/layout/window/background-window/background-window.component.ts
+++ b/src/app/layout/window/background-window/background-window.component.ts
@@ -7,7 +7,7 @@ import { EventSubscription } from '@app/event';
import { FeatureModule, FeatureSettings, FEATURE_MODULES } from '@app/feature';
import { FeatureSettingsService } from '@app/feature/feature-settings.service';
import { NotificationService } from '@app/notification';
-import { InfoUpdatesEvent, NewGameEvents, OnPressedEvent, OWGameClassId, OWGameListener, OWGamesEventsListener, OWHotkeysListener, OWWindow, OWWindowsListener, RunningGameInfo, WindowState, WindowStateChangedEvent } from '@app/odk';
+import { InfoUpdatesEvent, NewGameEvents, OnPressedEvent, OWFileListener, OWGameClassId, OWGameListener, OWGamesEventsListener, OWHotkeysListener, OWWindow, OWWindowsListener, RunningGameInfo, WindowState, WindowStateChangedEvent } from '@app/odk';
import { concat, forkJoin } from 'rxjs';
import { flatMap } from 'rxjs/operators';
import { AnnotationWindowService, LauncherWindowService, NotificationWindowService, SettingsWindowService } from '../../service';
@@ -20,10 +20,12 @@ import { AnnotationWindowService, LauncherWindowService, NotificationWindowServi
export class BackgroundWindowComponent implements OnInit, OnDestroy {
private settingsChange: EventSubscription;
private shouldQuit = false;
+
private readonly game: OWGameListener;
private readonly events: OWGamesEventsListener;
private readonly hotkeys: OWHotkeysListener;
private readonly windows: OWWindowsListener;
+ private readonly log: OWFileListener;
constructor(
@Inject(FEATURE_MODULES)
@@ -52,6 +54,10 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
this.windows = new OWWindowsListener({
onStateChange: this.onStateChange.bind(this)
});
+ this.log = new OWFileListener('log', {
+ onLineAdd: this.onLogLineAdd.bind(this),
+ onError: this.onLogError.bind(this),
+ });
}
public ngOnInit(): void {
@@ -61,7 +67,11 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
this.asset.load().subscribe(() => {
this.settingsChange = this.settings.change().on(settings => {
this.ngZone.run(() => {
- this.modules.forEach(module => module.onSettingsChange(settings));
+ this.modules.forEach(module => {
+ if (module.onSettingsChange) {
+ module.onSettingsChange(settings);
+ }
+ });
});
});
this.hotkeys.start();
@@ -84,13 +94,17 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
break;
default:
for (const module of this.modules) {
+ if (!module.onKeyPressed) {
+ continue;
+ }
for (const feature of module.getFeatures()) {
- if (feature.hotkey === event.name) {
- this.settings.get().subscribe(settings => {
- module.onKeyPressed(feature.hotkey, settings);
- });
- break;
+ if (feature.hotkey !== event.name) {
+ continue;
}
+ this.settings.get().subscribe(
+ settings => module.onKeyPressed(feature.hotkey, settings)
+ );
+ return;
}
}
}
@@ -104,6 +118,12 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
this.shouldQuit = false;
this.launcherWindow.close();
+ const path = info.executionPath.split('/');
+ path.pop();
+ const log = `${path.join('/')}/logs/Client.txt`;
+ console.log(log);
+ this.log.start(log);
+
forkJoin([
this.annotationWindow.open(info.width, info.height),
this.notificationWindow.open(info.width, info.height)
@@ -111,7 +131,11 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
flatMap(() => this.events.start()),
).subscribe(result => {
this.settings.get().subscribe(settings => {
- this.modules.forEach(module => module.onInfo(info, settings));
+ this.modules.forEach(module => {
+ if (module.onInfo) {
+ module.onInfo(info, settings);
+ }
+ });
});
if (!result) {
this.notification.show('event.start-error');
@@ -126,7 +150,11 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
this.events.stop();
this.settings.get().subscribe(settings => {
- this.modules.forEach(module => module.onInfo(info, settings));
+ this.modules.forEach(module => {
+ if (module.onInfo) {
+ module.onInfo(info, settings);
+ }
+ });
forkJoin([
this.settingsWindow.close(),
@@ -140,14 +168,26 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
private onInfoUpdates(event: InfoUpdatesEvent): void {
this.settings.get().subscribe(settings => {
- this.modules.forEach(module => module.onGameEvent(event, settings));
+ this.modules.forEach(module => {
+ if (module.onGameEvent) {
+ module.onGameEvent(event, settings);
+ }
+ });
});
}
private onNewEvents(event: NewGameEvents): void {
+ if (!event?.events?.length) {
+ return;
+ }
+
this.settings.get().subscribe(settings => {
this.modules.forEach(module => {
- event?.events?.forEach(e => module.onGameEvent(e, settings));
+ event.events.forEach(e => {
+ if (module.onGameEvent) {
+ module.onGameEvent(e, settings);
+ }
+ });
});
});
}
@@ -197,4 +237,16 @@ export class BackgroundWindowComponent implements OnInit, OnDestroy {
break;
}
}
+
+ public onLogLineAdd(line: string): void {
+ this.modules.forEach(module => {
+ if (module.onLogLineAdd) {
+ module.onLogLineAdd(line);
+ }
+ });
+ }
+
+ public onLogError(error: string): void {
+ console.error('An unexpected error occured while listening to the Client.txt file.', error);
+ }
}
diff --git a/src/app/modules/bookmarks/bookmarks.module.ts b/src/app/modules/bookmarks/bookmarks.module.ts
index fd3ffb2f..0d64a21b 100644
--- a/src/app/modules/bookmarks/bookmarks.module.ts
+++ b/src/app/modules/bookmarks/bookmarks.module.ts
@@ -93,8 +93,4 @@ export class BookmarksModule implements FeatureModule
throw new Error(`Hotkey: '${hotkey}' out of range.`);
}
}
-
- public onSettingsChange(): void { }
- public onGameEvent(): void { }
- public onInfo(): void { }
}
diff --git a/src/app/modules/commands/commands.module.ts b/src/app/modules/commands/commands.module.ts
index 646b3522..9bf251c2 100644
--- a/src/app/modules/commands/commands.module.ts
+++ b/src/app/modules/commands/commands.module.ts
@@ -92,8 +92,4 @@ export class CommandsModule implements FeatureModule {
throw new Error(`Hotkey: '${hotkey}' out of range.`);
}
}
-
- public onSettingsChange(): void { }
- public onGameEvent(): void { }
- public onInfo(): void { }
}
diff --git a/src/app/modules/evaluate/evaluate.module.ts b/src/app/modules/evaluate/evaluate.module.ts
index a0bce2fc..08122470 100644
--- a/src/app/modules/evaluate/evaluate.module.ts
+++ b/src/app/modules/evaluate/evaluate.module.ts
@@ -104,8 +104,4 @@ export class EvaluateModule implements FeatureModule {
throw new Error(`Hotkey: '${hotkey}' out of range.`);
}
}
-
- public onSettingsChange(): void { }
- public onGameEvent(): void { }
- public onInfo(): void { }
}
diff --git a/src/app/modules/inspect/inspect.module.ts b/src/app/modules/inspect/inspect.module.ts
index e5443650..f04187b5 100644
--- a/src/app/modules/inspect/inspect.module.ts
+++ b/src/app/modules/inspect/inspect.module.ts
@@ -72,8 +72,4 @@ export class InspectModule implements FeatureModule {
throw new Error(`Hotkey: '${hotkey}' out of range.`);
}
}
-
- public onSettingsChange(): void { }
- public onGameEvent(): void { }
- public onInfo(): void { }
}
diff --git a/src/app/modules/market/market.module.ts b/src/app/modules/market/market.module.ts
index a1b7145d..b9a9a259 100644
--- a/src/app/modules/market/market.module.ts
+++ b/src/app/modules/market/market.module.ts
@@ -81,8 +81,4 @@ export class MarketModule implements FeatureModule {
throw new Error(`Hotkey: '${hotkey}' out of range.`);
}
}
-
- public onSettingsChange(): void { }
- public onGameEvent(): void { }
- public onInfo(): void { }
}
diff --git a/src/app/modules/replay/replay.module.ts b/src/app/modules/replay/replay.module.ts
index aca79719..ece922d1 100644
--- a/src/app/modules/replay/replay.module.ts
+++ b/src/app/modules/replay/replay.module.ts
@@ -46,8 +46,6 @@ export class ReplayModule implements FeatureModule {
return features;
}
- public onKeyPressed(): void { }
-
public onSettingsChange(settings: ReplayFeatureSettings): void {
const shouldCapture = settings.replayCaptureDeath || settings.replayCaptureKill;
if (shouldCapture !== this.shouldCapture) {
diff --git a/src/app/modules/trade/component/index.ts b/src/app/modules/trade/component/index.ts
new file mode 100644
index 00000000..022d6053
--- /dev/null
+++ b/src/app/modules/trade/component/index.ts
@@ -0,0 +1,2 @@
+export * from './trade-settings/trade-settings.component';
+
diff --git a/src/app/modules/trade/component/trade-settings/trade-settings.component.html b/src/app/modules/trade/component/trade-settings/trade-settings.component.html
new file mode 100644
index 00000000..6f46f429
--- /dev/null
+++ b/src/app/modules/trade/component/trade-settings/trade-settings.component.html
@@ -0,0 +1 @@
+trade-settings works!
diff --git a/src/app/modules/trade/component/trade-settings/trade-settings.component.scss b/src/app/modules/trade/component/trade-settings/trade-settings.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/modules/trade/component/trade-settings/trade-settings.component.ts b/src/app/modules/trade/component/trade-settings/trade-settings.component.ts
new file mode 100644
index 00000000..de4be9a0
--- /dev/null
+++ b/src/app/modules/trade/component/trade-settings/trade-settings.component.ts
@@ -0,0 +1,13 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { FeatureSettingsComponent } from '@app/feature';
+import { TradeFeatureSettings } from '@modules/trade/trade-feature-settings';
+
+@Component({
+ selector: 'app-trade-settings',
+ templateUrl: './trade-settings.component.html',
+ styleUrls: ['./trade-settings.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TradeSettingsComponent extends FeatureSettingsComponent {
+ public load(): void { }
+}
diff --git a/src/app/modules/trade/service/index.ts b/src/app/modules/trade/service/index.ts
new file mode 100644
index 00000000..025b03cf
--- /dev/null
+++ b/src/app/modules/trade/service/index.ts
@@ -0,0 +1,2 @@
+export * from './trade-window.service';
+export * from './trade.service';
diff --git a/src/app/modules/trade/service/trade-window.service.ts b/src/app/modules/trade/service/trade-window.service.ts
new file mode 100644
index 00000000..f57a721c
--- /dev/null
+++ b/src/app/modules/trade/service/trade-window.service.ts
@@ -0,0 +1,44 @@
+import { Injectable } from '@angular/core';
+import { WindowName } from '@app/config';
+import { EventEmitter } from '@app/event';
+import { OWWindow } from '@app/odk';
+import { ProcessStorageService } from '@app/storage';
+import { TradeChatOffer, TradeChatRequest } from '@shared/module/poe/trade/chat';
+import { Observable } from 'rxjs';
+
+const WINDOW_DATA_KEY = 'TRADE_WINDOW_DATA';
+1;
+export interface TradeWindowData {
+ offers: TradeChatOffer[];
+ requests: TradeChatRequest[];
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TradeWindowService {
+ private readonly window: OWWindow;
+
+ constructor(private readonly storage: ProcessStorageService) {
+ this.window = new OWWindow(WindowName.Trade);
+ }
+
+ public get data$(): EventEmitter {
+ return this.storage.get(WINDOW_DATA_KEY, () => new EventEmitter({
+ offers: [],
+ requests: []
+ }));
+ }
+
+ public restore(): Observable {
+ return this.window.restore();
+ }
+
+ public close(): Observable {
+ return this.window.close();
+ }
+
+ public minimize(): Observable {
+ return this.window.minimize();
+ }
+}
diff --git a/src/app/modules/trade/service/trade.service.ts b/src/app/modules/trade/service/trade.service.ts
new file mode 100644
index 00000000..2d0e02a3
--- /dev/null
+++ b/src/app/modules/trade/service/trade.service.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@angular/core';
+import { TradeChatParserService } from '@shared/module/poe/trade/chat';
+import { Observable } from 'rxjs';
+import { TradeWindowService } from './trade-window.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TradeService {
+
+ constructor(
+ private readonly window: TradeWindowService,
+ private readonly parser: TradeChatParserService) { }
+
+ public onLogLineAdd(line: string): Observable {
+ const data = this.window.data$.get();
+
+ const { offer, request } = this.parser.parse(line);
+ if (offer || request) {
+ if (offer) {
+ data.offers.push(offer);
+ }
+ if (request) {
+ data.requests.push(request);
+ }
+ this.window.data$.next(data);
+ }
+
+ if (data.offers.length || data.requests.length) {
+ return this.window.restore();
+ }
+ return this.window.close();
+ }
+}
diff --git a/src/app/modules/trade/trade-feature-settings.ts b/src/app/modules/trade/trade-feature-settings.ts
new file mode 100644
index 00000000..bb6f220f
--- /dev/null
+++ b/src/app/modules/trade/trade-feature-settings.ts
@@ -0,0 +1,5 @@
+import { FeatureSettings } from '@app/feature';
+
+export interface TradeFeatureSettings extends FeatureSettings {
+ todo?: boolean;
+}
diff --git a/src/app/modules/trade/trade.module.ts b/src/app/modules/trade/trade.module.ts
new file mode 100644
index 00000000..10fa7508
--- /dev/null
+++ b/src/app/modules/trade/trade.module.ts
@@ -0,0 +1,39 @@
+import { NgModule } from '@angular/core';
+import { Feature, FeatureConfig, FeatureModule, FEATURE_MODULES } from '@app/feature';
+import { SharedModule } from '@shared/shared.module';
+import { TradeSettingsComponent } from './component';
+import { TradeService } from './service';
+import { TradeFeatureSettings } from './trade-feature-settings';
+import { TradeWindowComponent } from './window';
+
+@NgModule({
+ providers: [{ provide: FEATURE_MODULES, useClass: TradeModule, multi: true }],
+ declarations: [
+ TradeSettingsComponent,
+ TradeWindowComponent,
+ ],
+ exports: [TradeWindowComponent],
+ imports: [SharedModule]
+})
+export class TradeModule implements FeatureModule {
+ constructor(private readonly trade: TradeService) { }
+
+ public getConfig(): FeatureConfig {
+ const config: FeatureConfig = {
+ name: 'trade.name',
+ component: TradeSettingsComponent,
+ default: {
+ }
+ };
+ return config;
+ }
+
+ public getFeatures(): Feature[] {
+ const features: Feature[] = [];
+ return features;
+ }
+
+ public onLogLineAdd(line: string): void {
+ this.trade.onLogLineAdd(line).subscribe();
+ }
+}
diff --git a/src/app/modules/trade/window/index.ts b/src/app/modules/trade/window/index.ts
new file mode 100644
index 00000000..8c285e32
--- /dev/null
+++ b/src/app/modules/trade/window/index.ts
@@ -0,0 +1 @@
+export * from './trade-window/trade-window.component';
diff --git a/src/app/modules/trade/window/trade-window/trade-window.component.html b/src/app/modules/trade/window/trade-window/trade-window.component.html
new file mode 100644
index 00000000..9bb0910a
--- /dev/null
+++ b/src/app/modules/trade/window/trade-window/trade-window.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ {{data | json}}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/modules/trade/window/trade-window/trade-window.component.scss b/src/app/modules/trade/window/trade-window/trade-window.component.scss
new file mode 100644
index 00000000..f5659313
--- /dev/null
+++ b/src/app/modules/trade/window/trade-window/trade-window.component.scss
@@ -0,0 +1,3 @@
+.content {
+ color: #fff;
+}
diff --git a/src/app/modules/trade/window/trade-window/trade-window.component.ts b/src/app/modules/trade/window/trade-window/trade-window.component.ts
new file mode 100644
index 00000000..6a3e0b96
--- /dev/null
+++ b/src/app/modules/trade/window/trade-window/trade-window.component.ts
@@ -0,0 +1,31 @@
+import { ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit } from '@angular/core';
+import { EventSubscription } from '@app/event';
+import { TradeWindowData, TradeWindowService } from '@modules/trade/service';
+import { BehaviorSubject } from 'rxjs';
+
+@Component({
+ selector: 'app-trade-window',
+ templateUrl: './trade-window.component.html',
+ styleUrls: ['./trade-window.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TradeWindowComponent implements OnInit, OnDestroy {
+ private subscription: EventSubscription;
+
+ public data$ = new BehaviorSubject(null);
+
+ constructor(
+ private readonly window: TradeWindowService,
+ private readonly ngZone: NgZone) { }
+
+ public ngOnInit(): void {
+ this.data$.next(this.window.data$.get());
+ this.subscription = this.window.data$.on(data => this.ngZone.run(() => {
+ this.data$.next(data);
+ }));
+ }
+
+ public ngOnDestroy(): void {
+ this.subscription?.unsubscribe();
+ }
+}
diff --git a/src/app/shared/module/poe/trade/chat/index.ts b/src/app/shared/module/poe/trade/chat/index.ts
new file mode 100644
index 00000000..b0cdd3f2
--- /dev/null
+++ b/src/app/shared/module/poe/trade/chat/index.ts
@@ -0,0 +1,2 @@
+export * from './trade-chat';
+export * from './trade-chat-parser.service';
diff --git a/src/app/shared/module/poe/trade/chat/trade-chat-parser.service.ts b/src/app/shared/module/poe/trade/chat/trade-chat-parser.service.ts
new file mode 100644
index 00000000..09748310
--- /dev/null
+++ b/src/app/shared/module/poe/trade/chat/trade-chat-parser.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+import { TradeChatParseResult } from './trade-chat';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TradeChatParserService {
+ public parse(line: string): TradeChatParseResult {
+ // TODO: @Hyve
+ return {
+ offer: {
+ item: '123',
+ seller: line
+ }
+ };
+ }
+}
diff --git a/src/app/shared/module/poe/trade/chat/trade-chat.ts b/src/app/shared/module/poe/trade/chat/trade-chat.ts
new file mode 100644
index 00000000..fdd025a1
--- /dev/null
+++ b/src/app/shared/module/poe/trade/chat/trade-chat.ts
@@ -0,0 +1,14 @@
+export interface TradeChatOffer {
+ seller: string;
+ item: string;
+}
+
+export interface TradeChatRequest {
+ buyer: string;
+ item: string;
+}
+
+export interface TradeChatParseResult {
+ offer?: TradeChatOffer;
+ request?: TradeChatRequest;
+}