@mpgd/adapter-browser 0.3.1 → 0.4.0
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 +6 -1
- package/dist/index.js +169 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { type PlatformGateway } from '@mpgd/platform';
|
|
2
|
-
export
|
|
2
|
+
export interface BrowserPlatformGatewayOptions {
|
|
3
|
+
readonly locationHref?: string;
|
|
4
|
+
readonly share?: (data: ShareData) => Promise<void>;
|
|
5
|
+
readonly writeClipboardText?: (text: string) => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createBrowserPlatformGateway(options?: BrowserPlatformGatewayOptions): PlatformGateway;
|
package/dist/index.js
CHANGED
|
@@ -11,9 +11,10 @@ const mockProducts = [
|
|
|
11
11
|
},
|
|
12
12
|
},
|
|
13
13
|
];
|
|
14
|
-
export function createBrowserPlatformGateway() {
|
|
14
|
+
export function createBrowserPlatformGateway(options = {}) {
|
|
15
15
|
const pauseListeners = new Set();
|
|
16
16
|
const resumeListeners = new Set();
|
|
17
|
+
const shareSupported = canShare(options);
|
|
17
18
|
if (typeof document !== 'undefined') {
|
|
18
19
|
document.addEventListener('visibilitychange', () => {
|
|
19
20
|
const listeners = document.hidden ? pauseListeners : resumeListeners;
|
|
@@ -30,6 +31,7 @@ export function createBrowserPlatformGateway() {
|
|
|
30
31
|
rewardedAds: true,
|
|
31
32
|
interstitialAds: true,
|
|
32
33
|
cloudSave: true,
|
|
34
|
+
socialShare: shareSupported,
|
|
33
35
|
localizedContent: true,
|
|
34
36
|
};
|
|
35
37
|
},
|
|
@@ -40,6 +42,47 @@ export function createBrowserPlatformGateway() {
|
|
|
40
42
|
displayName: 'Browser Player',
|
|
41
43
|
};
|
|
42
44
|
},
|
|
45
|
+
async getSession() {
|
|
46
|
+
return {
|
|
47
|
+
identityLevel: 'guest',
|
|
48
|
+
playerId: 'browser-player',
|
|
49
|
+
trustLevel: 'local',
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
async requestUpgrade() {
|
|
53
|
+
return {
|
|
54
|
+
status: 'unavailable',
|
|
55
|
+
reloadExpected: false,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
presentation: {
|
|
60
|
+
async getLaunchIntent() {
|
|
61
|
+
return launchIntentFromUrl(resolveBrowserUrl(options.locationHref));
|
|
62
|
+
},
|
|
63
|
+
async requestGameSurface() {
|
|
64
|
+
return 'already-fullscreen';
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
sharing: {
|
|
68
|
+
...(shareSupported
|
|
69
|
+
? {
|
|
70
|
+
async share(intent) {
|
|
71
|
+
return shareFromBrowser(intent, options);
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
: {}),
|
|
75
|
+
async readInboundShare() {
|
|
76
|
+
return inboundShareFromUrl(resolveBrowserUrl(options.locationHref));
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
notifications: {
|
|
80
|
+
async getStatus() {
|
|
81
|
+
return 'unsupported';
|
|
82
|
+
},
|
|
83
|
+
async requestSubscription() {
|
|
84
|
+
return 'unavailable';
|
|
85
|
+
},
|
|
43
86
|
},
|
|
44
87
|
commerce: {
|
|
45
88
|
async getProducts() {
|
|
@@ -105,3 +148,128 @@ export function createBrowserPlatformGateway() {
|
|
|
105
148
|
},
|
|
106
149
|
};
|
|
107
150
|
}
|
|
151
|
+
const launchEntries = new Set([
|
|
152
|
+
'home',
|
|
153
|
+
'daily',
|
|
154
|
+
'practice',
|
|
155
|
+
'free-play',
|
|
156
|
+
'continue',
|
|
157
|
+
'leaderboard',
|
|
158
|
+
'friend-challenge',
|
|
159
|
+
]);
|
|
160
|
+
function launchIntentFromUrl(url) {
|
|
161
|
+
const params = mergedSearchParams(url);
|
|
162
|
+
const requestedEntry = params.get('entry');
|
|
163
|
+
const inbound = inboundShareFromParams(params);
|
|
164
|
+
let entry;
|
|
165
|
+
if (requestedEntry !== null && launchEntries.has(requestedEntry)) {
|
|
166
|
+
entry = requestedEntry;
|
|
167
|
+
}
|
|
168
|
+
else if (inbound?.challengeToken === undefined) {
|
|
169
|
+
entry = 'home';
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
entry = 'friend-challenge';
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
entry,
|
|
176
|
+
...(inbound?.puzzleId === undefined ? {} : { puzzleId: inbound.puzzleId }),
|
|
177
|
+
...(inbound?.challengeToken === undefined
|
|
178
|
+
? {}
|
|
179
|
+
: { referralToken: inbound.challengeToken }),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function inboundShareFromUrl(url) {
|
|
183
|
+
return inboundShareFromParams(mergedSearchParams(url));
|
|
184
|
+
}
|
|
185
|
+
function inboundShareFromParams(params) {
|
|
186
|
+
const puzzleId = nonEmptyParam(params.get('puzzleId'));
|
|
187
|
+
const challengeToken = nonEmptyParam(params.get('challengeToken'));
|
|
188
|
+
if (puzzleId === undefined && challengeToken === undefined) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
...(puzzleId === undefined ? {} : { puzzleId }),
|
|
193
|
+
...(challengeToken === undefined ? {} : { challengeToken }),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function mergedSearchParams(url) {
|
|
197
|
+
const params = new URLSearchParams(url?.search ?? '');
|
|
198
|
+
const nestedParams = params.get('queryParams');
|
|
199
|
+
if (nestedParams === null) {
|
|
200
|
+
return params;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const parsed = JSON.parse(nestedParams);
|
|
204
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
205
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
206
|
+
if (typeof value === 'string' && !params.has(key)) {
|
|
207
|
+
params.set(key, value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Inbound query payloads are untrusted. Invalid nested data is ignored.
|
|
214
|
+
}
|
|
215
|
+
return params;
|
|
216
|
+
}
|
|
217
|
+
async function shareFromBrowser(intent, options) {
|
|
218
|
+
const share = options.share ?? globalThis.navigator?.share?.bind(globalThis.navigator);
|
|
219
|
+
if (share !== undefined) {
|
|
220
|
+
try {
|
|
221
|
+
await share({
|
|
222
|
+
title: intent.title,
|
|
223
|
+
text: intent.text,
|
|
224
|
+
url: intent.deepLink,
|
|
225
|
+
});
|
|
226
|
+
return { status: 'shared' };
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (isAbortError(error)) {
|
|
230
|
+
return { status: 'cancelled' };
|
|
231
|
+
}
|
|
232
|
+
console.warn('Browser share failed; falling back to clipboard.', error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const writeClipboardText = options.writeClipboardText
|
|
236
|
+
?? globalThis.navigator?.clipboard?.writeText?.bind(globalThis.navigator.clipboard);
|
|
237
|
+
if (writeClipboardText === undefined) {
|
|
238
|
+
return { status: 'unavailable' };
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
await writeClipboardText(`${intent.text}\n${intent.deepLink}`);
|
|
242
|
+
return { status: 'shared' };
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return { status: 'unavailable' };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function canShare(options) {
|
|
249
|
+
return (options.share !== undefined
|
|
250
|
+
|| options.writeClipboardText !== undefined
|
|
251
|
+
|| typeof globalThis.navigator?.share === 'function'
|
|
252
|
+
|| typeof globalThis.navigator?.clipboard?.writeText === 'function');
|
|
253
|
+
}
|
|
254
|
+
function resolveBrowserUrl(locationHref) {
|
|
255
|
+
const href = locationHref ?? globalThis.location?.href;
|
|
256
|
+
if (href === undefined) {
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
return new URL(href);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function nonEmptyParam(value) {
|
|
267
|
+
return value === null || value.length === 0 ? undefined : value;
|
|
268
|
+
}
|
|
269
|
+
function isAbortError(error) {
|
|
270
|
+
return typeof DOMException !== 'undefined' && error instanceof DOMException
|
|
271
|
+
? error.name === 'AbortError'
|
|
272
|
+
: typeof error === 'object'
|
|
273
|
+
&& error !== null
|
|
274
|
+
&& error.name === 'AbortError';
|
|
275
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/adapter-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Browser preview platform adapter for mpgd games.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@mpgd/platform": "0.
|
|
37
|
+
"@mpgd/platform": "0.4.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"ttsc": "0.16.9",
|