@audiowalk/sdk 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/player/media-controls.d.ts +9 -0
- package/dist/player/media-controls.d.ts.map +1 -0
- package/dist/player/media-controls.js +54 -0
- package/dist/player/player-controller.d.ts +21 -8
- package/dist/player/player-controller.d.ts.map +1 -1
- package/dist/player/player-controller.js +78 -59
- package/dist/storage/local-storage.d.ts +2 -0
- package/dist/storage/local-storage.d.ts.map +1 -1
- package/dist/storage/local-storage.js +11 -1
- package/dist/story/story-controller.d.ts.map +1 -1
- package/dist/story/story-controller.js +6 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PlayerController } from "./player-controller";
|
|
2
|
+
export declare class MediaControlsController {
|
|
3
|
+
private playerDetachEvent;
|
|
4
|
+
setMetadata(metadata: MediaMetadataInit): void;
|
|
5
|
+
attachPlayer(player: PlayerController): void;
|
|
6
|
+
detachPlayer(): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=media-controls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-controls.d.ts","sourceRoot":"","sources":["../../src/player/media-controls.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,iBAAiB,CAAuB;IAEhD,WAAW,CAAC,QAAQ,EAAE,iBAAiB;IAMvC,YAAY,CAAC,MAAM,EAAE,gBAAgB;IAoCrC,YAAY;IASZ,KAAK;CAMN"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { combineLatest, Subject, takeUntil } from "rxjs";
|
|
2
|
+
export class MediaControlsController {
|
|
3
|
+
playerDetachEvent = new Subject();
|
|
4
|
+
setMetadata(metadata) {
|
|
5
|
+
navigator.mediaSession.metadata = new MediaMetadata(metadata);
|
|
6
|
+
navigator.mediaSession.playbackState = "paused";
|
|
7
|
+
navigator.mediaSession.setActionHandler("play", () => console.log("play"));
|
|
8
|
+
}
|
|
9
|
+
attachPlayer(player) {
|
|
10
|
+
this.detachPlayer();
|
|
11
|
+
navigator.mediaSession.setActionHandler("play", () => player.play());
|
|
12
|
+
navigator.mediaSession.setActionHandler("pause", () => player.pause());
|
|
13
|
+
navigator.mediaSession.setActionHandler("seekbackward", () => player.back());
|
|
14
|
+
navigator.mediaSession.setActionHandler("seekforward", () => player.forward());
|
|
15
|
+
navigator.mediaSession.setActionHandler("previoustrack", () => player.back());
|
|
16
|
+
navigator.mediaSession.setActionHandler("nexttrack", () => player.forward());
|
|
17
|
+
navigator.mediaSession.setActionHandler("seekto", (details) => {
|
|
18
|
+
// The fastSeek dictionary member will be true if the seek action is being called
|
|
19
|
+
// multiple times as part of a sequence and this is not the last call in that sequence.
|
|
20
|
+
if (details.fastSeek !== true && details.seekTime !== undefined)
|
|
21
|
+
player.seekTo(details.seekTime);
|
|
22
|
+
});
|
|
23
|
+
player.status.pipe(takeUntil(this.playerDetachEvent)).subscribe((status) => {
|
|
24
|
+
switch (status) {
|
|
25
|
+
case "playing":
|
|
26
|
+
navigator.mediaSession.playbackState = "playing";
|
|
27
|
+
break;
|
|
28
|
+
case "paused":
|
|
29
|
+
navigator.mediaSession.playbackState = "paused";
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
combineLatest([player.currentTime, player.totalTime])
|
|
34
|
+
.pipe(takeUntil(this.playerDetachEvent))
|
|
35
|
+
.subscribe(([currentTime, totalTime]) => {
|
|
36
|
+
navigator.mediaSession.setPositionState({
|
|
37
|
+
duration: Number.isNaN(totalTime) ? 0 : totalTime,
|
|
38
|
+
position: currentTime,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
detachPlayer() {
|
|
43
|
+
this.playerDetachEvent.next();
|
|
44
|
+
navigator.mediaSession.setActionHandler("play", null);
|
|
45
|
+
navigator.mediaSession.setActionHandler("pause", null);
|
|
46
|
+
navigator.mediaSession.setActionHandler("previoustrack", null);
|
|
47
|
+
navigator.mediaSession.setActionHandler("nexttrack", null);
|
|
48
|
+
}
|
|
49
|
+
clear() {
|
|
50
|
+
this.detachPlayer();
|
|
51
|
+
navigator.mediaSession.playbackState = "none";
|
|
52
|
+
navigator.mediaSession.metadata = null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { BehaviorSubject, Subject } from "rxjs";
|
|
2
2
|
export interface PlayerControllerOptions {
|
|
3
|
-
playOnInit?: boolean;
|
|
4
3
|
autoSave?: boolean;
|
|
4
|
+
audioElement?: HTMLAudioElement;
|
|
5
|
+
loop?: boolean;
|
|
6
|
+
fadeIn?: boolean;
|
|
7
|
+
fadeOut?: boolean;
|
|
8
|
+
fadeInterval?: number;
|
|
5
9
|
}
|
|
6
10
|
export declare enum PlayerStatus {
|
|
7
11
|
"playing" = "playing",
|
|
@@ -9,8 +13,6 @@ export declare enum PlayerStatus {
|
|
|
9
13
|
"ended" = "ended"
|
|
10
14
|
}
|
|
11
15
|
export declare class PlayerController {
|
|
12
|
-
private readonly playerElement;
|
|
13
|
-
private options;
|
|
14
16
|
readonly onPlay: Subject<void>;
|
|
15
17
|
readonly onPause: Subject<void>;
|
|
16
18
|
readonly onStop: Subject<void>;
|
|
@@ -20,13 +22,24 @@ export declare class PlayerController {
|
|
|
20
22
|
private trackId;
|
|
21
23
|
readonly status: BehaviorSubject<PlayerStatus | null>;
|
|
22
24
|
readonly playing: import("rxjs").Observable<boolean>;
|
|
25
|
+
private playerElement;
|
|
23
26
|
private localStorage;
|
|
24
|
-
|
|
27
|
+
private options;
|
|
28
|
+
private defaultOptions;
|
|
29
|
+
private destroyEvent;
|
|
30
|
+
private fadeIntervalSubscription?;
|
|
31
|
+
private fadePromiseResolve?;
|
|
32
|
+
constructor(trackId: string, trackUrl: string, options?: PlayerControllerOptions);
|
|
25
33
|
open(id: string, file: string): Promise<void>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
destroy(): Promise<void>;
|
|
35
|
+
play(options?: {
|
|
36
|
+
fadeIn?: boolean;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
fadeIn(): Promise<void>;
|
|
39
|
+
fadeOut(): Promise<void>;
|
|
40
|
+
pause(options?: {
|
|
41
|
+
fadeIn?: boolean;
|
|
42
|
+
}): Promise<void>;
|
|
30
43
|
seekTo(seconds: number): void;
|
|
31
44
|
back(seconds?: number): void;
|
|
32
45
|
forward(seconds?: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player-controller.d.ts","sourceRoot":"","sources":["../../src/player/player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAsB,OAAO,
|
|
1
|
+
{"version":3,"file":"player-controller.d.ts","sourceRoot":"","sources":["../../src/player/player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAsB,OAAO,EAA8B,MAAM,MAAM,CAAC;AAGhG,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,oBAAY,YAAY;IACtB,SAAS,YAAY;IACrB,QAAQ,WAAW;IACnB,OAAO,UAAU;CAClB;AAED,qBAAa,gBAAgB;IAC3B,SAAgB,MAAM,gBAAuB;IAC7C,SAAgB,OAAO,gBAAuB;IAC9C,SAAgB,MAAM,gBAAuB;IAE7C,SAAgB,WAAW,0BAAkC;IAC7D,SAAgB,SAAS,0BAAkC;IAC3D,SAAgB,QAAQ,oCAEtB;IAEF,OAAO,CAAC,OAAO,CAAuB;IAEtC,SAAgB,MAAM,uCAAkD;IACxE,SAAgB,OAAO,qCAA2D;IAElF,OAAO,CAAC,aAAa,CAAmB;IAExC,OAAO,CAAC,YAAY,CAA0C;IAE9D,OAAO,CAAC,OAAO,CAAoF;IACnG,OAAO,CAAC,cAAc,CAEpB;IAEF,OAAO,CAAC,YAAY,CAAuB;IAE3C,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,OAAO,CAAC,kBAAkB,CAAC,CAAa;gBAE5B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;IA8D9E,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQ7B,OAAO;IAQP,IAAI,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO;IASvC,MAAM;IAoBN,OAAO;IAmBP,KAAK,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO;IAQ9C,MAAM,CAAC,OAAO,EAAE,MAAM;IAOtB,IAAI,CAAC,OAAO,GAAE,MAAW;IAOzB,OAAO,CAAC,OAAO,GAAE,MAAW;YAQd,YAAY;IAK1B,OAAO,CAAC,GAAG;CAOZ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BehaviorSubject, combineLatest, map, Subject } from "rxjs";
|
|
1
|
+
import { BehaviorSubject, combineLatest, map, Subject, take, takeUntil, takeWhile } from "rxjs";
|
|
2
2
|
import { LocalStorage } from "../storage/local-storage";
|
|
3
3
|
export var PlayerStatus;
|
|
4
4
|
(function (PlayerStatus) {
|
|
@@ -7,8 +7,6 @@ export var PlayerStatus;
|
|
|
7
7
|
PlayerStatus["ended"] = "ended";
|
|
8
8
|
})(PlayerStatus || (PlayerStatus = {}));
|
|
9
9
|
export class PlayerController {
|
|
10
|
-
playerElement;
|
|
11
|
-
options;
|
|
12
10
|
onPlay = new Subject();
|
|
13
11
|
onPause = new Subject();
|
|
14
12
|
onStop = new Subject();
|
|
@@ -18,36 +16,18 @@ export class PlayerController {
|
|
|
18
16
|
trackId = null;
|
|
19
17
|
status = new BehaviorSubject(null);
|
|
20
18
|
playing = this.status.pipe(map((status) => status === "playing"));
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// multiple times as part of a sequence and this is not the last call in that sequence.
|
|
34
|
-
if (details.fastSeek !== true && details.seekTime !== undefined)
|
|
35
|
-
this.seekTo(details.seekTime);
|
|
36
|
-
});
|
|
37
|
-
this.status.subscribe((status) => {
|
|
38
|
-
switch (status) {
|
|
39
|
-
case "playing":
|
|
40
|
-
navigator.mediaSession.playbackState = "playing";
|
|
41
|
-
break;
|
|
42
|
-
case "paused":
|
|
43
|
-
navigator.mediaSession.playbackState = "paused";
|
|
44
|
-
break;
|
|
45
|
-
default:
|
|
46
|
-
case "ended":
|
|
47
|
-
navigator.mediaSession.playbackState = "none";
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
19
|
+
playerElement;
|
|
20
|
+
localStorage = new LocalStorage({ prefix: "player" });
|
|
21
|
+
options;
|
|
22
|
+
defaultOptions = {
|
|
23
|
+
fadeInterval: 2000,
|
|
24
|
+
};
|
|
25
|
+
destroyEvent = new Subject();
|
|
26
|
+
fadeIntervalSubscription;
|
|
27
|
+
fadePromiseResolve;
|
|
28
|
+
constructor(trackId, trackUrl, options = {}) {
|
|
29
|
+
this.options = { ...this.defaultOptions, ...options };
|
|
30
|
+
this.playerElement = this.options.audioElement ?? new Audio();
|
|
51
31
|
this.playerElement.addEventListener("play", () => {
|
|
52
32
|
this.onPlay.next();
|
|
53
33
|
this.status.next(PlayerStatus.playing);
|
|
@@ -66,19 +46,30 @@ export class PlayerController {
|
|
|
66
46
|
}
|
|
67
47
|
});
|
|
68
48
|
this.playerElement.addEventListener("timeupdate", () => {
|
|
69
|
-
navigator.mediaSession.setPositionState({
|
|
70
|
-
duration: Number.isNaN(this.playerElement.duration) ? 0 : this.playerElement.duration,
|
|
71
|
-
playbackRate: this.playerElement.playbackRate,
|
|
72
|
-
position: this.playerElement.currentTime,
|
|
73
|
-
});
|
|
74
49
|
this.currentTime.next(this.playerElement.currentTime);
|
|
75
50
|
if (this.playerElement.duration) {
|
|
76
51
|
this.totalTime.next(this.playerElement.duration);
|
|
77
52
|
}
|
|
78
|
-
if (this.options.autoSave) {
|
|
79
|
-
this.savePosition(this.playerElement.currentTime);
|
|
80
|
-
}
|
|
81
53
|
});
|
|
54
|
+
if (this.options.autoSave) {
|
|
55
|
+
this.currentTime.pipe(takeUntil(this.destroyEvent)).subscribe((currentTime) => this.savePosition(currentTime));
|
|
56
|
+
}
|
|
57
|
+
if (this.options.fadeOut && !this.options.loop) {
|
|
58
|
+
combineLatest([this.currentTime, this.totalTime])
|
|
59
|
+
.pipe(takeUntil(this.destroyEvent))
|
|
60
|
+
.pipe(takeWhile(([currentTime, totalTime]) => totalTime - currentTime < this.options.fadeInterval), take(1))
|
|
61
|
+
.subscribe(async (currentTime) => {
|
|
62
|
+
this.fadeOut();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (this.options.loop) {
|
|
66
|
+
this.onStop.pipe(takeUntil(this.destroyEvent)).subscribe(() => {
|
|
67
|
+
this.playerElement.currentTime = 0;
|
|
68
|
+
this.playerElement.play();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
this.open(trackId, trackUrl);
|
|
72
|
+
this.log("Initialized player", this.playerElement);
|
|
82
73
|
}
|
|
83
74
|
async open(id, file) {
|
|
84
75
|
this.trackId = id;
|
|
@@ -86,33 +77,61 @@ export class PlayerController {
|
|
|
86
77
|
const position = await this.localStorage.get(`progress-${this.trackId}`, (value) => typeof value === "number");
|
|
87
78
|
if (position && this.options.autoSave)
|
|
88
79
|
this.playerElement.currentTime = position;
|
|
89
|
-
if (this.options.playOnInit)
|
|
90
|
-
await this.playerElement.play();
|
|
91
|
-
}
|
|
92
|
-
setMetadata(metadata) {
|
|
93
|
-
navigator.mediaSession.metadata = new MediaMetadata(metadata);
|
|
94
80
|
}
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
navigator.mediaSession.setActionHandler("pause", null);
|
|
100
|
-
navigator.mediaSession.setActionHandler("previoustrack", null);
|
|
101
|
-
navigator.mediaSession.setActionHandler("nexttrack", null);
|
|
102
|
-
navigator.mediaSession.playbackState = "none";
|
|
103
|
-
navigator.mediaSession.metadata = null;
|
|
81
|
+
async destroy() {
|
|
82
|
+
this.destroyEvent.next();
|
|
83
|
+
await this.pause();
|
|
84
|
+
this.playerElement.remove();
|
|
104
85
|
}
|
|
105
|
-
play() {
|
|
86
|
+
async play(options = {}) {
|
|
106
87
|
if (!this.playerElement.src)
|
|
107
88
|
throw new Error("No file opened");
|
|
108
89
|
this.log("Called play");
|
|
109
90
|
this.playerElement?.play();
|
|
91
|
+
if (options.fadeIn !== undefined ? options.fadeIn : this.options.fadeIn)
|
|
92
|
+
await this.fadeIn();
|
|
93
|
+
}
|
|
94
|
+
async fadeIn() {
|
|
95
|
+
if (!this.options.fadeInterval) {
|
|
96
|
+
clearInterval(this.fadeIntervalSubscription);
|
|
97
|
+
this.fadePromiseResolve?.();
|
|
98
|
+
}
|
|
99
|
+
return new Promise((resolve) => {
|
|
100
|
+
this.playerElement.volume = 0;
|
|
101
|
+
this.fadePromiseResolve = resolve;
|
|
102
|
+
this.fadeIntervalSubscription = setInterval(() => {
|
|
103
|
+
if (this.playerElement.volume >= 1) {
|
|
104
|
+
clearInterval(this.fadeIntervalSubscription);
|
|
105
|
+
return resolve();
|
|
106
|
+
}
|
|
107
|
+
this.playerElement.volume += (1 / this.options.fadeInterval) * 100;
|
|
108
|
+
}, 100);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async fadeOut() {
|
|
112
|
+
if (!this.options.fadeInterval) {
|
|
113
|
+
clearInterval(this.fadeIntervalSubscription);
|
|
114
|
+
this.fadePromiseResolve?.();
|
|
115
|
+
}
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
this.fadePromiseResolve = resolve;
|
|
118
|
+
this.fadeIntervalSubscription = setInterval(() => {
|
|
119
|
+
if (this.playerElement.volume <= 0) {
|
|
120
|
+
clearInterval(this.fadeIntervalSubscription);
|
|
121
|
+
this.playerElement.pause();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.playerElement.volume -= (1 / this.options.fadeInterval) * 100;
|
|
125
|
+
}, 100);
|
|
126
|
+
});
|
|
110
127
|
}
|
|
111
|
-
pause() {
|
|
128
|
+
async pause(options = {}) {
|
|
112
129
|
if (!this.playerElement.src)
|
|
113
130
|
throw new Error("No file opened");
|
|
114
131
|
this.log("Called pause");
|
|
115
132
|
this.playerElement?.pause();
|
|
133
|
+
if (options.fadeIn !== undefined ? options.fadeIn : this.options.fadeIn)
|
|
134
|
+
await this.fadeOut();
|
|
116
135
|
}
|
|
117
136
|
seekTo(seconds) {
|
|
118
137
|
if (!this.playerElement.src)
|
|
@@ -138,10 +157,10 @@ export class PlayerController {
|
|
|
138
157
|
return;
|
|
139
158
|
await this.localStorage.set(`progress-${this.trackId}`, currentTime);
|
|
140
159
|
}
|
|
141
|
-
log(message) {
|
|
160
|
+
log(message, ...args) {
|
|
142
161
|
const time = this.playerElement?.currentTime ? Math.round(this.playerElement?.currentTime) : null;
|
|
143
162
|
if (time)
|
|
144
163
|
message += ` @${time}s`;
|
|
145
|
-
console.log(`[PlayerController] ${message}
|
|
164
|
+
console.log(`[PlayerController] ${message}`, ...args);
|
|
146
165
|
}
|
|
147
166
|
}
|
|
@@ -3,7 +3,9 @@ export interface LocalStorageOptions {
|
|
|
3
3
|
}
|
|
4
4
|
export declare class LocalStorage {
|
|
5
5
|
private options;
|
|
6
|
+
private static readonly globalPrefix;
|
|
6
7
|
constructor(options?: Partial<LocalStorageOptions>);
|
|
8
|
+
static clearAll(prefix?: string): Promise<void>;
|
|
7
9
|
get<T = unknown>(key: string, validate?: (value: unknown) => value is T): Promise<T | null>;
|
|
8
10
|
set(key: string, value: any): Promise<void>;
|
|
9
11
|
private parseData;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-storage.d.ts","sourceRoot":"","sources":["../../src/storage/local-storage.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,YAAY;
|
|
1
|
+
{"version":3,"file":"local-storage.d.ts","sourceRoot":"","sources":["../../src/storage/local-storage.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,YAAY;IAGX,OAAO,CAAC,OAAO;IAF3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAe;gBAE/B,OAAO,GAAE,OAAO,CAAC,mBAAmB,CAAM;WAEjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAU/B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAiB3F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAKjC,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,cAAc;CAQvB"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
export class LocalStorage {
|
|
2
2
|
options;
|
|
3
|
+
static globalPrefix = "audiowalk";
|
|
3
4
|
constructor(options = {}) {
|
|
4
5
|
this.options = options;
|
|
5
6
|
}
|
|
7
|
+
static async clearAll(prefix) {
|
|
8
|
+
prefix = prefix ? `${LocalStorage.globalPrefix}-${prefix}` : LocalStorage.globalPrefix;
|
|
9
|
+
const keys = Object.keys(window.localStorage);
|
|
10
|
+
for (const key of keys) {
|
|
11
|
+
if (key.startsWith(prefix)) {
|
|
12
|
+
window.localStorage.removeItem(key);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
6
16
|
async get(key, validate) {
|
|
7
17
|
const data = window.localStorage.getItem(this.getPrefixedKey(key));
|
|
8
18
|
if (data === null)
|
|
@@ -33,7 +43,7 @@ export class LocalStorage {
|
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
getPrefixedKey(key) {
|
|
36
|
-
const keyParts = [
|
|
46
|
+
const keyParts = [LocalStorage.globalPrefix];
|
|
37
47
|
if (this.options.prefix)
|
|
38
48
|
keyParts.push(this.options.prefix);
|
|
39
49
|
keyParts.push(key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"story-controller.d.ts","sourceRoot":"","sources":["../../src/story/story-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"story-controller.d.ts","sourceRoot":"","sources":["../../src/story/story-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA6B,OAAO,EAAE,MAAM,MAAM,CAAC;AAG3E,MAAM,WAAW,sBAAsB,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,EAAE,UAAU,SAAS,EAAE,GAAG,EAAE;IACnG,EAAE,EAAE,SAAS,CAAC;IACd,WAAW,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC;CAClH;AAED,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE,cAAc,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB,CAAC,UAAU;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,KAAK,IAAI,UAAU,CAAC;CACrD;AAED,MAAM,WAAW,eAAe,CAC9B,SAAS,SAAS,MAAM,GAAG,MAAM,EACjC,UAAU,SAAS;IAAE,cAAc,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,eAAe,CAAC,SAAS,CAAC,EAC9E,iBAAiB,SAAS,sBAAsB,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,sBAAsB,CAC9F,SAAS,EACT,UAAU,CACX;IAED,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAC/C,YAAY,EAAE,UAAU,CAAC;CAC1B;AAED,qBAAa,eAAe,CAC1B,SAAS,SAAS,MAAM,GAAG,MAAM,EACjC,UAAU,SAAS;IAAE,cAAc,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,eAAe,CAAC,SAAS,CAAC,EAC9E,iBAAiB,SAAS,sBAAsB,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,sBAAsB,CAC9F,SAAS,EACT,UAAU,CACX;aAWiB,KAAK,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,EAAE,iBAAiB,CAAC;IAChF,OAAO,CAAC,OAAO;IAVjB,SAAgB,UAAU,8BAA8C;IACxE,SAAgB,cAAc,4CAAuD;IACrF,SAAgB,GAAG,gBAAuB;IAE1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAE5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAW;gBAGzB,KAAK,EAAE,eAAe,CAAC,SAAS,EAAE,UAAU,EAAE,iBAAiB,CAAC,EACxE,OAAO,GAAE,sBAAsB,CAAC,UAAU,CAAM;IAgBpD,QAAQ,CAAC,KAAK,EAAE,UAAU;IAK1B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;IAa7E,UAAU,CAAC,MAAM,EAAE,SAAS;IAI5B,WAAW;IAuBX,UAAU;IAKhB,QAAQ;YAIM,SAAS;YAaT,SAAS;CAGxB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BehaviorSubject, map, Subject } from "rxjs";
|
|
1
|
+
import { BehaviorSubject, distinctUntilChanged, map, Subject } from "rxjs";
|
|
2
2
|
import { LocalStorage } from "../storage/local-storage";
|
|
3
3
|
export class StoryController {
|
|
4
4
|
story;
|
|
@@ -13,7 +13,9 @@ export class StoryController {
|
|
|
13
13
|
this.options = options;
|
|
14
14
|
this.localStorage = new LocalStorage({ prefix: options.stateStorageKey ?? "story-state" });
|
|
15
15
|
this.storyState
|
|
16
|
-
.pipe(map((state) =>
|
|
16
|
+
.pipe(map((state) => state.currentChapter))
|
|
17
|
+
.pipe(distinctUntilChanged())
|
|
18
|
+
.pipe(map((currentChapter) => (currentChapter ? story.chapters[currentChapter] : null)))
|
|
17
19
|
.subscribe(this.currentChapter);
|
|
18
20
|
this.loadState().then((state) => {
|
|
19
21
|
if (state)
|
|
@@ -53,11 +55,12 @@ export class StoryController {
|
|
|
53
55
|
if (typeof chapter.nextChapter === "function") {
|
|
54
56
|
const storyState = this.storyState.value;
|
|
55
57
|
const string = await chapter.nextChapter(storyState);
|
|
56
|
-
this.setChapter(string);
|
|
58
|
+
return this.setChapter(string);
|
|
57
59
|
}
|
|
58
60
|
throw new Error(`Invalid nextChapter type in chapter ${chapter.id}`);
|
|
59
61
|
}
|
|
60
62
|
async resetStory() {
|
|
63
|
+
await LocalStorage.clearAll();
|
|
61
64
|
await this.setState(this.story.initialState);
|
|
62
65
|
}
|
|
63
66
|
endStory() {
|