@mideind/netskrafl-react 1.7.1 → 1.8.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/cjs/css/netskrafl.css +4 -1
- package/dist/cjs/index.js +1431 -1031
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/css/netskrafl.css +4 -1
- package/dist/esm/index.js +1431 -1031
- package/dist/esm/index.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const DEFAULT_STATE = {
|
|
|
10
10
|
measurementId: "",
|
|
11
11
|
account: "",
|
|
12
12
|
userEmail: "",
|
|
13
|
-
|
|
13
|
+
netskraflUserId: "",
|
|
14
14
|
userNick: "",
|
|
15
15
|
userFullname: "",
|
|
16
16
|
locale: "is_IS",
|
|
@@ -74,8 +74,8 @@ const saveAuthSettings = (settings) => {
|
|
|
74
74
|
// Only add optional fields if they are defined
|
|
75
75
|
if (settings.account !== undefined)
|
|
76
76
|
filteredSettings.account = settings.account;
|
|
77
|
-
if (settings.
|
|
78
|
-
filteredSettings.
|
|
77
|
+
if (settings.netskraflUserId !== undefined)
|
|
78
|
+
filteredSettings.netskraflUserId = settings.netskraflUserId;
|
|
79
79
|
if (settings.userNick !== undefined)
|
|
80
80
|
filteredSettings.userNick = settings.userNick;
|
|
81
81
|
if (settings.firebaseApiKey !== undefined)
|
|
@@ -130,7 +130,6 @@ const clearAuthSettings = () => {
|
|
|
130
130
|
};
|
|
131
131
|
// Apply persisted settings to a GlobalState object
|
|
132
132
|
const applyPersistedSettings = (state) => {
|
|
133
|
-
var _a, _b, _c, _d, _e, _f;
|
|
134
133
|
const persisted = loadAuthSettings();
|
|
135
134
|
if (!persisted) {
|
|
136
135
|
return state;
|
|
@@ -146,18 +145,18 @@ const applyPersistedSettings = (state) => {
|
|
|
146
145
|
return {
|
|
147
146
|
...state,
|
|
148
147
|
// Only apply persisted values if current values are defaults
|
|
149
|
-
account:
|
|
150
|
-
|
|
148
|
+
account: persisted.account || state.account || state.netskraflUserId,
|
|
149
|
+
netskraflUserId: persisted.netskraflUserId || state.netskraflUserId,
|
|
151
150
|
// userNick is special: the container provides a "guess", but the backend login
|
|
152
151
|
// returns the authoritative nickname, which should persist across re-initializations
|
|
153
152
|
userNick: persisted.userNick || state.userNick,
|
|
154
153
|
firebaseApiKey: state.firebaseApiKey || persisted.firebaseApiKey || state.firebaseApiKey,
|
|
155
|
-
beginner:
|
|
156
|
-
fairPlay:
|
|
157
|
-
ready:
|
|
158
|
-
readyTimed:
|
|
159
|
-
audio:
|
|
160
|
-
fanfare:
|
|
154
|
+
beginner: persisted.beginner ?? state.beginner,
|
|
155
|
+
fairPlay: persisted.fairPlay ?? state.fairPlay,
|
|
156
|
+
ready: persisted.ready ?? state.ready,
|
|
157
|
+
readyTimed: persisted.readyTimed ?? state.readyTimed,
|
|
158
|
+
audio: persisted.audio ?? state.audio,
|
|
159
|
+
fanfare: persisted.fanfare ?? state.fanfare,
|
|
161
160
|
};
|
|
162
161
|
};
|
|
163
162
|
|
|
@@ -2437,7 +2436,7 @@ async function loadMessages(state, locale) {
|
|
|
2437
2436
|
});
|
|
2438
2437
|
setLocale(locale, messages);
|
|
2439
2438
|
}
|
|
2440
|
-
catch
|
|
2439
|
+
catch {
|
|
2441
2440
|
setLocale(locale, {});
|
|
2442
2441
|
}
|
|
2443
2442
|
}
|
|
@@ -2801,7 +2800,7 @@ function escapeHtml(string) {
|
|
|
2801
2800
|
"'": ''',
|
|
2802
2801
|
"/": '/'
|
|
2803
2802
|
};
|
|
2804
|
-
return String(string).replace(/[&<>"'/]/g, (s) =>
|
|
2803
|
+
return String(string).replace(/[&<>"'/]/g, (s) => entityMap[s] ?? "");
|
|
2805
2804
|
}
|
|
2806
2805
|
function getUrlVars(url) {
|
|
2807
2806
|
// Get values from a URL query string
|
|
@@ -2938,6 +2937,8 @@ const loginUserByEmail = async (state) => {
|
|
|
2938
2937
|
});
|
|
2939
2938
|
};
|
|
2940
2939
|
|
|
2940
|
+
const getDefaultsFromPostinstall = () => (undefined);
|
|
2941
|
+
|
|
2941
2942
|
/**
|
|
2942
2943
|
* @license
|
|
2943
2944
|
* Copyright 2017 Google LLC
|
|
@@ -3486,7 +3487,8 @@ const getDefaultsFromCookie = () => {
|
|
|
3486
3487
|
*/
|
|
3487
3488
|
const getDefaults = () => {
|
|
3488
3489
|
try {
|
|
3489
|
-
return (
|
|
3490
|
+
return (getDefaultsFromPostinstall() ||
|
|
3491
|
+
getDefaultsFromGlobal() ||
|
|
3490
3492
|
getDefaultsFromEnvVariable() ||
|
|
3491
3493
|
getDefaultsFromCookie());
|
|
3492
3494
|
}
|
|
@@ -3507,7 +3509,7 @@ const getDefaults = () => {
|
|
|
3507
3509
|
* @returns a URL host formatted like `127.0.0.1:9999` or `[::1]:4000` if available
|
|
3508
3510
|
* @public
|
|
3509
3511
|
*/
|
|
3510
|
-
const getDefaultEmulatorHost = (productName) =>
|
|
3512
|
+
const getDefaultEmulatorHost = (productName) => getDefaults()?.emulatorHosts?.[productName];
|
|
3511
3513
|
/**
|
|
3512
3514
|
* Returns emulator hostname and port stored in the __FIREBASE_DEFAULTS__ object
|
|
3513
3515
|
* for the given product.
|
|
@@ -3537,13 +3539,13 @@ const getDefaultEmulatorHostnameAndPort = (productName) => {
|
|
|
3537
3539
|
* Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object.
|
|
3538
3540
|
* @public
|
|
3539
3541
|
*/
|
|
3540
|
-
const getDefaultAppConfig = () =>
|
|
3542
|
+
const getDefaultAppConfig = () => getDefaults()?.config;
|
|
3541
3543
|
/**
|
|
3542
3544
|
* Returns an experimental setting on the __FIREBASE_DEFAULTS__ object (properties
|
|
3543
3545
|
* prefixed by "_")
|
|
3544
3546
|
* @public
|
|
3545
3547
|
*/
|
|
3546
|
-
const getExperimentalSetting = (name) =>
|
|
3548
|
+
const getExperimentalSetting = (name) => getDefaults()?.[`_${name}`];
|
|
3547
3549
|
|
|
3548
3550
|
/**
|
|
3549
3551
|
* @license
|
|
@@ -3600,6 +3602,53 @@ class Deferred {
|
|
|
3600
3602
|
}
|
|
3601
3603
|
}
|
|
3602
3604
|
|
|
3605
|
+
/**
|
|
3606
|
+
* @license
|
|
3607
|
+
* Copyright 2025 Google LLC
|
|
3608
|
+
*
|
|
3609
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3610
|
+
* you may not use this file except in compliance with the License.
|
|
3611
|
+
* You may obtain a copy of the License at
|
|
3612
|
+
*
|
|
3613
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3614
|
+
*
|
|
3615
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3616
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3617
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3618
|
+
* See the License for the specific language governing permissions and
|
|
3619
|
+
* limitations under the License.
|
|
3620
|
+
*/
|
|
3621
|
+
/**
|
|
3622
|
+
* Checks whether host is a cloud workstation or not.
|
|
3623
|
+
* @public
|
|
3624
|
+
*/
|
|
3625
|
+
function isCloudWorkstation(url) {
|
|
3626
|
+
// `isCloudWorkstation` is called without protocol in certain connect*Emulator functions
|
|
3627
|
+
// In HTTP request builders, it's called with the protocol.
|
|
3628
|
+
// If called with protocol prefix, it's a valid URL, so we extract the hostname
|
|
3629
|
+
// If called without, we assume the string is the hostname.
|
|
3630
|
+
try {
|
|
3631
|
+
const host = url.startsWith('http://') || url.startsWith('https://')
|
|
3632
|
+
? new URL(url).hostname
|
|
3633
|
+
: url;
|
|
3634
|
+
return host.endsWith('.cloudworkstations.dev');
|
|
3635
|
+
}
|
|
3636
|
+
catch {
|
|
3637
|
+
return false;
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
/**
|
|
3641
|
+
* Makes a fetch request to the given server.
|
|
3642
|
+
* Mostly used for forwarding cookies in Firebase Studio.
|
|
3643
|
+
* @public
|
|
3644
|
+
*/
|
|
3645
|
+
async function pingServer(endpoint) {
|
|
3646
|
+
const result = await fetch(endpoint, {
|
|
3647
|
+
credentials: 'include'
|
|
3648
|
+
});
|
|
3649
|
+
return result.ok;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3603
3652
|
/**
|
|
3604
3653
|
* @license
|
|
3605
3654
|
* Copyright 2021 Google LLC
|
|
@@ -3631,12 +3680,22 @@ function createMockUserToken(token, projectId) {
|
|
|
3631
3680
|
if (!sub) {
|
|
3632
3681
|
throw new Error("mockUserToken must contain 'sub' or 'user_id' field!");
|
|
3633
3682
|
}
|
|
3634
|
-
const payload =
|
|
3683
|
+
const payload = {
|
|
3635
3684
|
// Set all required fields to decent defaults
|
|
3636
|
-
iss: `https://securetoken.google.com/${project}`,
|
|
3685
|
+
iss: `https://securetoken.google.com/${project}`,
|
|
3686
|
+
aud: project,
|
|
3687
|
+
iat,
|
|
3688
|
+
exp: iat + 3600,
|
|
3689
|
+
auth_time: iat,
|
|
3690
|
+
sub,
|
|
3691
|
+
user_id: sub,
|
|
3692
|
+
firebase: {
|
|
3637
3693
|
sign_in_provider: 'custom',
|
|
3638
3694
|
identities: {}
|
|
3639
|
-
}
|
|
3695
|
+
},
|
|
3696
|
+
// Override with user options
|
|
3697
|
+
...token
|
|
3698
|
+
};
|
|
3640
3699
|
// Unsecured JWTs use the empty string as a signature.
|
|
3641
3700
|
const signature = '';
|
|
3642
3701
|
return [
|
|
@@ -3645,6 +3704,152 @@ function createMockUserToken(token, projectId) {
|
|
|
3645
3704
|
signature
|
|
3646
3705
|
].join('.');
|
|
3647
3706
|
}
|
|
3707
|
+
const emulatorStatus = {};
|
|
3708
|
+
// Checks whether any products are running on an emulator
|
|
3709
|
+
function getEmulatorSummary() {
|
|
3710
|
+
const summary = {
|
|
3711
|
+
prod: [],
|
|
3712
|
+
emulator: []
|
|
3713
|
+
};
|
|
3714
|
+
for (const key of Object.keys(emulatorStatus)) {
|
|
3715
|
+
if (emulatorStatus[key]) {
|
|
3716
|
+
summary.emulator.push(key);
|
|
3717
|
+
}
|
|
3718
|
+
else {
|
|
3719
|
+
summary.prod.push(key);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
return summary;
|
|
3723
|
+
}
|
|
3724
|
+
function getOrCreateEl(id) {
|
|
3725
|
+
let parentDiv = document.getElementById(id);
|
|
3726
|
+
let created = false;
|
|
3727
|
+
if (!parentDiv) {
|
|
3728
|
+
parentDiv = document.createElement('div');
|
|
3729
|
+
parentDiv.setAttribute('id', id);
|
|
3730
|
+
created = true;
|
|
3731
|
+
}
|
|
3732
|
+
return { created, element: parentDiv };
|
|
3733
|
+
}
|
|
3734
|
+
let previouslyDismissed = false;
|
|
3735
|
+
/**
|
|
3736
|
+
* Updates Emulator Banner. Primarily used for Firebase Studio
|
|
3737
|
+
* @param name
|
|
3738
|
+
* @param isRunningEmulator
|
|
3739
|
+
* @public
|
|
3740
|
+
*/
|
|
3741
|
+
function updateEmulatorBanner(name, isRunningEmulator) {
|
|
3742
|
+
if (typeof window === 'undefined' ||
|
|
3743
|
+
typeof document === 'undefined' ||
|
|
3744
|
+
!isCloudWorkstation(window.location.host) ||
|
|
3745
|
+
emulatorStatus[name] === isRunningEmulator ||
|
|
3746
|
+
emulatorStatus[name] || // If already set to use emulator, can't go back to prod.
|
|
3747
|
+
previouslyDismissed) {
|
|
3748
|
+
return;
|
|
3749
|
+
}
|
|
3750
|
+
emulatorStatus[name] = isRunningEmulator;
|
|
3751
|
+
function prefixedId(id) {
|
|
3752
|
+
return `__firebase__banner__${id}`;
|
|
3753
|
+
}
|
|
3754
|
+
const bannerId = '__firebase__banner';
|
|
3755
|
+
const summary = getEmulatorSummary();
|
|
3756
|
+
const showError = summary.prod.length > 0;
|
|
3757
|
+
function tearDown() {
|
|
3758
|
+
const element = document.getElementById(bannerId);
|
|
3759
|
+
if (element) {
|
|
3760
|
+
element.remove();
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
function setupBannerStyles(bannerEl) {
|
|
3764
|
+
bannerEl.style.display = 'flex';
|
|
3765
|
+
bannerEl.style.background = '#7faaf0';
|
|
3766
|
+
bannerEl.style.position = 'fixed';
|
|
3767
|
+
bannerEl.style.bottom = '5px';
|
|
3768
|
+
bannerEl.style.left = '5px';
|
|
3769
|
+
bannerEl.style.padding = '.5em';
|
|
3770
|
+
bannerEl.style.borderRadius = '5px';
|
|
3771
|
+
bannerEl.style.alignItems = 'center';
|
|
3772
|
+
}
|
|
3773
|
+
function setupIconStyles(prependIcon, iconId) {
|
|
3774
|
+
prependIcon.setAttribute('width', '24');
|
|
3775
|
+
prependIcon.setAttribute('id', iconId);
|
|
3776
|
+
prependIcon.setAttribute('height', '24');
|
|
3777
|
+
prependIcon.setAttribute('viewBox', '0 0 24 24');
|
|
3778
|
+
prependIcon.setAttribute('fill', 'none');
|
|
3779
|
+
prependIcon.style.marginLeft = '-6px';
|
|
3780
|
+
}
|
|
3781
|
+
function setupCloseBtn() {
|
|
3782
|
+
const closeBtn = document.createElement('span');
|
|
3783
|
+
closeBtn.style.cursor = 'pointer';
|
|
3784
|
+
closeBtn.style.marginLeft = '16px';
|
|
3785
|
+
closeBtn.style.fontSize = '24px';
|
|
3786
|
+
closeBtn.innerHTML = ' ×';
|
|
3787
|
+
closeBtn.onclick = () => {
|
|
3788
|
+
previouslyDismissed = true;
|
|
3789
|
+
tearDown();
|
|
3790
|
+
};
|
|
3791
|
+
return closeBtn;
|
|
3792
|
+
}
|
|
3793
|
+
function setupLinkStyles(learnMoreLink, learnMoreId) {
|
|
3794
|
+
learnMoreLink.setAttribute('id', learnMoreId);
|
|
3795
|
+
learnMoreLink.innerText = 'Learn more';
|
|
3796
|
+
learnMoreLink.href =
|
|
3797
|
+
'https://firebase.google.com/docs/studio/preview-apps#preview-backend';
|
|
3798
|
+
learnMoreLink.setAttribute('target', '__blank');
|
|
3799
|
+
learnMoreLink.style.paddingLeft = '5px';
|
|
3800
|
+
learnMoreLink.style.textDecoration = 'underline';
|
|
3801
|
+
}
|
|
3802
|
+
function setupDom() {
|
|
3803
|
+
const banner = getOrCreateEl(bannerId);
|
|
3804
|
+
const firebaseTextId = prefixedId('text');
|
|
3805
|
+
const firebaseText = document.getElementById(firebaseTextId) || document.createElement('span');
|
|
3806
|
+
const learnMoreId = prefixedId('learnmore');
|
|
3807
|
+
const learnMoreLink = document.getElementById(learnMoreId) ||
|
|
3808
|
+
document.createElement('a');
|
|
3809
|
+
const prependIconId = prefixedId('preprendIcon');
|
|
3810
|
+
const prependIcon = document.getElementById(prependIconId) ||
|
|
3811
|
+
document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
3812
|
+
if (banner.created) {
|
|
3813
|
+
// update styles
|
|
3814
|
+
const bannerEl = banner.element;
|
|
3815
|
+
setupBannerStyles(bannerEl);
|
|
3816
|
+
setupLinkStyles(learnMoreLink, learnMoreId);
|
|
3817
|
+
const closeBtn = setupCloseBtn();
|
|
3818
|
+
setupIconStyles(prependIcon, prependIconId);
|
|
3819
|
+
bannerEl.append(prependIcon, firebaseText, learnMoreLink, closeBtn);
|
|
3820
|
+
document.body.appendChild(bannerEl);
|
|
3821
|
+
}
|
|
3822
|
+
if (showError) {
|
|
3823
|
+
firebaseText.innerText = `Preview backend disconnected.`;
|
|
3824
|
+
prependIcon.innerHTML = `<g clip-path="url(#clip0_6013_33858)">
|
|
3825
|
+
<path d="M4.8 17.6L12 5.6L19.2 17.6H4.8ZM6.91667 16.4H17.0833L12 7.93333L6.91667 16.4ZM12 15.6C12.1667 15.6 12.3056 15.5444 12.4167 15.4333C12.5389 15.3111 12.6 15.1667 12.6 15C12.6 14.8333 12.5389 14.6944 12.4167 14.5833C12.3056 14.4611 12.1667 14.4 12 14.4C11.8333 14.4 11.6889 14.4611 11.5667 14.5833C11.4556 14.6944 11.4 14.8333 11.4 15C11.4 15.1667 11.4556 15.3111 11.5667 15.4333C11.6889 15.5444 11.8333 15.6 12 15.6ZM11.4 13.6H12.6V10.4H11.4V13.6Z" fill="#212121"/>
|
|
3826
|
+
</g>
|
|
3827
|
+
<defs>
|
|
3828
|
+
<clipPath id="clip0_6013_33858">
|
|
3829
|
+
<rect width="24" height="24" fill="white"/>
|
|
3830
|
+
</clipPath>
|
|
3831
|
+
</defs>`;
|
|
3832
|
+
}
|
|
3833
|
+
else {
|
|
3834
|
+
prependIcon.innerHTML = `<g clip-path="url(#clip0_6083_34804)">
|
|
3835
|
+
<path d="M11.4 15.2H12.6V11.2H11.4V15.2ZM12 10C12.1667 10 12.3056 9.94444 12.4167 9.83333C12.5389 9.71111 12.6 9.56667 12.6 9.4C12.6 9.23333 12.5389 9.09444 12.4167 8.98333C12.3056 8.86111 12.1667 8.8 12 8.8C11.8333 8.8 11.6889 8.86111 11.5667 8.98333C11.4556 9.09444 11.4 9.23333 11.4 9.4C11.4 9.56667 11.4556 9.71111 11.5667 9.83333C11.6889 9.94444 11.8333 10 12 10ZM12 18.4C11.1222 18.4 10.2944 18.2333 9.51667 17.9C8.73889 17.5667 8.05556 17.1111 7.46667 16.5333C6.88889 15.9444 6.43333 15.2611 6.1 14.4833C5.76667 13.7056 5.6 12.8778 5.6 12C5.6 11.1111 5.76667 10.2833 6.1 9.51667C6.43333 8.73889 6.88889 8.06111 7.46667 7.48333C8.05556 6.89444 8.73889 6.43333 9.51667 6.1C10.2944 5.76667 11.1222 5.6 12 5.6C12.8889 5.6 13.7167 5.76667 14.4833 6.1C15.2611 6.43333 15.9389 6.89444 16.5167 7.48333C17.1056 8.06111 17.5667 8.73889 17.9 9.51667C18.2333 10.2833 18.4 11.1111 18.4 12C18.4 12.8778 18.2333 13.7056 17.9 14.4833C17.5667 15.2611 17.1056 15.9444 16.5167 16.5333C15.9389 17.1111 15.2611 17.5667 14.4833 17.9C13.7167 18.2333 12.8889 18.4 12 18.4ZM12 17.2C13.4444 17.2 14.6722 16.6944 15.6833 15.6833C16.6944 14.6722 17.2 13.4444 17.2 12C17.2 10.5556 16.6944 9.32778 15.6833 8.31667C14.6722 7.30555 13.4444 6.8 12 6.8C10.5556 6.8 9.32778 7.30555 8.31667 8.31667C7.30556 9.32778 6.8 10.5556 6.8 12C6.8 13.4444 7.30556 14.6722 8.31667 15.6833C9.32778 16.6944 10.5556 17.2 12 17.2Z" fill="#212121"/>
|
|
3836
|
+
</g>
|
|
3837
|
+
<defs>
|
|
3838
|
+
<clipPath id="clip0_6083_34804">
|
|
3839
|
+
<rect width="24" height="24" fill="white"/>
|
|
3840
|
+
</clipPath>
|
|
3841
|
+
</defs>`;
|
|
3842
|
+
firebaseText.innerText = 'Preview backend running in this workspace.';
|
|
3843
|
+
}
|
|
3844
|
+
firebaseText.setAttribute('id', firebaseTextId);
|
|
3845
|
+
}
|
|
3846
|
+
if (document.readyState === 'loading') {
|
|
3847
|
+
window.addEventListener('DOMContentLoaded', setupDom);
|
|
3848
|
+
}
|
|
3849
|
+
else {
|
|
3850
|
+
setupDom();
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3648
3853
|
|
|
3649
3854
|
/**
|
|
3650
3855
|
* @license
|
|
@@ -3762,8 +3967,7 @@ function validateIndexedDBOpenable() {
|
|
|
3762
3967
|
preExist = false;
|
|
3763
3968
|
};
|
|
3764
3969
|
request.onerror = () => {
|
|
3765
|
-
|
|
3766
|
-
reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');
|
|
3970
|
+
reject(request.error?.message || '');
|
|
3767
3971
|
};
|
|
3768
3972
|
}
|
|
3769
3973
|
catch (error) {
|
|
@@ -4865,10 +5069,9 @@ class Provider {
|
|
|
4865
5069
|
return this.instancesDeferred.get(normalizedIdentifier).promise;
|
|
4866
5070
|
}
|
|
4867
5071
|
getImmediate(options) {
|
|
4868
|
-
var _a;
|
|
4869
5072
|
// if multipleInstances is not supported, use the default name
|
|
4870
|
-
const normalizedIdentifier = this.normalizeInstanceIdentifier(options
|
|
4871
|
-
const optional =
|
|
5073
|
+
const normalizedIdentifier = this.normalizeInstanceIdentifier(options?.identifier);
|
|
5074
|
+
const optional = options?.optional ?? false;
|
|
4872
5075
|
if (this.isInitialized(normalizedIdentifier) ||
|
|
4873
5076
|
this.shouldAutoInitialize()) {
|
|
4874
5077
|
try {
|
|
@@ -5000,9 +5203,9 @@ class Provider {
|
|
|
5000
5203
|
* @returns a function to unregister the callback
|
|
5001
5204
|
*/
|
|
5002
5205
|
onInit(callback, identifier) {
|
|
5003
|
-
var _a;
|
|
5004
5206
|
const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);
|
|
5005
|
-
const existingCallbacks =
|
|
5207
|
+
const existingCallbacks = this.onInitCallbacks.get(normalizedIdentifier) ??
|
|
5208
|
+
new Set();
|
|
5006
5209
|
existingCallbacks.add(callback);
|
|
5007
5210
|
this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks);
|
|
5008
5211
|
const existingInstance = this.instances.get(normalizedIdentifier);
|
|
@@ -5026,7 +5229,7 @@ class Provider {
|
|
|
5026
5229
|
try {
|
|
5027
5230
|
callback(instance, identifier);
|
|
5028
5231
|
}
|
|
5029
|
-
catch
|
|
5232
|
+
catch {
|
|
5030
5233
|
// ignore errors in the onInit callback
|
|
5031
5234
|
}
|
|
5032
5235
|
}
|
|
@@ -5055,7 +5258,7 @@ class Provider {
|
|
|
5055
5258
|
try {
|
|
5056
5259
|
this.component.onInstanceCreated(this.container, instanceIdentifier, instance);
|
|
5057
5260
|
}
|
|
5058
|
-
catch
|
|
5261
|
+
catch {
|
|
5059
5262
|
// ignore errors in the onInstanceCreatedCallback
|
|
5060
5263
|
}
|
|
5061
5264
|
}
|
|
@@ -5622,11 +5825,11 @@ class PlatformLoggerServiceImpl {
|
|
|
5622
5825
|
*/
|
|
5623
5826
|
function isVersionServiceProvider(provider) {
|
|
5624
5827
|
const component = provider.getComponent();
|
|
5625
|
-
return
|
|
5828
|
+
return component?.type === "VERSION" /* ComponentType.VERSION */;
|
|
5626
5829
|
}
|
|
5627
5830
|
|
|
5628
5831
|
const name$q = "@firebase/app";
|
|
5629
|
-
const version$1$1 = "0.
|
|
5832
|
+
const version$1$1 = "0.14.5";
|
|
5630
5833
|
|
|
5631
5834
|
/**
|
|
5632
5835
|
* @license
|
|
@@ -5692,12 +5895,12 @@ const name$4$1 = "@firebase/storage-compat";
|
|
|
5692
5895
|
|
|
5693
5896
|
const name$3$1 = "@firebase/firestore";
|
|
5694
5897
|
|
|
5695
|
-
const name$2$1 = "@firebase/
|
|
5898
|
+
const name$2$1 = "@firebase/ai";
|
|
5696
5899
|
|
|
5697
5900
|
const name$1$1 = "@firebase/firestore-compat";
|
|
5698
5901
|
|
|
5699
5902
|
const name$r = "firebase";
|
|
5700
|
-
const version$5 = "
|
|
5903
|
+
const version$5 = "12.5.0";
|
|
5701
5904
|
|
|
5702
5905
|
/**
|
|
5703
5906
|
* @license
|
|
@@ -5846,6 +6049,9 @@ function _getProvider(app, name) {
|
|
|
5846
6049
|
* @internal
|
|
5847
6050
|
*/
|
|
5848
6051
|
function _isFirebaseServerApp(obj) {
|
|
6052
|
+
if (obj === null || obj === undefined) {
|
|
6053
|
+
return false;
|
|
6054
|
+
}
|
|
5849
6055
|
return obj.settings !== undefined;
|
|
5850
6056
|
}
|
|
5851
6057
|
|
|
@@ -5904,8 +6110,8 @@ const ERROR_FACTORY$2 = new ErrorFactory('app', 'Firebase', ERRORS$1);
|
|
|
5904
6110
|
class FirebaseAppImpl {
|
|
5905
6111
|
constructor(options, config, container) {
|
|
5906
6112
|
this._isDeleted = false;
|
|
5907
|
-
this._options =
|
|
5908
|
-
this._config =
|
|
6113
|
+
this._options = { ...options };
|
|
6114
|
+
this._config = { ...config };
|
|
5909
6115
|
this._name = config.name;
|
|
5910
6116
|
this._automaticDataCollectionEnabled =
|
|
5911
6117
|
config.automaticDataCollectionEnabled;
|
|
@@ -5980,7 +6186,11 @@ function initializeApp(_options, rawConfig = {}) {
|
|
|
5980
6186
|
const name = rawConfig;
|
|
5981
6187
|
rawConfig = { name };
|
|
5982
6188
|
}
|
|
5983
|
-
const config =
|
|
6189
|
+
const config = {
|
|
6190
|
+
name: DEFAULT_ENTRY_NAME,
|
|
6191
|
+
automaticDataCollectionEnabled: true,
|
|
6192
|
+
...rawConfig
|
|
6193
|
+
};
|
|
5984
6194
|
const name = config.name;
|
|
5985
6195
|
if (typeof name !== 'string' || !name) {
|
|
5986
6196
|
throw ERROR_FACTORY$2.create("bad-app-name" /* AppError.BAD_APP_NAME */, {
|
|
@@ -6058,10 +6268,9 @@ function getApp(name = DEFAULT_ENTRY_NAME) {
|
|
|
6058
6268
|
* @public
|
|
6059
6269
|
*/
|
|
6060
6270
|
function registerVersion(libraryKeyOrName, version, variant) {
|
|
6061
|
-
var _a;
|
|
6062
6271
|
// TODO: We can use this check to whitelist strings when/if we set up
|
|
6063
6272
|
// a good whitelist system.
|
|
6064
|
-
let library =
|
|
6273
|
+
let library = PLATFORM_LOG_STRING[libraryKeyOrName] ?? libraryKeyOrName;
|
|
6065
6274
|
if (variant) {
|
|
6066
6275
|
library += `-${variant}`;
|
|
6067
6276
|
}
|
|
@@ -6152,7 +6361,7 @@ async function readHeartbeatsFromIndexedDB(app) {
|
|
|
6152
6361
|
}
|
|
6153
6362
|
else {
|
|
6154
6363
|
const idbGetError = ERROR_FACTORY$2.create("idb-get" /* AppError.IDB_GET */, {
|
|
6155
|
-
originalErrorMessage: e
|
|
6364
|
+
originalErrorMessage: e?.message
|
|
6156
6365
|
});
|
|
6157
6366
|
logger$2.warn(idbGetError.message);
|
|
6158
6367
|
}
|
|
@@ -6172,7 +6381,7 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
|
|
|
6172
6381
|
}
|
|
6173
6382
|
else {
|
|
6174
6383
|
const idbGetError = ERROR_FACTORY$2.create("idb-set" /* AppError.IDB_WRITE */, {
|
|
6175
|
-
originalErrorMessage: e
|
|
6384
|
+
originalErrorMessage: e?.message
|
|
6176
6385
|
});
|
|
6177
6386
|
logger$2.warn(idbGetError.message);
|
|
6178
6387
|
}
|
|
@@ -6199,8 +6408,7 @@ function computeKey(app) {
|
|
|
6199
6408
|
* limitations under the License.
|
|
6200
6409
|
*/
|
|
6201
6410
|
const MAX_HEADER_BYTES = 1024;
|
|
6202
|
-
|
|
6203
|
-
const STORED_HEARTBEAT_RETENTION_MAX_MILLIS = 30 * 24 * 60 * 60 * 1000;
|
|
6411
|
+
const MAX_NUM_STORED_HEARTBEATS = 30;
|
|
6204
6412
|
class HeartbeatServiceImpl {
|
|
6205
6413
|
constructor(container) {
|
|
6206
6414
|
this.container = container;
|
|
@@ -6229,7 +6437,6 @@ class HeartbeatServiceImpl {
|
|
|
6229
6437
|
* already logged, subsequent calls to this function in the same day will be ignored.
|
|
6230
6438
|
*/
|
|
6231
6439
|
async triggerHeartbeat() {
|
|
6232
|
-
var _a, _b;
|
|
6233
6440
|
try {
|
|
6234
6441
|
const platformLogger = this.container
|
|
6235
6442
|
.getProvider('platform-logger')
|
|
@@ -6238,10 +6445,10 @@ class HeartbeatServiceImpl {
|
|
|
6238
6445
|
// service, not the browser user agent.
|
|
6239
6446
|
const agent = platformLogger.getPlatformInfoString();
|
|
6240
6447
|
const date = getUTCDateString();
|
|
6241
|
-
if (
|
|
6448
|
+
if (this._heartbeatsCache?.heartbeats == null) {
|
|
6242
6449
|
this._heartbeatsCache = await this._heartbeatsCachePromise;
|
|
6243
6450
|
// If we failed to construct a heartbeats cache, then return immediately.
|
|
6244
|
-
if (
|
|
6451
|
+
if (this._heartbeatsCache?.heartbeats == null) {
|
|
6245
6452
|
return;
|
|
6246
6453
|
}
|
|
6247
6454
|
}
|
|
@@ -6254,14 +6461,13 @@ class HeartbeatServiceImpl {
|
|
|
6254
6461
|
else {
|
|
6255
6462
|
// There is no entry for this date. Create one.
|
|
6256
6463
|
this._heartbeatsCache.heartbeats.push({ date, agent });
|
|
6464
|
+
// If the number of stored heartbeats exceeds the maximum number of stored heartbeats, remove the heartbeat with the earliest date.
|
|
6465
|
+
// Since this is executed each time a heartbeat is pushed, the limit can only be exceeded by one, so only one needs to be removed.
|
|
6466
|
+
if (this._heartbeatsCache.heartbeats.length > MAX_NUM_STORED_HEARTBEATS) {
|
|
6467
|
+
const earliestHeartbeatIdx = getEarliestHeartbeatIdx(this._heartbeatsCache.heartbeats);
|
|
6468
|
+
this._heartbeatsCache.heartbeats.splice(earliestHeartbeatIdx, 1);
|
|
6469
|
+
}
|
|
6257
6470
|
}
|
|
6258
|
-
// Remove entries older than 30 days.
|
|
6259
|
-
this._heartbeatsCache.heartbeats =
|
|
6260
|
-
this._heartbeatsCache.heartbeats.filter(singleDateHeartbeat => {
|
|
6261
|
-
const hbTimestamp = new Date(singleDateHeartbeat.date).valueOf();
|
|
6262
|
-
const now = Date.now();
|
|
6263
|
-
return now - hbTimestamp <= STORED_HEARTBEAT_RETENTION_MAX_MILLIS;
|
|
6264
|
-
});
|
|
6265
6471
|
return this._storage.overwrite(this._heartbeatsCache);
|
|
6266
6472
|
}
|
|
6267
6473
|
catch (e) {
|
|
@@ -6276,13 +6482,12 @@ class HeartbeatServiceImpl {
|
|
|
6276
6482
|
* returns an empty string.
|
|
6277
6483
|
*/
|
|
6278
6484
|
async getHeartbeatsHeader() {
|
|
6279
|
-
var _a;
|
|
6280
6485
|
try {
|
|
6281
6486
|
if (this._heartbeatsCache === null) {
|
|
6282
6487
|
await this._heartbeatsCachePromise;
|
|
6283
6488
|
}
|
|
6284
6489
|
// If it's still null or the array is empty, there is no data to send.
|
|
6285
|
-
if (
|
|
6490
|
+
if (this._heartbeatsCache?.heartbeats == null ||
|
|
6286
6491
|
this._heartbeatsCache.heartbeats.length === 0) {
|
|
6287
6492
|
return '';
|
|
6288
6493
|
}
|
|
@@ -6383,7 +6588,7 @@ class HeartbeatStorageImpl {
|
|
|
6383
6588
|
}
|
|
6384
6589
|
else {
|
|
6385
6590
|
const idbHeartbeatObject = await readHeartbeatsFromIndexedDB(this.app);
|
|
6386
|
-
if (idbHeartbeatObject
|
|
6591
|
+
if (idbHeartbeatObject?.heartbeats) {
|
|
6387
6592
|
return idbHeartbeatObject;
|
|
6388
6593
|
}
|
|
6389
6594
|
else {
|
|
@@ -6393,7 +6598,6 @@ class HeartbeatStorageImpl {
|
|
|
6393
6598
|
}
|
|
6394
6599
|
// overwrite the storage with the provided heartbeats
|
|
6395
6600
|
async overwrite(heartbeatsObject) {
|
|
6396
|
-
var _a;
|
|
6397
6601
|
const canUseIndexedDB = await this._canUseIndexedDBPromise;
|
|
6398
6602
|
if (!canUseIndexedDB) {
|
|
6399
6603
|
return;
|
|
@@ -6401,14 +6605,14 @@ class HeartbeatStorageImpl {
|
|
|
6401
6605
|
else {
|
|
6402
6606
|
const existingHeartbeatsObject = await this.read();
|
|
6403
6607
|
return writeHeartbeatsToIndexedDB(this.app, {
|
|
6404
|
-
lastSentHeartbeatDate:
|
|
6608
|
+
lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
|
|
6609
|
+
existingHeartbeatsObject.lastSentHeartbeatDate,
|
|
6405
6610
|
heartbeats: heartbeatsObject.heartbeats
|
|
6406
6611
|
});
|
|
6407
6612
|
}
|
|
6408
6613
|
}
|
|
6409
6614
|
// add heartbeats
|
|
6410
6615
|
async add(heartbeatsObject) {
|
|
6411
|
-
var _a;
|
|
6412
6616
|
const canUseIndexedDB = await this._canUseIndexedDBPromise;
|
|
6413
6617
|
if (!canUseIndexedDB) {
|
|
6414
6618
|
return;
|
|
@@ -6416,7 +6620,8 @@ class HeartbeatStorageImpl {
|
|
|
6416
6620
|
else {
|
|
6417
6621
|
const existingHeartbeatsObject = await this.read();
|
|
6418
6622
|
return writeHeartbeatsToIndexedDB(this.app, {
|
|
6419
|
-
lastSentHeartbeatDate:
|
|
6623
|
+
lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
|
|
6624
|
+
existingHeartbeatsObject.lastSentHeartbeatDate,
|
|
6420
6625
|
heartbeats: [
|
|
6421
6626
|
...existingHeartbeatsObject.heartbeats,
|
|
6422
6627
|
...heartbeatsObject.heartbeats
|
|
@@ -6436,6 +6641,24 @@ function countBytes(heartbeatsCache) {
|
|
|
6436
6641
|
// heartbeatsCache wrapper properties
|
|
6437
6642
|
JSON.stringify({ version: 2, heartbeats: heartbeatsCache })).length;
|
|
6438
6643
|
}
|
|
6644
|
+
/**
|
|
6645
|
+
* Returns the index of the heartbeat with the earliest date.
|
|
6646
|
+
* If the heartbeats array is empty, -1 is returned.
|
|
6647
|
+
*/
|
|
6648
|
+
function getEarliestHeartbeatIdx(heartbeats) {
|
|
6649
|
+
if (heartbeats.length === 0) {
|
|
6650
|
+
return -1;
|
|
6651
|
+
}
|
|
6652
|
+
let earliestHeartbeatIdx = 0;
|
|
6653
|
+
let earliestHeartbeatDate = heartbeats[0].date;
|
|
6654
|
+
for (let i = 1; i < heartbeats.length; i++) {
|
|
6655
|
+
if (heartbeats[i].date < earliestHeartbeatDate) {
|
|
6656
|
+
earliestHeartbeatDate = heartbeats[i].date;
|
|
6657
|
+
earliestHeartbeatIdx = i;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
return earliestHeartbeatIdx;
|
|
6661
|
+
}
|
|
6439
6662
|
|
|
6440
6663
|
/**
|
|
6441
6664
|
* @license
|
|
@@ -6458,8 +6681,8 @@ function registerCoreComponents(variant) {
|
|
|
6458
6681
|
_registerComponent(new Component('heartbeat', container => new HeartbeatServiceImpl(container), "PRIVATE" /* ComponentType.PRIVATE */));
|
|
6459
6682
|
// Register `app` package.
|
|
6460
6683
|
registerVersion(name$q, version$1$1, variant);
|
|
6461
|
-
// BUILD_TARGET will be replaced by values like
|
|
6462
|
-
registerVersion(name$q, version$1$1, '
|
|
6684
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
6685
|
+
registerVersion(name$q, version$1$1, 'esm2020');
|
|
6463
6686
|
// Register platform SDK identifier (no version).
|
|
6464
6687
|
registerVersion('fire-js', '');
|
|
6465
6688
|
}
|
|
@@ -6473,7 +6696,7 @@ function registerCoreComponents(variant) {
|
|
|
6473
6696
|
registerCoreComponents('');
|
|
6474
6697
|
|
|
6475
6698
|
var name$4 = "firebase";
|
|
6476
|
-
var version$4 = "
|
|
6699
|
+
var version$4 = "12.5.0";
|
|
6477
6700
|
|
|
6478
6701
|
/**
|
|
6479
6702
|
* @license
|
|
@@ -6494,7 +6717,7 @@ var version$4 = "11.0.2";
|
|
|
6494
6717
|
registerVersion(name$4, version$4, 'app');
|
|
6495
6718
|
|
|
6496
6719
|
const name$3 = "@firebase/installations";
|
|
6497
|
-
const version$3 = "0.6.
|
|
6720
|
+
const version$3 = "0.6.19";
|
|
6498
6721
|
|
|
6499
6722
|
/**
|
|
6500
6723
|
* @license
|
|
@@ -6755,7 +6978,7 @@ function generateFid() {
|
|
|
6755
6978
|
const fid = encode(fidByteArray);
|
|
6756
6979
|
return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;
|
|
6757
6980
|
}
|
|
6758
|
-
catch
|
|
6981
|
+
catch {
|
|
6759
6982
|
// FID generation errored
|
|
6760
6983
|
return INVALID_FID;
|
|
6761
6984
|
}
|
|
@@ -7242,7 +7465,10 @@ function updateAuthTokenRequest(appConfig) {
|
|
|
7242
7465
|
}
|
|
7243
7466
|
const oldAuthToken = oldEntry.authToken;
|
|
7244
7467
|
if (hasAuthTokenRequestTimedOut(oldAuthToken)) {
|
|
7245
|
-
return
|
|
7468
|
+
return {
|
|
7469
|
+
...oldEntry,
|
|
7470
|
+
authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
|
|
7471
|
+
};
|
|
7246
7472
|
}
|
|
7247
7473
|
return oldEntry;
|
|
7248
7474
|
});
|
|
@@ -7250,7 +7476,10 @@ function updateAuthTokenRequest(appConfig) {
|
|
|
7250
7476
|
async function fetchAuthTokenFromServer(installations, installationEntry) {
|
|
7251
7477
|
try {
|
|
7252
7478
|
const authToken = await generateAuthTokenRequest(installations, installationEntry);
|
|
7253
|
-
const updatedInstallationEntry =
|
|
7479
|
+
const updatedInstallationEntry = {
|
|
7480
|
+
...installationEntry,
|
|
7481
|
+
authToken
|
|
7482
|
+
};
|
|
7254
7483
|
await set$1(installations.appConfig, updatedInstallationEntry);
|
|
7255
7484
|
return authToken;
|
|
7256
7485
|
}
|
|
@@ -7262,7 +7491,10 @@ async function fetchAuthTokenFromServer(installations, installationEntry) {
|
|
|
7262
7491
|
await remove$1(installations.appConfig);
|
|
7263
7492
|
}
|
|
7264
7493
|
else {
|
|
7265
|
-
const updatedInstallationEntry =
|
|
7494
|
+
const updatedInstallationEntry = {
|
|
7495
|
+
...installationEntry,
|
|
7496
|
+
authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
|
|
7497
|
+
};
|
|
7266
7498
|
await set$1(installations.appConfig, updatedInstallationEntry);
|
|
7267
7499
|
}
|
|
7268
7500
|
throw e;
|
|
@@ -7287,7 +7519,10 @@ function makeAuthTokenRequestInProgressEntry(oldEntry) {
|
|
|
7287
7519
|
requestStatus: 1 /* RequestStatus.IN_PROGRESS */,
|
|
7288
7520
|
requestTime: Date.now()
|
|
7289
7521
|
};
|
|
7290
|
-
return
|
|
7522
|
+
return {
|
|
7523
|
+
...oldEntry,
|
|
7524
|
+
authToken: inProgressAuthToken
|
|
7525
|
+
};
|
|
7291
7526
|
}
|
|
7292
7527
|
function hasAuthTokenRequestTimedOut(authToken) {
|
|
7293
7528
|
return (authToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */ &&
|
|
@@ -7472,8 +7707,8 @@ function registerInstallations() {
|
|
|
7472
7707
|
*/
|
|
7473
7708
|
registerInstallations();
|
|
7474
7709
|
registerVersion(name$3, version$3);
|
|
7475
|
-
// BUILD_TARGET will be replaced by values like
|
|
7476
|
-
registerVersion(name$3, version$3, '
|
|
7710
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
7711
|
+
registerVersion(name$3, version$3, 'esm2020');
|
|
7477
7712
|
|
|
7478
7713
|
/**
|
|
7479
7714
|
* @license
|
|
@@ -7633,7 +7868,7 @@ function insertScriptTag(dataLayerName, measurementId) {
|
|
|
7633
7868
|
// without fid. We will initialize ga-id using gtag (config) command together with fid.
|
|
7634
7869
|
const gtagScriptURL = `${GTAG_URL}?l=${dataLayerName}&id=${measurementId}`;
|
|
7635
7870
|
script.src = trustedTypesPolicy
|
|
7636
|
-
? trustedTypesPolicy
|
|
7871
|
+
? trustedTypesPolicy?.createScriptURL(gtagScriptURL)
|
|
7637
7872
|
: gtagScriptURL;
|
|
7638
7873
|
script.async = true;
|
|
7639
7874
|
document.head.appendChild(script);
|
|
@@ -7919,7 +8154,6 @@ function getHeaders(apiKey) {
|
|
|
7919
8154
|
* @param app Firebase app to fetch config for.
|
|
7920
8155
|
*/
|
|
7921
8156
|
async function fetchDynamicConfig(appFields) {
|
|
7922
|
-
var _a;
|
|
7923
8157
|
const { appId, apiKey } = appFields;
|
|
7924
8158
|
const request = {
|
|
7925
8159
|
method: 'GET',
|
|
@@ -7932,7 +8166,7 @@ async function fetchDynamicConfig(appFields) {
|
|
|
7932
8166
|
try {
|
|
7933
8167
|
// Try to get any error message text from server response.
|
|
7934
8168
|
const jsonResponse = (await response.json());
|
|
7935
|
-
if (
|
|
8169
|
+
if (jsonResponse.error?.message) {
|
|
7936
8170
|
errorMessage = jsonResponse.error.message;
|
|
7937
8171
|
}
|
|
7938
8172
|
}
|
|
@@ -7983,7 +8217,6 @@ retryData = defaultRetryData, timeoutMillis) {
|
|
|
7983
8217
|
*/
|
|
7984
8218
|
async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMillis, backoffCount }, signal, retryData = defaultRetryData // for testing
|
|
7985
8219
|
) {
|
|
7986
|
-
var _a;
|
|
7987
8220
|
const { appId, measurementId } = appFields;
|
|
7988
8221
|
// Starts with a (potentially zero) timeout to support resumption from stored state.
|
|
7989
8222
|
// Ensures the throttle end time is honored if the last attempt timed out.
|
|
@@ -7995,7 +8228,7 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
|
|
|
7995
8228
|
if (measurementId) {
|
|
7996
8229
|
logger$1.warn(`Timed out fetching this Firebase app's measurement ID from the server.` +
|
|
7997
8230
|
` Falling back to the measurement ID ${measurementId}` +
|
|
7998
|
-
` provided in the "measurementId" field in the local Firebase config. [${e
|
|
8231
|
+
` provided in the "measurementId" field in the local Firebase config. [${e?.message}]`);
|
|
7999
8232
|
return { appId, measurementId };
|
|
8000
8233
|
}
|
|
8001
8234
|
throw e;
|
|
@@ -8013,14 +8246,14 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
|
|
|
8013
8246
|
if (measurementId) {
|
|
8014
8247
|
logger$1.warn(`Failed to fetch this Firebase app's measurement ID from the server.` +
|
|
8015
8248
|
` Falling back to the measurement ID ${measurementId}` +
|
|
8016
|
-
` provided in the "measurementId" field in the local Firebase config. [${error
|
|
8249
|
+
` provided in the "measurementId" field in the local Firebase config. [${error?.message}]`);
|
|
8017
8250
|
return { appId, measurementId };
|
|
8018
8251
|
}
|
|
8019
8252
|
else {
|
|
8020
8253
|
throw e;
|
|
8021
8254
|
}
|
|
8022
8255
|
}
|
|
8023
|
-
const backoffMillis = Number(
|
|
8256
|
+
const backoffMillis = Number(error?.customData?.httpStatus) === 503
|
|
8024
8257
|
? calculateBackoffMillis(backoffCount, retryData.intervalMillis, LONG_RETRY_FACTOR)
|
|
8025
8258
|
: calculateBackoffMillis(backoffCount, retryData.intervalMillis);
|
|
8026
8259
|
// Increments backoff state.
|
|
@@ -8108,10 +8341,37 @@ async function logEvent$1(gtagFunction, initializationPromise, eventName, eventP
|
|
|
8108
8341
|
}
|
|
8109
8342
|
else {
|
|
8110
8343
|
const measurementId = await initializationPromise;
|
|
8111
|
-
const params =
|
|
8344
|
+
const params = {
|
|
8345
|
+
...eventParams,
|
|
8346
|
+
'send_to': measurementId
|
|
8347
|
+
};
|
|
8112
8348
|
gtagFunction("event" /* GtagCommand.EVENT */, eventName, params);
|
|
8113
8349
|
}
|
|
8114
8350
|
}
|
|
8351
|
+
/**
|
|
8352
|
+
* Set all other user properties other than user_id and screen_name.
|
|
8353
|
+
*
|
|
8354
|
+
* @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
|
|
8355
|
+
* @param properties Map of user properties to set
|
|
8356
|
+
*/
|
|
8357
|
+
async function setUserProperties$1(gtagFunction, initializationPromise, properties, options) {
|
|
8358
|
+
if (options && options.global) {
|
|
8359
|
+
const flatProperties = {};
|
|
8360
|
+
for (const key of Object.keys(properties)) {
|
|
8361
|
+
// use dot notation for merge behavior in gtag.js
|
|
8362
|
+
flatProperties[`user_properties.${key}`] = properties[key];
|
|
8363
|
+
}
|
|
8364
|
+
gtagFunction("set" /* GtagCommand.SET */, flatProperties);
|
|
8365
|
+
return Promise.resolve();
|
|
8366
|
+
}
|
|
8367
|
+
else {
|
|
8368
|
+
const measurementId = await initializationPromise;
|
|
8369
|
+
gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
|
|
8370
|
+
update: true,
|
|
8371
|
+
'user_properties': properties
|
|
8372
|
+
});
|
|
8373
|
+
}
|
|
8374
|
+
}
|
|
8115
8375
|
|
|
8116
8376
|
/**
|
|
8117
8377
|
* @license
|
|
@@ -8142,7 +8402,7 @@ async function validateIndexedDB() {
|
|
|
8142
8402
|
}
|
|
8143
8403
|
catch (e) {
|
|
8144
8404
|
logger$1.warn(ERROR_FACTORY.create("indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */, {
|
|
8145
|
-
errorInfo: e
|
|
8405
|
+
errorInfo: e?.toString()
|
|
8146
8406
|
}).message);
|
|
8147
8407
|
return false;
|
|
8148
8408
|
}
|
|
@@ -8163,7 +8423,6 @@ async function validateIndexedDB() {
|
|
|
8163
8423
|
* @returns Measurement ID.
|
|
8164
8424
|
*/
|
|
8165
8425
|
async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementIdToAppId, installations, gtagCore, dataLayerName, options) {
|
|
8166
|
-
var _a;
|
|
8167
8426
|
const dynamicConfigPromise = fetchDynamicConfigWithRetry(app);
|
|
8168
8427
|
// Once fetched, map measurementIds to appId, for ease of lookup in wrapped gtag function.
|
|
8169
8428
|
dynamicConfigPromise
|
|
@@ -8205,7 +8464,7 @@ async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementI
|
|
|
8205
8464
|
gtagCore('js', new Date());
|
|
8206
8465
|
// User config added first. We don't want users to accidentally overwrite
|
|
8207
8466
|
// base Firebase config properties.
|
|
8208
|
-
const configProperties =
|
|
8467
|
+
const configProperties = options?.config ?? {};
|
|
8209
8468
|
// guard against developers accidentally setting properties with prefix `firebase_`
|
|
8210
8469
|
configProperties[ORIGIN_KEY] = 'firebase';
|
|
8211
8470
|
configProperties.update = true;
|
|
@@ -8393,6 +8652,15 @@ function initializeAnalytics(app, options = {}) {
|
|
|
8393
8652
|
const analyticsInstance = analyticsProvider.initialize({ options });
|
|
8394
8653
|
return analyticsInstance;
|
|
8395
8654
|
}
|
|
8655
|
+
/**
|
|
8656
|
+
* Use gtag `config` command to set all params specified.
|
|
8657
|
+
*
|
|
8658
|
+
* @public
|
|
8659
|
+
*/
|
|
8660
|
+
function setUserProperties(analyticsInstance, properties, options) {
|
|
8661
|
+
analyticsInstance = getModularInstance(analyticsInstance);
|
|
8662
|
+
setUserProperties$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], properties, options).catch(e => logger$1.error(e));
|
|
8663
|
+
}
|
|
8396
8664
|
/**
|
|
8397
8665
|
* Sends a Google Analytics event with given `eventParams`. This method
|
|
8398
8666
|
* automatically associates this logged event with this Firebase web
|
|
@@ -8410,7 +8678,7 @@ function logEvent$2(analyticsInstance, eventName, eventParams, options) {
|
|
|
8410
8678
|
}
|
|
8411
8679
|
|
|
8412
8680
|
const name$2 = "@firebase/analytics";
|
|
8413
|
-
const version$2 = "0.10.
|
|
8681
|
+
const version$2 = "0.10.19";
|
|
8414
8682
|
|
|
8415
8683
|
/**
|
|
8416
8684
|
* The Firebase Analytics Web SDK.
|
|
@@ -8429,13 +8697,14 @@ function registerAnalytics() {
|
|
|
8429
8697
|
}, "PUBLIC" /* ComponentType.PUBLIC */));
|
|
8430
8698
|
_registerComponent(new Component('analytics-internal', internalFactory, "PRIVATE" /* ComponentType.PRIVATE */));
|
|
8431
8699
|
registerVersion(name$2, version$2);
|
|
8432
|
-
// BUILD_TARGET will be replaced by values like
|
|
8433
|
-
registerVersion(name$2, version$2, '
|
|
8700
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
8701
|
+
registerVersion(name$2, version$2, 'esm2020');
|
|
8434
8702
|
function internalFactory(container) {
|
|
8435
8703
|
try {
|
|
8436
8704
|
const analytics = container.getProvider(ANALYTICS_TYPE).getImmediate();
|
|
8437
8705
|
return {
|
|
8438
|
-
logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options)
|
|
8706
|
+
logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options),
|
|
8707
|
+
setUserProperties: (properties, options) => setUserProperties(analytics, properties, options)
|
|
8439
8708
|
};
|
|
8440
8709
|
}
|
|
8441
8710
|
catch (e) {
|
|
@@ -8447,40 +8716,6 @@ function registerAnalytics() {
|
|
|
8447
8716
|
}
|
|
8448
8717
|
registerAnalytics();
|
|
8449
8718
|
|
|
8450
|
-
/******************************************************************************
|
|
8451
|
-
Copyright (c) Microsoft Corporation.
|
|
8452
|
-
|
|
8453
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
8454
|
-
purpose with or without fee is hereby granted.
|
|
8455
|
-
|
|
8456
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8457
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
8458
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
8459
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
8460
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
8461
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
8462
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
8463
|
-
***************************************************************************** */
|
|
8464
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
function __rest(s, e) {
|
|
8468
|
-
var t = {};
|
|
8469
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
8470
|
-
t[p] = s[p];
|
|
8471
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
8472
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8473
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8474
|
-
t[p[i]] = s[p[i]];
|
|
8475
|
-
}
|
|
8476
|
-
return t;
|
|
8477
|
-
}
|
|
8478
|
-
|
|
8479
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
8480
|
-
var e = new Error(message);
|
|
8481
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
8482
|
-
};
|
|
8483
|
-
|
|
8484
8719
|
function _prodErrorMap() {
|
|
8485
8720
|
// We will include this one message in the prod error map since by the very
|
|
8486
8721
|
// nature of this error, developers will never be able to see the message
|
|
@@ -8552,7 +8787,10 @@ function _createError(authOrCode, ...rest) {
|
|
|
8552
8787
|
return createErrorInternal(authOrCode, ...rest);
|
|
8553
8788
|
}
|
|
8554
8789
|
function _errorWithCustomMessage(auth, code, message) {
|
|
8555
|
-
const errorMap =
|
|
8790
|
+
const errorMap = {
|
|
8791
|
+
...prodErrorMap(),
|
|
8792
|
+
[code]: message
|
|
8793
|
+
};
|
|
8556
8794
|
const factory = new ErrorFactory('auth', 'Firebase', errorMap);
|
|
8557
8795
|
return factory.create(code, {
|
|
8558
8796
|
appName: auth.name
|
|
@@ -8623,15 +8861,13 @@ function debugAssert(assertion, message) {
|
|
|
8623
8861
|
* limitations under the License.
|
|
8624
8862
|
*/
|
|
8625
8863
|
function _getCurrentUrl() {
|
|
8626
|
-
|
|
8627
|
-
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.href)) || '';
|
|
8864
|
+
return (typeof self !== 'undefined' && self.location?.href) || '';
|
|
8628
8865
|
}
|
|
8629
8866
|
function _isHttpOrHttps() {
|
|
8630
8867
|
return _getCurrentScheme() === 'http:' || _getCurrentScheme() === 'https:';
|
|
8631
8868
|
}
|
|
8632
8869
|
function _getCurrentScheme() {
|
|
8633
|
-
|
|
8634
|
-
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.protocol)) || null;
|
|
8870
|
+
return (typeof self !== 'undefined' && self.location?.protocol) || null;
|
|
8635
8871
|
}
|
|
8636
8872
|
|
|
8637
8873
|
/**
|
|
@@ -8934,10 +9170,21 @@ const SERVER_ERROR_MAP = {
|
|
|
8934
9170
|
* See the License for the specific language governing permissions and
|
|
8935
9171
|
* limitations under the License.
|
|
8936
9172
|
*/
|
|
9173
|
+
const CookieAuthProxiedEndpoints = [
|
|
9174
|
+
"/v1/accounts:signInWithCustomToken" /* Endpoint.SIGN_IN_WITH_CUSTOM_TOKEN */,
|
|
9175
|
+
"/v1/accounts:signInWithEmailLink" /* Endpoint.SIGN_IN_WITH_EMAIL_LINK */,
|
|
9176
|
+
"/v1/accounts:signInWithIdp" /* Endpoint.SIGN_IN_WITH_IDP */,
|
|
9177
|
+
"/v1/accounts:signInWithPassword" /* Endpoint.SIGN_IN_WITH_PASSWORD */,
|
|
9178
|
+
"/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */,
|
|
9179
|
+
"/v1/token" /* Endpoint.TOKEN */
|
|
9180
|
+
];
|
|
8937
9181
|
const DEFAULT_API_TIMEOUT_MS = new Delay(30000, 60000);
|
|
8938
9182
|
function _addTidIfNecessary(auth, request) {
|
|
8939
9183
|
if (auth.tenantId && !request.tenantId) {
|
|
8940
|
-
return
|
|
9184
|
+
return {
|
|
9185
|
+
...request,
|
|
9186
|
+
tenantId: auth.tenantId
|
|
9187
|
+
};
|
|
8941
9188
|
}
|
|
8942
9189
|
return request;
|
|
8943
9190
|
}
|
|
@@ -8955,14 +9202,20 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
|
|
|
8955
9202
|
};
|
|
8956
9203
|
}
|
|
8957
9204
|
}
|
|
8958
|
-
const query = querystring(
|
|
9205
|
+
const query = querystring({
|
|
9206
|
+
key: auth.config.apiKey,
|
|
9207
|
+
...params
|
|
9208
|
+
}).slice(1);
|
|
8959
9209
|
const headers = await auth._getAdditionalHeaders();
|
|
8960
9210
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/json';
|
|
8961
9211
|
if (auth.languageCode) {
|
|
8962
9212
|
headers["X-Firebase-Locale" /* HttpHeader.X_FIREBASE_LOCALE */] = auth.languageCode;
|
|
8963
9213
|
}
|
|
8964
|
-
const fetchArgs =
|
|
8965
|
-
|
|
9214
|
+
const fetchArgs = {
|
|
9215
|
+
method,
|
|
9216
|
+
headers,
|
|
9217
|
+
...body
|
|
9218
|
+
};
|
|
8966
9219
|
/* Security-conscious server-side frameworks tend to have built in mitigations for referrer
|
|
8967
9220
|
problems". See the Cloudflare GitHub issue #487: Error: The 'referrerPolicy' field on
|
|
8968
9221
|
'RequestInitializerDict' is not implemented."
|
|
@@ -8970,12 +9223,15 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
|
|
|
8970
9223
|
if (!isCloudflareWorker()) {
|
|
8971
9224
|
fetchArgs.referrerPolicy = 'no-referrer';
|
|
8972
9225
|
}
|
|
8973
|
-
|
|
9226
|
+
if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) {
|
|
9227
|
+
fetchArgs.credentials = 'include';
|
|
9228
|
+
}
|
|
9229
|
+
return FetchProvider.fetch()(await _getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs);
|
|
8974
9230
|
});
|
|
8975
9231
|
}
|
|
8976
9232
|
async function _performFetchWithErrorHandling(auth, customErrorMap, fetchFn) {
|
|
8977
9233
|
auth._canInitEmulator = false;
|
|
8978
|
-
const errorMap =
|
|
9234
|
+
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
|
|
8979
9235
|
try {
|
|
8980
9236
|
const networkTimeout = new NetworkTimeout(auth);
|
|
8981
9237
|
const response = await Promise.race([
|
|
@@ -9035,12 +9291,25 @@ async function _performSignInRequest(auth, method, path, request, customErrorMap
|
|
|
9035
9291
|
}
|
|
9036
9292
|
return serverResponse;
|
|
9037
9293
|
}
|
|
9038
|
-
function _getFinalTarget(auth, host, path, query) {
|
|
9294
|
+
async function _getFinalTarget(auth, host, path, query) {
|
|
9039
9295
|
const base = `${host}${path}?${query}`;
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9296
|
+
const authInternal = auth;
|
|
9297
|
+
const finalTarget = authInternal.config.emulator
|
|
9298
|
+
? _emulatorUrl(auth.config, base)
|
|
9299
|
+
: `${auth.config.apiScheme}://${base}`;
|
|
9300
|
+
// Cookie auth works by MiTMing the signIn and token endpoints from the developer's backend,
|
|
9301
|
+
// saving the idToken and refreshToken into cookies, and then redacting the refreshToken
|
|
9302
|
+
// from the response
|
|
9303
|
+
if (CookieAuthProxiedEndpoints.includes(path)) {
|
|
9304
|
+
// Persistence manager is async, we need to await it. We can't just wait for auth initialized
|
|
9305
|
+
// here since auth initialization calls this function.
|
|
9306
|
+
await authInternal._persistenceManagerAvailable;
|
|
9307
|
+
if (authInternal._getPersistenceType() === "COOKIE" /* PersistenceType.COOKIE */) {
|
|
9308
|
+
const cookiePersistence = authInternal._getPersistence();
|
|
9309
|
+
return cookiePersistence._getFinalTarget(finalTarget).toString();
|
|
9310
|
+
}
|
|
9311
|
+
}
|
|
9312
|
+
return finalTarget;
|
|
9044
9313
|
}
|
|
9045
9314
|
class NetworkTimeout {
|
|
9046
9315
|
clearNetworkTimeout() {
|
|
@@ -9150,7 +9419,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
|
|
|
9150
9419
|
const claims = _parseToken(token);
|
|
9151
9420
|
_assert(claims && claims.exp && claims.auth_time && claims.iat, userInternal.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9152
9421
|
const firebase = typeof claims.firebase === 'object' ? claims.firebase : undefined;
|
|
9153
|
-
const signInProvider = firebase
|
|
9422
|
+
const signInProvider = firebase?.['sign_in_provider'];
|
|
9154
9423
|
return {
|
|
9155
9424
|
claims,
|
|
9156
9425
|
token,
|
|
@@ -9158,7 +9427,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
|
|
|
9158
9427
|
issuedAtTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.iat)),
|
|
9159
9428
|
expirationTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.exp)),
|
|
9160
9429
|
signInProvider: signInProvider || null,
|
|
9161
|
-
signInSecondFactor:
|
|
9430
|
+
signInSecondFactor: firebase?.['sign_in_second_factor'] || null
|
|
9162
9431
|
};
|
|
9163
9432
|
}
|
|
9164
9433
|
function secondsStringToMilliseconds(seconds) {
|
|
@@ -9181,7 +9450,7 @@ function _parseToken(token) {
|
|
|
9181
9450
|
return JSON.parse(decoded);
|
|
9182
9451
|
}
|
|
9183
9452
|
catch (e) {
|
|
9184
|
-
_logError('Caught error parsing JWT payload as JSON', e
|
|
9453
|
+
_logError('Caught error parsing JWT payload as JSON', e?.toString());
|
|
9185
9454
|
return null;
|
|
9186
9455
|
}
|
|
9187
9456
|
}
|
|
@@ -9277,7 +9546,6 @@ class ProactiveRefresh {
|
|
|
9277
9546
|
}
|
|
9278
9547
|
}
|
|
9279
9548
|
getInterval(wasError) {
|
|
9280
|
-
var _a;
|
|
9281
9549
|
if (wasError) {
|
|
9282
9550
|
const interval = this.errorBackoff;
|
|
9283
9551
|
this.errorBackoff = Math.min(this.errorBackoff * 2, 960000 /* Duration.RETRY_BACKOFF_MAX */);
|
|
@@ -9286,7 +9554,7 @@ class ProactiveRefresh {
|
|
|
9286
9554
|
else {
|
|
9287
9555
|
// Reset the error backoff
|
|
9288
9556
|
this.errorBackoff = 30000 /* Duration.RETRY_BACKOFF_MIN */;
|
|
9289
|
-
const expTime =
|
|
9557
|
+
const expTime = this.user.stsTokenManager.expirationTime ?? 0;
|
|
9290
9558
|
const interval = expTime - Date.now() - 300000 /* Duration.OFFSET */;
|
|
9291
9559
|
return Math.max(0, interval);
|
|
9292
9560
|
}
|
|
@@ -9307,7 +9575,7 @@ class ProactiveRefresh {
|
|
|
9307
9575
|
}
|
|
9308
9576
|
catch (e) {
|
|
9309
9577
|
// Only retry on network errors
|
|
9310
|
-
if (
|
|
9578
|
+
if (e?.code ===
|
|
9311
9579
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
9312
9580
|
this.schedule(/* wasError */ true);
|
|
9313
9581
|
}
|
|
@@ -9373,14 +9641,13 @@ class UserMetadata {
|
|
|
9373
9641
|
* limitations under the License.
|
|
9374
9642
|
*/
|
|
9375
9643
|
async function _reloadWithoutSaving(user) {
|
|
9376
|
-
var _a;
|
|
9377
9644
|
const auth = user.auth;
|
|
9378
9645
|
const idToken = await user.getIdToken();
|
|
9379
9646
|
const response = await _logoutIfInvalidated(user, getAccountInfo(auth, { idToken }));
|
|
9380
|
-
_assert(response
|
|
9647
|
+
_assert(response?.users.length, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9381
9648
|
const coreAccount = response.users[0];
|
|
9382
9649
|
user._notifyReloadListener(coreAccount);
|
|
9383
|
-
const newProviderData =
|
|
9650
|
+
const newProviderData = coreAccount.providerUserInfo?.length
|
|
9384
9651
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
9385
9652
|
: [];
|
|
9386
9653
|
const providerData = mergeProviderData(user.providerData, newProviderData);
|
|
@@ -9390,7 +9657,7 @@ async function _reloadWithoutSaving(user) {
|
|
|
9390
9657
|
// On the other hand, if it was not anonymous before, it should never be
|
|
9391
9658
|
// considered anonymous now.
|
|
9392
9659
|
const oldIsAnonymous = user.isAnonymous;
|
|
9393
|
-
const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !
|
|
9660
|
+
const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !providerData?.length;
|
|
9394
9661
|
const isAnonymous = !oldIsAnonymous ? false : newIsAnonymous;
|
|
9395
9662
|
const updates = {
|
|
9396
9663
|
uid: coreAccount.localId,
|
|
@@ -9427,8 +9694,7 @@ function mergeProviderData(original, newData) {
|
|
|
9427
9694
|
return [...deduped, ...newData];
|
|
9428
9695
|
}
|
|
9429
9696
|
function extractProviderData(providers) {
|
|
9430
|
-
return providers.map((
|
|
9431
|
-
var { providerId } = _a, provider = __rest(_a, ["providerId"]);
|
|
9697
|
+
return providers.map(({ providerId, ...provider }) => {
|
|
9432
9698
|
return {
|
|
9433
9699
|
providerId,
|
|
9434
9700
|
uid: provider.rawId || '',
|
|
@@ -9463,14 +9729,19 @@ async function requestStsToken(auth, refreshToken) {
|
|
|
9463
9729
|
'refresh_token': refreshToken
|
|
9464
9730
|
}).slice(1);
|
|
9465
9731
|
const { tokenApiHost, apiKey } = auth.config;
|
|
9466
|
-
const url = _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
|
|
9732
|
+
const url = await _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
|
|
9467
9733
|
const headers = await auth._getAdditionalHeaders();
|
|
9468
9734
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/x-www-form-urlencoded';
|
|
9469
|
-
|
|
9735
|
+
const options = {
|
|
9470
9736
|
method: "POST" /* HttpMethod.POST */,
|
|
9471
9737
|
headers,
|
|
9472
9738
|
body
|
|
9473
|
-
}
|
|
9739
|
+
};
|
|
9740
|
+
if (auth.emulatorConfig &&
|
|
9741
|
+
isCloudWorkstation(auth.emulatorConfig.host)) {
|
|
9742
|
+
options.credentials = 'include';
|
|
9743
|
+
}
|
|
9744
|
+
return FetchProvider.fetch()(url, options);
|
|
9474
9745
|
});
|
|
9475
9746
|
// The response comes back in snake_case. Convert to camel:
|
|
9476
9747
|
return {
|
|
@@ -9615,8 +9886,7 @@ function assertStringOrUndefined(assertion, appName) {
|
|
|
9615
9886
|
_assert(typeof assertion === 'string' || typeof assertion === 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, { appName });
|
|
9616
9887
|
}
|
|
9617
9888
|
class UserImpl {
|
|
9618
|
-
constructor(
|
|
9619
|
-
var { uid, auth, stsTokenManager } = _a, opt = __rest(_a, ["uid", "auth", "stsTokenManager"]);
|
|
9889
|
+
constructor({ uid, auth, stsTokenManager, ...opt }) {
|
|
9620
9890
|
// For the user object, provider is always Firebase.
|
|
9621
9891
|
this.providerId = "firebase" /* ProviderId.FIREBASE */;
|
|
9622
9892
|
this.proactiveRefresh = new ProactiveRefresh(this);
|
|
@@ -9664,12 +9934,16 @@ class UserImpl {
|
|
|
9664
9934
|
this.phoneNumber = user.phoneNumber;
|
|
9665
9935
|
this.isAnonymous = user.isAnonymous;
|
|
9666
9936
|
this.tenantId = user.tenantId;
|
|
9667
|
-
this.providerData = user.providerData.map(userInfo => (
|
|
9937
|
+
this.providerData = user.providerData.map(userInfo => ({ ...userInfo }));
|
|
9668
9938
|
this.metadata._copy(user.metadata);
|
|
9669
9939
|
this.stsTokenManager._assign(user.stsTokenManager);
|
|
9670
9940
|
}
|
|
9671
9941
|
_clone(auth) {
|
|
9672
|
-
const newUser = new UserImpl(
|
|
9942
|
+
const newUser = new UserImpl({
|
|
9943
|
+
...this,
|
|
9944
|
+
auth,
|
|
9945
|
+
stsTokenManager: this.stsTokenManager._clone()
|
|
9946
|
+
});
|
|
9673
9947
|
newUser.metadata._copy(this.metadata);
|
|
9674
9948
|
return newUser;
|
|
9675
9949
|
}
|
|
@@ -9724,26 +9998,40 @@ class UserImpl {
|
|
|
9724
9998
|
return this.auth.signOut();
|
|
9725
9999
|
}
|
|
9726
10000
|
toJSON() {
|
|
9727
|
-
return
|
|
10001
|
+
return {
|
|
10002
|
+
uid: this.uid,
|
|
10003
|
+
email: this.email || undefined,
|
|
10004
|
+
emailVerified: this.emailVerified,
|
|
10005
|
+
displayName: this.displayName || undefined,
|
|
10006
|
+
isAnonymous: this.isAnonymous,
|
|
10007
|
+
photoURL: this.photoURL || undefined,
|
|
10008
|
+
phoneNumber: this.phoneNumber || undefined,
|
|
10009
|
+
tenantId: this.tenantId || undefined,
|
|
10010
|
+
providerData: this.providerData.map(userInfo => ({ ...userInfo })),
|
|
10011
|
+
stsTokenManager: this.stsTokenManager.toJSON(),
|
|
9728
10012
|
// Redirect event ID must be maintained in case there is a pending
|
|
9729
10013
|
// redirect event.
|
|
9730
|
-
_redirectEventId: this._redirectEventId
|
|
10014
|
+
_redirectEventId: this._redirectEventId,
|
|
10015
|
+
...this.metadata.toJSON(),
|
|
9731
10016
|
// Required for compatibility with the legacy SDK (go/firebase-auth-sdk-persistence-parsing):
|
|
9732
|
-
apiKey: this.auth.config.apiKey,
|
|
10017
|
+
apiKey: this.auth.config.apiKey,
|
|
10018
|
+
appName: this.auth.name
|
|
10019
|
+
// Missing authDomain will be tolerated by the legacy SDK.
|
|
10020
|
+
// stsTokenManager.apiKey isn't actually required (despite the legacy SDK persisting it).
|
|
10021
|
+
};
|
|
9733
10022
|
}
|
|
9734
10023
|
get refreshToken() {
|
|
9735
10024
|
return this.stsTokenManager.refreshToken || '';
|
|
9736
10025
|
}
|
|
9737
10026
|
static _fromJSON(auth, object) {
|
|
9738
|
-
|
|
9739
|
-
const
|
|
9740
|
-
const
|
|
9741
|
-
const
|
|
9742
|
-
const
|
|
9743
|
-
const
|
|
9744
|
-
const
|
|
9745
|
-
const
|
|
9746
|
-
const lastLoginAt = (_h = object.lastLoginAt) !== null && _h !== void 0 ? _h : undefined;
|
|
10027
|
+
const displayName = object.displayName ?? undefined;
|
|
10028
|
+
const email = object.email ?? undefined;
|
|
10029
|
+
const phoneNumber = object.phoneNumber ?? undefined;
|
|
10030
|
+
const photoURL = object.photoURL ?? undefined;
|
|
10031
|
+
const tenantId = object.tenantId ?? undefined;
|
|
10032
|
+
const _redirectEventId = object._redirectEventId ?? undefined;
|
|
10033
|
+
const createdAt = object.createdAt ?? undefined;
|
|
10034
|
+
const lastLoginAt = object.lastLoginAt ?? undefined;
|
|
9747
10035
|
const { uid, emailVerified, isAnonymous, providerData, stsTokenManager: plainObjectTokenManager } = object;
|
|
9748
10036
|
_assert(uid && plainObjectTokenManager, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9749
10037
|
const stsTokenManager = StsTokenManager.fromJSON(this.name, plainObjectTokenManager);
|
|
@@ -9773,7 +10061,7 @@ class UserImpl {
|
|
|
9773
10061
|
lastLoginAt
|
|
9774
10062
|
});
|
|
9775
10063
|
if (providerData && Array.isArray(providerData)) {
|
|
9776
|
-
user.providerData = providerData.map(userInfo => (
|
|
10064
|
+
user.providerData = providerData.map(userInfo => ({ ...userInfo }));
|
|
9777
10065
|
}
|
|
9778
10066
|
if (_redirectEventId) {
|
|
9779
10067
|
user._redirectEventId = _redirectEventId;
|
|
@@ -9810,7 +10098,7 @@ class UserImpl {
|
|
|
9810
10098
|
const providerData = coreAccount.providerUserInfo !== undefined
|
|
9811
10099
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
9812
10100
|
: [];
|
|
9813
|
-
const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !
|
|
10101
|
+
const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !providerData?.length;
|
|
9814
10102
|
const stsTokenManager = new StsTokenManager();
|
|
9815
10103
|
stsTokenManager.updateFromIdToken(idToken);
|
|
9816
10104
|
// Initialize the Firebase Auth user.
|
|
@@ -9832,7 +10120,7 @@ class UserImpl {
|
|
|
9832
10120
|
providerData,
|
|
9833
10121
|
metadata: new UserMetadata(coreAccount.createdAt, coreAccount.lastLoginAt),
|
|
9834
10122
|
isAnonymous: !(coreAccount.email && coreAccount.passwordHash) &&
|
|
9835
|
-
!
|
|
10123
|
+
!providerData?.length
|
|
9836
10124
|
};
|
|
9837
10125
|
Object.assign(user, updates);
|
|
9838
10126
|
return user;
|
|
@@ -9954,7 +10242,17 @@ class PersistenceUserManager {
|
|
|
9954
10242
|
}
|
|
9955
10243
|
async getCurrentUser() {
|
|
9956
10244
|
const blob = await this.persistence._get(this.fullUserKey);
|
|
9957
|
-
|
|
10245
|
+
if (!blob) {
|
|
10246
|
+
return null;
|
|
10247
|
+
}
|
|
10248
|
+
if (typeof blob === 'string') {
|
|
10249
|
+
const response = await getAccountInfo(this.auth, { idToken: blob }).catch(() => undefined);
|
|
10250
|
+
if (!response) {
|
|
10251
|
+
return null;
|
|
10252
|
+
}
|
|
10253
|
+
return UserImpl._fromGetAccountInfoResponse(this.auth, response, blob);
|
|
10254
|
+
}
|
|
10255
|
+
return UserImpl._fromJSON(this.auth, blob);
|
|
9958
10256
|
}
|
|
9959
10257
|
removeCurrentUser() {
|
|
9960
10258
|
return this.persistence._remove(this.fullUserKey);
|
|
@@ -10001,7 +10299,19 @@ class PersistenceUserManager {
|
|
|
10001
10299
|
try {
|
|
10002
10300
|
const blob = await persistence._get(key);
|
|
10003
10301
|
if (blob) {
|
|
10004
|
-
|
|
10302
|
+
let user;
|
|
10303
|
+
if (typeof blob === 'string') {
|
|
10304
|
+
const response = await getAccountInfo(auth, {
|
|
10305
|
+
idToken: blob
|
|
10306
|
+
}).catch(() => undefined);
|
|
10307
|
+
if (!response) {
|
|
10308
|
+
break;
|
|
10309
|
+
}
|
|
10310
|
+
user = await UserImpl._fromGetAccountInfoResponse(auth, response, blob);
|
|
10311
|
+
}
|
|
10312
|
+
else {
|
|
10313
|
+
user = UserImpl._fromJSON(auth, blob); // throws for unparsable blob (wrong format)
|
|
10314
|
+
}
|
|
10005
10315
|
if (persistence !== selectedPersistence) {
|
|
10006
10316
|
userToMigrate = user;
|
|
10007
10317
|
}
|
|
@@ -10009,7 +10319,7 @@ class PersistenceUserManager {
|
|
|
10009
10319
|
break;
|
|
10010
10320
|
}
|
|
10011
10321
|
}
|
|
10012
|
-
catch
|
|
10322
|
+
catch { }
|
|
10013
10323
|
}
|
|
10014
10324
|
// If we find the user in a persistence that does support migration, use
|
|
10015
10325
|
// that migration path (of only persistences that support migration)
|
|
@@ -10032,7 +10342,7 @@ class PersistenceUserManager {
|
|
|
10032
10342
|
try {
|
|
10033
10343
|
await persistence._remove(key);
|
|
10034
10344
|
}
|
|
10035
|
-
catch
|
|
10345
|
+
catch { }
|
|
10036
10346
|
}
|
|
10037
10347
|
}));
|
|
10038
10348
|
return new PersistenceUserManager(selectedPersistence, auth, userKey);
|
|
@@ -10102,7 +10412,7 @@ function _getBrowserName(userAgent) {
|
|
|
10102
10412
|
// Most modern browsers have name/version at end of user agent string.
|
|
10103
10413
|
const re = /([a-zA-Z\d\.]+)\/[a-zA-Z\d\.]*$/;
|
|
10104
10414
|
const matches = userAgent.match(re);
|
|
10105
|
-
if (
|
|
10415
|
+
if (matches?.length === 2) {
|
|
10106
10416
|
return matches[1];
|
|
10107
10417
|
}
|
|
10108
10418
|
}
|
|
@@ -10138,8 +10448,7 @@ function _isIOS(ua = getUA()) {
|
|
|
10138
10448
|
(/macintosh/i.test(ua) && /mobile/i.test(ua)));
|
|
10139
10449
|
}
|
|
10140
10450
|
function _isIOSStandalone(ua = getUA()) {
|
|
10141
|
-
|
|
10142
|
-
return _isIOS(ua) && !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.standalone);
|
|
10451
|
+
return _isIOS(ua) && !!window.navigator?.standalone;
|
|
10143
10452
|
}
|
|
10144
10453
|
function _isIE10() {
|
|
10145
10454
|
return isIE() && document.documentMode === 10;
|
|
@@ -10270,7 +10579,7 @@ class AuthMiddlewareQueue {
|
|
|
10270
10579
|
}
|
|
10271
10580
|
}
|
|
10272
10581
|
throw this.auth._errorFactory.create("login-blocked" /* AuthErrorCode.LOGIN_BLOCKED */, {
|
|
10273
|
-
originalMessage: e
|
|
10582
|
+
originalMessage: e?.message
|
|
10274
10583
|
});
|
|
10275
10584
|
}
|
|
10276
10585
|
}
|
|
@@ -10328,13 +10637,12 @@ const MINIMUM_MIN_PASSWORD_LENGTH = 6;
|
|
|
10328
10637
|
*/
|
|
10329
10638
|
class PasswordPolicyImpl {
|
|
10330
10639
|
constructor(response) {
|
|
10331
|
-
var _a, _b, _c, _d;
|
|
10332
10640
|
// Only include custom strength options defined in the response.
|
|
10333
10641
|
const responseOptions = response.customStrengthOptions;
|
|
10334
10642
|
this.customStrengthOptions = {};
|
|
10335
10643
|
// TODO: Remove once the backend is updated to include the minimum min password length instead of undefined when there is no minimum length set.
|
|
10336
10644
|
this.customStrengthOptions.minPasswordLength =
|
|
10337
|
-
|
|
10645
|
+
responseOptions.minPasswordLength ?? MINIMUM_MIN_PASSWORD_LENGTH;
|
|
10338
10646
|
if (responseOptions.maxPasswordLength) {
|
|
10339
10647
|
this.customStrengthOptions.maxPasswordLength =
|
|
10340
10648
|
responseOptions.maxPasswordLength;
|
|
@@ -10361,12 +10669,11 @@ class PasswordPolicyImpl {
|
|
|
10361
10669
|
}
|
|
10362
10670
|
// Use an empty string if no non-alphanumeric characters are specified in the response.
|
|
10363
10671
|
this.allowedNonAlphanumericCharacters =
|
|
10364
|
-
|
|
10365
|
-
this.forceUpgradeOnSignin =
|
|
10672
|
+
response.allowedNonAlphanumericCharacters?.join('') ?? '';
|
|
10673
|
+
this.forceUpgradeOnSignin = response.forceUpgradeOnSignin ?? false;
|
|
10366
10674
|
this.schemaVersion = response.schemaVersion;
|
|
10367
10675
|
}
|
|
10368
10676
|
validatePassword(password) {
|
|
10369
|
-
var _a, _b, _c, _d, _e, _f;
|
|
10370
10677
|
const status = {
|
|
10371
10678
|
isValid: true,
|
|
10372
10679
|
passwordPolicy: this
|
|
@@ -10375,12 +10682,12 @@ class PasswordPolicyImpl {
|
|
|
10375
10682
|
this.validatePasswordLengthOptions(password, status);
|
|
10376
10683
|
this.validatePasswordCharacterOptions(password, status);
|
|
10377
10684
|
// Combine the status into single isValid property.
|
|
10378
|
-
status.isValid && (status.isValid =
|
|
10379
|
-
status.isValid && (status.isValid =
|
|
10380
|
-
status.isValid && (status.isValid =
|
|
10381
|
-
status.isValid && (status.isValid =
|
|
10382
|
-
status.isValid && (status.isValid =
|
|
10383
|
-
status.isValid && (status.isValid =
|
|
10685
|
+
status.isValid && (status.isValid = status.meetsMinPasswordLength ?? true);
|
|
10686
|
+
status.isValid && (status.isValid = status.meetsMaxPasswordLength ?? true);
|
|
10687
|
+
status.isValid && (status.isValid = status.containsLowercaseLetter ?? true);
|
|
10688
|
+
status.isValid && (status.isValid = status.containsUppercaseLetter ?? true);
|
|
10689
|
+
status.isValid && (status.isValid = status.containsNumericCharacter ?? true);
|
|
10690
|
+
status.isValid && (status.isValid = status.containsNonAlphanumericCharacter ?? true);
|
|
10384
10691
|
return status;
|
|
10385
10692
|
}
|
|
10386
10693
|
/**
|
|
@@ -10495,6 +10802,7 @@ class AuthImpl {
|
|
|
10495
10802
|
this._tenantRecaptchaConfigs = {};
|
|
10496
10803
|
this._projectPasswordPolicy = null;
|
|
10497
10804
|
this._tenantPasswordPolicies = {};
|
|
10805
|
+
this._resolvePersistenceManagerAvailable = undefined;
|
|
10498
10806
|
// Tracks the last notified UID for state change listeners to prevent
|
|
10499
10807
|
// repeated calls to the callbacks. Undefined means it's never been
|
|
10500
10808
|
// called, whereas null means it's been called with a signed out user
|
|
@@ -10505,6 +10813,9 @@ class AuthImpl {
|
|
|
10505
10813
|
this.frameworks = [];
|
|
10506
10814
|
this.name = app.name;
|
|
10507
10815
|
this.clientVersion = config.sdkClientVersion;
|
|
10816
|
+
// TODO(jamesdaniels) explore less hacky way to do this, cookie authentication needs
|
|
10817
|
+
// persistenceMananger to be available. see _getFinalTarget for more context
|
|
10818
|
+
this._persistenceManagerAvailable = new Promise(resolve => (this._resolvePersistenceManagerAvailable = resolve));
|
|
10508
10819
|
}
|
|
10509
10820
|
_initializeWithPersistence(persistenceHierarchy, popupRedirectResolver) {
|
|
10510
10821
|
if (popupRedirectResolver) {
|
|
@@ -10513,17 +10824,17 @@ class AuthImpl {
|
|
|
10513
10824
|
// Have to check for app deletion throughout initialization (after each
|
|
10514
10825
|
// promise resolution)
|
|
10515
10826
|
this._initializationPromise = this.queue(async () => {
|
|
10516
|
-
var _a, _b;
|
|
10517
10827
|
if (this._deleted) {
|
|
10518
10828
|
return;
|
|
10519
10829
|
}
|
|
10520
10830
|
this.persistenceManager = await PersistenceUserManager.create(this, persistenceHierarchy);
|
|
10831
|
+
this._resolvePersistenceManagerAvailable?.();
|
|
10521
10832
|
if (this._deleted) {
|
|
10522
10833
|
return;
|
|
10523
10834
|
}
|
|
10524
10835
|
// Initialize the resolver early if necessary (only applicable to web:
|
|
10525
10836
|
// this will cause the iframe to load immediately in certain cases)
|
|
10526
|
-
if (
|
|
10837
|
+
if (this._popupRedirectResolver?._shouldInitProactively) {
|
|
10527
10838
|
// If this fails, don't halt auth loading
|
|
10528
10839
|
try {
|
|
10529
10840
|
await this._popupRedirectResolver._initialize(this);
|
|
@@ -10533,7 +10844,7 @@ class AuthImpl {
|
|
|
10533
10844
|
}
|
|
10534
10845
|
}
|
|
10535
10846
|
await this.initializeCurrentUser(popupRedirectResolver);
|
|
10536
|
-
this.lastNotifiedUid =
|
|
10847
|
+
this.lastNotifiedUid = this.currentUser?.uid || null;
|
|
10537
10848
|
if (this._deleted) {
|
|
10538
10849
|
return;
|
|
10539
10850
|
}
|
|
@@ -10578,7 +10889,6 @@ class AuthImpl {
|
|
|
10578
10889
|
}
|
|
10579
10890
|
}
|
|
10580
10891
|
async initializeCurrentUser(popupRedirectResolver) {
|
|
10581
|
-
var _a;
|
|
10582
10892
|
if (_isFirebaseServerApp(this.app)) {
|
|
10583
10893
|
const idToken = this.app.settings.authIdToken;
|
|
10584
10894
|
if (idToken) {
|
|
@@ -10598,15 +10908,15 @@ class AuthImpl {
|
|
|
10598
10908
|
let needsTocheckMiddleware = false;
|
|
10599
10909
|
if (popupRedirectResolver && this.config.authDomain) {
|
|
10600
10910
|
await this.getOrInitRedirectPersistenceManager();
|
|
10601
|
-
const redirectUserEventId =
|
|
10602
|
-
const storedUserEventId = futureCurrentUser
|
|
10911
|
+
const redirectUserEventId = this.redirectUser?._redirectEventId;
|
|
10912
|
+
const storedUserEventId = futureCurrentUser?._redirectEventId;
|
|
10603
10913
|
const result = await this.tryRedirectSignIn(popupRedirectResolver);
|
|
10604
10914
|
// If the stored user (i.e. the old "currentUser") has a redirectId that
|
|
10605
10915
|
// matches the redirect user, then we want to initially sign in with the
|
|
10606
10916
|
// new user object from result.
|
|
10607
10917
|
// TODO(samgho): More thoroughly test all of this
|
|
10608
10918
|
if ((!redirectUserEventId || redirectUserEventId === storedUserEventId) &&
|
|
10609
|
-
|
|
10919
|
+
result?.user) {
|
|
10610
10920
|
futureCurrentUser = result.user;
|
|
10611
10921
|
needsTocheckMiddleware = true;
|
|
10612
10922
|
}
|
|
@@ -10681,7 +10991,7 @@ class AuthImpl {
|
|
|
10681
10991
|
await _reloadWithoutSaving(user);
|
|
10682
10992
|
}
|
|
10683
10993
|
catch (e) {
|
|
10684
|
-
if (
|
|
10994
|
+
if (e?.code !==
|
|
10685
10995
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
10686
10996
|
// Something's wrong with the user's token. Log them out and remove
|
|
10687
10997
|
// them from storage
|
|
@@ -10787,9 +11097,12 @@ class AuthImpl {
|
|
|
10787
11097
|
this._tenantPasswordPolicies[this.tenantId] = passwordPolicy;
|
|
10788
11098
|
}
|
|
10789
11099
|
}
|
|
10790
|
-
|
|
11100
|
+
_getPersistenceType() {
|
|
10791
11101
|
return this.assertedPersistence.persistence.type;
|
|
10792
11102
|
}
|
|
11103
|
+
_getPersistence() {
|
|
11104
|
+
return this.assertedPersistence.persistence;
|
|
11105
|
+
}
|
|
10793
11106
|
_updateErrorMap(errorMap) {
|
|
10794
11107
|
this._errorFactory = new ErrorFactory('auth', 'Firebase', errorMap());
|
|
10795
11108
|
}
|
|
@@ -10835,12 +11148,11 @@ class AuthImpl {
|
|
|
10835
11148
|
}
|
|
10836
11149
|
}
|
|
10837
11150
|
toJSON() {
|
|
10838
|
-
var _a;
|
|
10839
11151
|
return {
|
|
10840
11152
|
apiKey: this.config.apiKey,
|
|
10841
11153
|
authDomain: this.config.authDomain,
|
|
10842
11154
|
appName: this.name,
|
|
10843
|
-
currentUser:
|
|
11155
|
+
currentUser: this._currentUser?.toJSON()
|
|
10844
11156
|
};
|
|
10845
11157
|
}
|
|
10846
11158
|
async _setRedirectUser(user, popupRedirectResolver) {
|
|
@@ -10861,16 +11173,15 @@ class AuthImpl {
|
|
|
10861
11173
|
return this.redirectPersistenceManager;
|
|
10862
11174
|
}
|
|
10863
11175
|
async _redirectUserForId(id) {
|
|
10864
|
-
var _a, _b;
|
|
10865
11176
|
// Make sure we've cleared any pending persistence actions if we're not in
|
|
10866
11177
|
// the initializer
|
|
10867
11178
|
if (this._isInitialized) {
|
|
10868
11179
|
await this.queue(async () => { });
|
|
10869
11180
|
}
|
|
10870
|
-
if (
|
|
11181
|
+
if (this._currentUser?._redirectEventId === id) {
|
|
10871
11182
|
return this._currentUser;
|
|
10872
11183
|
}
|
|
10873
|
-
if (
|
|
11184
|
+
if (this.redirectUser?._redirectEventId === id) {
|
|
10874
11185
|
return this.redirectUser;
|
|
10875
11186
|
}
|
|
10876
11187
|
return null;
|
|
@@ -10906,12 +11217,11 @@ class AuthImpl {
|
|
|
10906
11217
|
return this.currentUser;
|
|
10907
11218
|
}
|
|
10908
11219
|
notifyAuthListeners() {
|
|
10909
|
-
var _a, _b;
|
|
10910
11220
|
if (!this._isInitialized) {
|
|
10911
11221
|
return;
|
|
10912
11222
|
}
|
|
10913
11223
|
this.idTokenSubscription.next(this.currentUser);
|
|
10914
|
-
const currentUid =
|
|
11224
|
+
const currentUid = this.currentUser?.uid ?? null;
|
|
10915
11225
|
if (this.lastNotifiedUid !== currentUid) {
|
|
10916
11226
|
this.lastNotifiedUid = currentUid;
|
|
10917
11227
|
this.authStateSubscription.next(this.currentUser);
|
|
@@ -10996,7 +11306,6 @@ class AuthImpl {
|
|
|
10996
11306
|
return this.frameworks;
|
|
10997
11307
|
}
|
|
10998
11308
|
async _getAdditionalHeaders() {
|
|
10999
|
-
var _a;
|
|
11000
11309
|
// Additional headers on every request
|
|
11001
11310
|
const headers = {
|
|
11002
11311
|
["X-Client-Version" /* HttpHeader.X_CLIENT_VERSION */]: this.clientVersion
|
|
@@ -11005,10 +11314,11 @@ class AuthImpl {
|
|
|
11005
11314
|
headers["X-Firebase-gmpid" /* HttpHeader.X_FIREBASE_GMPID */] = this.app.options.appId;
|
|
11006
11315
|
}
|
|
11007
11316
|
// If the heartbeat service exists, add the heartbeat string
|
|
11008
|
-
const heartbeatsHeader = await
|
|
11317
|
+
const heartbeatsHeader = await this.heartbeatServiceProvider
|
|
11009
11318
|
.getImmediate({
|
|
11010
11319
|
optional: true
|
|
11011
|
-
})
|
|
11320
|
+
})
|
|
11321
|
+
?.getHeartbeatsHeader();
|
|
11012
11322
|
if (heartbeatsHeader) {
|
|
11013
11323
|
headers["X-Firebase-Client" /* HttpHeader.X_FIREBASE_CLIENT */] = heartbeatsHeader;
|
|
11014
11324
|
}
|
|
@@ -11020,17 +11330,20 @@ class AuthImpl {
|
|
|
11020
11330
|
return headers;
|
|
11021
11331
|
}
|
|
11022
11332
|
async _getAppCheckToken() {
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11333
|
+
if (_isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
|
|
11334
|
+
return this.app.settings.appCheckToken;
|
|
11335
|
+
}
|
|
11336
|
+
const appCheckTokenResult = await this.appCheckServiceProvider
|
|
11337
|
+
.getImmediate({ optional: true })
|
|
11338
|
+
?.getToken();
|
|
11339
|
+
if (appCheckTokenResult?.error) {
|
|
11027
11340
|
// Context: appCheck.getToken() will never throw even if an error happened.
|
|
11028
11341
|
// In the error case, a dummy token will be returned along with an error field describing
|
|
11029
11342
|
// the error. In general, we shouldn't care about the error condition and just use
|
|
11030
11343
|
// the token (actual or dummy) to send requests.
|
|
11031
11344
|
_logWarn(`Error while retrieving App Check token: ${appCheckTokenResult.error}`);
|
|
11032
11345
|
}
|
|
11033
|
-
return appCheckTokenResult
|
|
11346
|
+
return appCheckTokenResult?.token;
|
|
11034
11347
|
}
|
|
11035
11348
|
}
|
|
11036
11349
|
/**
|
|
@@ -11138,7 +11451,7 @@ function initializeAuth(app, deps) {
|
|
|
11138
11451
|
if (provider.isInitialized()) {
|
|
11139
11452
|
const auth = provider.getImmediate();
|
|
11140
11453
|
const initialOptions = provider.getOptions();
|
|
11141
|
-
if (deepEqual(initialOptions, deps
|
|
11454
|
+
if (deepEqual(initialOptions, deps ?? {})) {
|
|
11142
11455
|
return auth;
|
|
11143
11456
|
}
|
|
11144
11457
|
else {
|
|
@@ -11149,15 +11462,15 @@ function initializeAuth(app, deps) {
|
|
|
11149
11462
|
return auth;
|
|
11150
11463
|
}
|
|
11151
11464
|
function _initializeAuthInstance(auth, deps) {
|
|
11152
|
-
const persistence =
|
|
11465
|
+
const persistence = deps?.persistence || [];
|
|
11153
11466
|
const hierarchy = (Array.isArray(persistence) ? persistence : [persistence]).map(_getInstance);
|
|
11154
|
-
if (deps
|
|
11467
|
+
if (deps?.errorMap) {
|
|
11155
11468
|
auth._updateErrorMap(deps.errorMap);
|
|
11156
11469
|
}
|
|
11157
11470
|
// This promise is intended to float; auth initialization happens in the
|
|
11158
11471
|
// background, meanwhile the auth object may be used by the app.
|
|
11159
11472
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
11160
|
-
auth._initializeWithPersistence(hierarchy, deps
|
|
11473
|
+
auth._initializeWithPersistence(hierarchy, deps?.popupRedirectResolver);
|
|
11161
11474
|
}
|
|
11162
11475
|
|
|
11163
11476
|
/**
|
|
@@ -11184,22 +11497,41 @@ function _initializeAuthInstance(auth, deps) {
|
|
|
11184
11497
|
*/
|
|
11185
11498
|
function connectAuthEmulator(auth, url, options) {
|
|
11186
11499
|
const authInternal = _castAuth(auth);
|
|
11187
|
-
_assert(authInternal._canInitEmulator, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11188
11500
|
_assert(/^https?:\/\//.test(url), authInternal, "invalid-emulator-scheme" /* AuthErrorCode.INVALID_EMULATOR_SCHEME */);
|
|
11189
|
-
const disableWarnings = !!
|
|
11501
|
+
const disableWarnings = !!options?.disableWarnings;
|
|
11190
11502
|
const protocol = extractProtocol(url);
|
|
11191
11503
|
const { host, port } = extractHostAndPort(url);
|
|
11192
11504
|
const portStr = port === null ? '' : `:${port}`;
|
|
11193
11505
|
// Always replace path with "/" (even if input url had no path at all, or had a different one).
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
authInternal.emulatorConfig = Object.freeze({
|
|
11506
|
+
const emulator = { url: `${protocol}//${host}${portStr}/` };
|
|
11507
|
+
const emulatorConfig = Object.freeze({
|
|
11197
11508
|
host,
|
|
11198
11509
|
port,
|
|
11199
11510
|
protocol: protocol.replace(':', ''),
|
|
11200
11511
|
options: Object.freeze({ disableWarnings })
|
|
11201
11512
|
});
|
|
11202
|
-
|
|
11513
|
+
// There are a few scenarios to guard against if the Auth instance has already started:
|
|
11514
|
+
if (!authInternal._canInitEmulator) {
|
|
11515
|
+
// Applications may not initialize the emulator for the first time if Auth has already started
|
|
11516
|
+
// to make network requests.
|
|
11517
|
+
_assert(authInternal.config.emulator && authInternal.emulatorConfig, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11518
|
+
// Applications may not alter the configuration of the emulator (aka pass a different config)
|
|
11519
|
+
// once Auth has started to make network requests.
|
|
11520
|
+
_assert(deepEqual(emulator, authInternal.config.emulator) &&
|
|
11521
|
+
deepEqual(emulatorConfig, authInternal.emulatorConfig), authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11522
|
+
// It's valid, however, to invoke connectAuthEmulator() after Auth has started making
|
|
11523
|
+
// connections, so long as the config matches the existing config. This results in a no-op.
|
|
11524
|
+
return;
|
|
11525
|
+
}
|
|
11526
|
+
authInternal.config.emulator = emulator;
|
|
11527
|
+
authInternal.emulatorConfig = emulatorConfig;
|
|
11528
|
+
authInternal.settings.appVerificationDisabledForTesting = true;
|
|
11529
|
+
// Workaround to get cookies in Firebase Studio
|
|
11530
|
+
if (isCloudWorkstation(host)) {
|
|
11531
|
+
void pingServer(`${protocol}//${host}${portStr}`);
|
|
11532
|
+
updateEmulatorBanner('Auth', true);
|
|
11533
|
+
}
|
|
11534
|
+
else {
|
|
11203
11535
|
emitEmulatorWarning();
|
|
11204
11536
|
}
|
|
11205
11537
|
}
|
|
@@ -11438,7 +11770,7 @@ class OAuthCredential extends AuthCredential {
|
|
|
11438
11770
|
*/
|
|
11439
11771
|
static fromJSON(json) {
|
|
11440
11772
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
11441
|
-
const { providerId, signInMethod
|
|
11773
|
+
const { providerId, signInMethod, ...rest } = obj;
|
|
11442
11774
|
if (!providerId || !signInMethod) {
|
|
11443
11775
|
return null;
|
|
11444
11776
|
}
|
|
@@ -11715,7 +12047,7 @@ class FacebookAuthProvider extends BaseOAuthProvider {
|
|
|
11715
12047
|
try {
|
|
11716
12048
|
return FacebookAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
11717
12049
|
}
|
|
11718
|
-
catch
|
|
12050
|
+
catch {
|
|
11719
12051
|
return null;
|
|
11720
12052
|
}
|
|
11721
12053
|
}
|
|
@@ -11837,7 +12169,7 @@ class GoogleAuthProvider extends BaseOAuthProvider {
|
|
|
11837
12169
|
try {
|
|
11838
12170
|
return GoogleAuthProvider.credential(oauthIdToken, oauthAccessToken);
|
|
11839
12171
|
}
|
|
11840
|
-
catch
|
|
12172
|
+
catch {
|
|
11841
12173
|
return null;
|
|
11842
12174
|
}
|
|
11843
12175
|
}
|
|
@@ -11948,7 +12280,7 @@ class GithubAuthProvider extends BaseOAuthProvider {
|
|
|
11948
12280
|
try {
|
|
11949
12281
|
return GithubAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
11950
12282
|
}
|
|
11951
|
-
catch
|
|
12283
|
+
catch {
|
|
11952
12284
|
return null;
|
|
11953
12285
|
}
|
|
11954
12286
|
}
|
|
@@ -12059,7 +12391,7 @@ class TwitterAuthProvider extends BaseOAuthProvider {
|
|
|
12059
12391
|
try {
|
|
12060
12392
|
return TwitterAuthProvider.credential(oauthAccessToken, oauthTokenSecret);
|
|
12061
12393
|
}
|
|
12062
|
-
catch
|
|
12394
|
+
catch {
|
|
12063
12395
|
return null;
|
|
12064
12396
|
}
|
|
12065
12397
|
}
|
|
@@ -12142,7 +12474,6 @@ function providerIdForResponse(response) {
|
|
|
12142
12474
|
*/
|
|
12143
12475
|
class MultiFactorError extends FirebaseError {
|
|
12144
12476
|
constructor(auth, error, operationType, user) {
|
|
12145
|
-
var _a;
|
|
12146
12477
|
super(error.code, error.message);
|
|
12147
12478
|
this.operationType = operationType;
|
|
12148
12479
|
this.user = user;
|
|
@@ -12150,7 +12481,7 @@ class MultiFactorError extends FirebaseError {
|
|
|
12150
12481
|
Object.setPrototypeOf(this, MultiFactorError.prototype);
|
|
12151
12482
|
this.customData = {
|
|
12152
12483
|
appName: auth.name,
|
|
12153
|
-
tenantId:
|
|
12484
|
+
tenantId: auth.tenantId ?? undefined,
|
|
12154
12485
|
_serverResponse: error.customData._serverResponse,
|
|
12155
12486
|
operationType
|
|
12156
12487
|
};
|
|
@@ -12208,7 +12539,7 @@ async function _reauthenticate(user, credential, bypassAuthState = false) {
|
|
|
12208
12539
|
}
|
|
12209
12540
|
catch (e) {
|
|
12210
12541
|
// Convert user deleted error into user mismatch
|
|
12211
|
-
if (
|
|
12542
|
+
if (e?.code === `auth/${"user-not-found" /* AuthErrorCode.USER_DELETED */}`) {
|
|
12212
12543
|
_fail(auth, "user-mismatch" /* AuthErrorCode.USER_MISMATCH */);
|
|
12213
12544
|
}
|
|
12214
12545
|
throw e;
|
|
@@ -12398,7 +12729,7 @@ class BrowserPersistenceClass {
|
|
|
12398
12729
|
this.storage.removeItem(STORAGE_AVAILABLE_KEY);
|
|
12399
12730
|
return Promise.resolve(true);
|
|
12400
12731
|
}
|
|
12401
|
-
catch
|
|
12732
|
+
catch {
|
|
12402
12733
|
return Promise.resolve(false);
|
|
12403
12734
|
}
|
|
12404
12735
|
}
|
|
@@ -12739,7 +13070,7 @@ class Receiver {
|
|
|
12739
13070
|
const messageEvent = event;
|
|
12740
13071
|
const { eventId, eventType, data } = messageEvent.data;
|
|
12741
13072
|
const handlers = this.handlersMap[eventType];
|
|
12742
|
-
if (!
|
|
13073
|
+
if (!handlers?.size) {
|
|
12743
13074
|
return;
|
|
12744
13075
|
}
|
|
12745
13076
|
messageEvent.ports[0].postMessage({
|
|
@@ -12975,20 +13306,19 @@ function _isWorker() {
|
|
|
12975
13306
|
typeof _window()['importScripts'] === 'function');
|
|
12976
13307
|
}
|
|
12977
13308
|
async function _getActiveServiceWorker() {
|
|
12978
|
-
if (!
|
|
13309
|
+
if (!navigator?.serviceWorker) {
|
|
12979
13310
|
return null;
|
|
12980
13311
|
}
|
|
12981
13312
|
try {
|
|
12982
13313
|
const registration = await navigator.serviceWorker.ready;
|
|
12983
13314
|
return registration.active;
|
|
12984
13315
|
}
|
|
12985
|
-
catch
|
|
13316
|
+
catch {
|
|
12986
13317
|
return null;
|
|
12987
13318
|
}
|
|
12988
13319
|
}
|
|
12989
13320
|
function _getServiceWorkerController() {
|
|
12990
|
-
|
|
12991
|
-
return ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker) === null || _a === void 0 ? void 0 : _a.controller) || null;
|
|
13321
|
+
return navigator?.serviceWorker?.controller || null;
|
|
12992
13322
|
}
|
|
12993
13323
|
function _getWorkerGlobalScope() {
|
|
12994
13324
|
return _isWorker() ? self : null;
|
|
@@ -13171,7 +13501,6 @@ class IndexedDBLocalPersistence {
|
|
|
13171
13501
|
* may not resolve.
|
|
13172
13502
|
*/
|
|
13173
13503
|
async initializeSender() {
|
|
13174
|
-
var _a, _b;
|
|
13175
13504
|
// Check to see if there's an active service worker.
|
|
13176
13505
|
this.activeServiceWorker = await _getActiveServiceWorker();
|
|
13177
13506
|
if (!this.activeServiceWorker) {
|
|
@@ -13183,8 +13512,8 @@ class IndexedDBLocalPersistence {
|
|
|
13183
13512
|
if (!results) {
|
|
13184
13513
|
return;
|
|
13185
13514
|
}
|
|
13186
|
-
if (
|
|
13187
|
-
|
|
13515
|
+
if (results[0]?.fulfilled &&
|
|
13516
|
+
results[0]?.value.includes("keyChanged" /* _EventType.KEY_CHANGED */)) {
|
|
13188
13517
|
this.serviceWorkerReceiverAvailable = true;
|
|
13189
13518
|
}
|
|
13190
13519
|
}
|
|
@@ -13210,7 +13539,7 @@ class IndexedDBLocalPersistence {
|
|
|
13210
13539
|
? 800 /* _TimeoutDuration.LONG_ACK */
|
|
13211
13540
|
: 50 /* _TimeoutDuration.ACK */);
|
|
13212
13541
|
}
|
|
13213
|
-
catch
|
|
13542
|
+
catch {
|
|
13214
13543
|
// This is a best effort approach. Ignore errors.
|
|
13215
13544
|
}
|
|
13216
13545
|
}
|
|
@@ -13224,7 +13553,7 @@ class IndexedDBLocalPersistence {
|
|
|
13224
13553
|
await _deleteObject(db, STORAGE_AVAILABLE_KEY);
|
|
13225
13554
|
return true;
|
|
13226
13555
|
}
|
|
13227
|
-
catch
|
|
13556
|
+
catch { }
|
|
13228
13557
|
return false;
|
|
13229
13558
|
}
|
|
13230
13559
|
async _withPendingWrite(write) {
|
|
@@ -13594,8 +13923,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
|
|
|
13594
13923
|
this.pollUserCancellation();
|
|
13595
13924
|
}
|
|
13596
13925
|
get eventId() {
|
|
13597
|
-
|
|
13598
|
-
return ((_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.associatedEvent) || null;
|
|
13926
|
+
return this.authWindow?.associatedEvent || null;
|
|
13599
13927
|
}
|
|
13600
13928
|
cancel() {
|
|
13601
13929
|
this.reject(_createError(this.auth, "cancelled-popup-request" /* AuthErrorCode.EXPIRED_POPUP_REQUEST */));
|
|
@@ -13613,8 +13941,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
|
|
|
13613
13941
|
}
|
|
13614
13942
|
pollUserCancellation() {
|
|
13615
13943
|
const poll = () => {
|
|
13616
|
-
|
|
13617
|
-
if ((_b = (_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.window) === null || _b === void 0 ? void 0 : _b.closed) {
|
|
13944
|
+
if (this.authWindow?.window?.closed) {
|
|
13618
13945
|
// Make sure that there is sufficient time for whatever action to
|
|
13619
13946
|
// complete. The window could have closed but the sign in network
|
|
13620
13947
|
// call could still be in flight. This is specifically true for
|
|
@@ -13814,9 +14141,8 @@ class AuthEventManager {
|
|
|
13814
14141
|
return handled;
|
|
13815
14142
|
}
|
|
13816
14143
|
sendToConsumer(event, consumer) {
|
|
13817
|
-
var _a;
|
|
13818
14144
|
if (event.error && !isNullRedirectEvent(event)) {
|
|
13819
|
-
const code =
|
|
14145
|
+
const code = event.error.code?.split('auth/')[1] ||
|
|
13820
14146
|
"internal-error" /* AuthErrorCode.INTERNAL_ERROR */;
|
|
13821
14147
|
consumer.onError(_createError(this.auth, code));
|
|
13822
14148
|
}
|
|
@@ -13846,7 +14172,7 @@ function eventUid(e) {
|
|
|
13846
14172
|
}
|
|
13847
14173
|
function isNullRedirectEvent({ type, error }) {
|
|
13848
14174
|
return (type === "unknown" /* AuthEventType.UNKNOWN */ &&
|
|
13849
|
-
|
|
14175
|
+
error?.code === `auth/${"no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */}`);
|
|
13850
14176
|
}
|
|
13851
14177
|
function isRedirectEvent(event) {
|
|
13852
14178
|
switch (event.type) {
|
|
@@ -13911,7 +14237,7 @@ async function _validateOrigin(auth) {
|
|
|
13911
14237
|
return;
|
|
13912
14238
|
}
|
|
13913
14239
|
}
|
|
13914
|
-
catch
|
|
14240
|
+
catch {
|
|
13915
14241
|
// Do nothing if there's a URL error; just continue searching
|
|
13916
14242
|
}
|
|
13917
14243
|
}
|
|
@@ -13974,7 +14300,7 @@ function resetUnloadedGapiModules() {
|
|
|
13974
14300
|
// Get gapix.beacon context.
|
|
13975
14301
|
const beacon = _window().___jsl;
|
|
13976
14302
|
// Get current hint.
|
|
13977
|
-
if (beacon
|
|
14303
|
+
if (beacon?.H) {
|
|
13978
14304
|
// Get gapi hint.
|
|
13979
14305
|
for (const hint of Object.keys(beacon.H)) {
|
|
13980
14306
|
// Requested modules.
|
|
@@ -13995,7 +14321,6 @@ function resetUnloadedGapiModules() {
|
|
|
13995
14321
|
}
|
|
13996
14322
|
function loadGapi(auth) {
|
|
13997
14323
|
return new Promise((resolve, reject) => {
|
|
13998
|
-
var _a, _b, _c;
|
|
13999
14324
|
// Function to run when gapi.load is ready.
|
|
14000
14325
|
function loadGapiIframe() {
|
|
14001
14326
|
// The developer may have tried to previously run gapi.load and failed.
|
|
@@ -14018,11 +14343,11 @@ function loadGapi(auth) {
|
|
|
14018
14343
|
timeout: NETWORK_TIMEOUT.get()
|
|
14019
14344
|
});
|
|
14020
14345
|
}
|
|
14021
|
-
if (
|
|
14346
|
+
if (_window().gapi?.iframes?.Iframe) {
|
|
14022
14347
|
// If gapi.iframes.Iframe available, resolve.
|
|
14023
14348
|
resolve(gapi.iframes.getContext());
|
|
14024
14349
|
}
|
|
14025
|
-
else if (!!
|
|
14350
|
+
else if (!!_window().gapi?.load) {
|
|
14026
14351
|
// Gapi loader ready, load gapi.iframes.
|
|
14027
14352
|
loadGapiIframe();
|
|
14028
14353
|
}
|
|
@@ -14195,8 +14520,13 @@ function _open(auth, url, name, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT)
|
|
|
14195
14520
|
const top = Math.max((window.screen.availHeight - height) / 2, 0).toString();
|
|
14196
14521
|
const left = Math.max((window.screen.availWidth - width) / 2, 0).toString();
|
|
14197
14522
|
let target = '';
|
|
14198
|
-
const options =
|
|
14199
|
-
|
|
14523
|
+
const options = {
|
|
14524
|
+
...BASE_POPUP_OPTIONS,
|
|
14525
|
+
width: width.toString(),
|
|
14526
|
+
height: height.toString(),
|
|
14527
|
+
top,
|
|
14528
|
+
left
|
|
14529
|
+
};
|
|
14200
14530
|
// Chrome iOS 7 and 8 is returning an undefined popup win when target is
|
|
14201
14531
|
// specified, even though the popup is not necessarily blocked.
|
|
14202
14532
|
const ua = getUA().toLowerCase();
|
|
@@ -14356,8 +14686,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14356
14686
|
// Wrapping in async even though we don't await anywhere in order
|
|
14357
14687
|
// to make sure errors are raised as promise rejections
|
|
14358
14688
|
async _openPopup(auth, provider, authType, eventId) {
|
|
14359
|
-
|
|
14360
|
-
debugAssert((_a = this.eventManagers[auth._key()]) === null || _a === void 0 ? void 0 : _a.manager, '_initialize() not called before _openPopup()');
|
|
14689
|
+
debugAssert(this.eventManagers[auth._key()]?.manager, '_initialize() not called before _openPopup()');
|
|
14361
14690
|
const url = await _getRedirectUrl(auth, provider, authType, _getCurrentUrl(), eventId);
|
|
14362
14691
|
return _open(auth, url, _generateEventId());
|
|
14363
14692
|
}
|
|
@@ -14392,7 +14721,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14392
14721
|
const iframe = await _openIframe(auth);
|
|
14393
14722
|
const manager = new AuthEventManager(auth);
|
|
14394
14723
|
iframe.register('authEvent', (iframeEvent) => {
|
|
14395
|
-
_assert(iframeEvent
|
|
14724
|
+
_assert(iframeEvent?.authEvent, auth, "invalid-auth-event" /* AuthErrorCode.INVALID_AUTH_EVENT */);
|
|
14396
14725
|
// TODO: Consider splitting redirect and popup events earlier on
|
|
14397
14726
|
const handled = manager.onEvent(iframeEvent.authEvent);
|
|
14398
14727
|
return { status: handled ? "ACK" /* GapiOutcome.ACK */ : "ERROR" /* GapiOutcome.ERROR */ };
|
|
@@ -14404,8 +14733,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14404
14733
|
_isIframeWebStorageSupported(auth, cb) {
|
|
14405
14734
|
const iframe = this.iframes[auth._key()];
|
|
14406
14735
|
iframe.send(WEB_STORAGE_SUPPORT_KEY, { type: WEB_STORAGE_SUPPORT_KEY }, result => {
|
|
14407
|
-
|
|
14408
|
-
const isSupported = (_a = result === null || result === void 0 ? void 0 : result[0]) === null || _a === void 0 ? void 0 : _a[WEB_STORAGE_SUPPORT_KEY];
|
|
14736
|
+
const isSupported = result?.[0]?.[WEB_STORAGE_SUPPORT_KEY];
|
|
14409
14737
|
if (isSupported !== undefined) {
|
|
14410
14738
|
cb(!!isSupported);
|
|
14411
14739
|
}
|
|
@@ -14436,7 +14764,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14436
14764
|
const browserPopupRedirectResolver = BrowserPopupRedirectResolver;
|
|
14437
14765
|
|
|
14438
14766
|
var name$1 = "@firebase/auth";
|
|
14439
|
-
var version$1 = "1.
|
|
14767
|
+
var version$1 = "1.11.1";
|
|
14440
14768
|
|
|
14441
14769
|
/**
|
|
14442
14770
|
* @license
|
|
@@ -14460,9 +14788,8 @@ class AuthInterop {
|
|
|
14460
14788
|
this.internalListeners = new Map();
|
|
14461
14789
|
}
|
|
14462
14790
|
getUid() {
|
|
14463
|
-
var _a;
|
|
14464
14791
|
this.assertAuthConfigured();
|
|
14465
|
-
return
|
|
14792
|
+
return this.auth.currentUser?.uid || null;
|
|
14466
14793
|
}
|
|
14467
14794
|
async getToken(forceRefresh) {
|
|
14468
14795
|
this.assertAuthConfigured();
|
|
@@ -14479,7 +14806,7 @@ class AuthInterop {
|
|
|
14479
14806
|
return;
|
|
14480
14807
|
}
|
|
14481
14808
|
const unsubscribe = this.auth.onIdTokenChanged(user => {
|
|
14482
|
-
listener(
|
|
14809
|
+
listener(user?.stsTokenManager.accessToken || null);
|
|
14483
14810
|
});
|
|
14484
14811
|
this.internalListeners.set(listener, unsubscribe);
|
|
14485
14812
|
this.updateProactiveRefresh();
|
|
@@ -14578,8 +14905,8 @@ function registerAuth(clientPlatform) {
|
|
|
14578
14905
|
return (auth => new AuthInterop(auth))(auth);
|
|
14579
14906
|
}, "PRIVATE" /* ComponentType.PRIVATE */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
|
|
14580
14907
|
registerVersion(name$1, version$1, getVersionForPlatform(clientPlatform));
|
|
14581
|
-
// BUILD_TARGET will be replaced by values like
|
|
14582
|
-
registerVersion(name$1, version$1, '
|
|
14908
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
14909
|
+
registerVersion(name$1, version$1, 'esm2020');
|
|
14583
14910
|
}
|
|
14584
14911
|
|
|
14585
14912
|
/**
|
|
@@ -14609,7 +14936,7 @@ const mintCookieFactory = (url) => async (user) => {
|
|
|
14609
14936
|
return;
|
|
14610
14937
|
}
|
|
14611
14938
|
// Specifically trip null => undefined when logged out, to delete any existing cookie
|
|
14612
|
-
const idToken = idTokenResult
|
|
14939
|
+
const idToken = idTokenResult?.token;
|
|
14613
14940
|
if (lastPostedIdToken === idToken) {
|
|
14614
14941
|
return;
|
|
14615
14942
|
}
|
|
@@ -14664,8 +14991,7 @@ function getAuth(app = getApp()) {
|
|
|
14664
14991
|
return auth;
|
|
14665
14992
|
}
|
|
14666
14993
|
function getScriptParentElement() {
|
|
14667
|
-
|
|
14668
|
-
return (_b = (_a = document.getElementsByTagName('head')) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : document;
|
|
14994
|
+
return document.getElementsByTagName('head')?.[0] ?? document;
|
|
14669
14995
|
}
|
|
14670
14996
|
_setExternalJSProvider({
|
|
14671
14997
|
loadJS(url) {
|
|
@@ -14691,7 +15017,7 @@ _setExternalJSProvider({
|
|
|
14691
15017
|
registerAuth("Browser" /* ClientPlatform.BROWSER */);
|
|
14692
15018
|
|
|
14693
15019
|
const name = "@firebase/database";
|
|
14694
|
-
const version = "1.0
|
|
15020
|
+
const version = "1.1.0";
|
|
14695
15021
|
|
|
14696
15022
|
/**
|
|
14697
15023
|
* @license
|
|
@@ -15388,15 +15714,24 @@ const setTimeoutNonBlocking = function (fn, time) {
|
|
|
15388
15714
|
* Abstraction around AppCheck's token fetching capabilities.
|
|
15389
15715
|
*/
|
|
15390
15716
|
class AppCheckTokenProvider {
|
|
15391
|
-
constructor(
|
|
15392
|
-
this.appName_ = appName_;
|
|
15717
|
+
constructor(app, appCheckProvider) {
|
|
15393
15718
|
this.appCheckProvider = appCheckProvider;
|
|
15394
|
-
this.
|
|
15719
|
+
this.appName = app.name;
|
|
15720
|
+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
|
|
15721
|
+
this.serverAppAppCheckToken = app.settings.appCheckToken;
|
|
15722
|
+
}
|
|
15723
|
+
this.appCheck = appCheckProvider?.getImmediate({ optional: true });
|
|
15395
15724
|
if (!this.appCheck) {
|
|
15396
|
-
appCheckProvider
|
|
15725
|
+
appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck));
|
|
15397
15726
|
}
|
|
15398
15727
|
}
|
|
15399
15728
|
getToken(forceRefresh) {
|
|
15729
|
+
if (this.serverAppAppCheckToken) {
|
|
15730
|
+
if (forceRefresh) {
|
|
15731
|
+
throw new Error('Attempted reuse of `FirebaseServerApp.appCheckToken` after previous usage failed.');
|
|
15732
|
+
}
|
|
15733
|
+
return Promise.resolve({ token: this.serverAppAppCheckToken });
|
|
15734
|
+
}
|
|
15400
15735
|
if (!this.appCheck) {
|
|
15401
15736
|
return new Promise((resolve, reject) => {
|
|
15402
15737
|
// Support delayed initialization of FirebaseAppCheck. This allows our
|
|
@@ -15416,11 +15751,12 @@ class AppCheckTokenProvider {
|
|
|
15416
15751
|
return this.appCheck.getToken(forceRefresh);
|
|
15417
15752
|
}
|
|
15418
15753
|
addTokenChangeListener(listener) {
|
|
15419
|
-
|
|
15420
|
-
|
|
15754
|
+
this.appCheckProvider
|
|
15755
|
+
?.get()
|
|
15756
|
+
.then(appCheck => appCheck.addTokenListener(listener));
|
|
15421
15757
|
}
|
|
15422
15758
|
notifyForInvalidToken() {
|
|
15423
|
-
warn(`Provided AppCheck credentials for the app named "${this.
|
|
15759
|
+
warn(`Provided AppCheck credentials for the app named "${this.appName}" ` +
|
|
15424
15760
|
'are invalid. This usually indicates your app was not initialized correctly.');
|
|
15425
15761
|
}
|
|
15426
15762
|
}
|
|
@@ -15606,7 +15942,7 @@ class RepoInfo {
|
|
|
15606
15942
|
* @param nodeAdmin - Whether this instance uses Admin SDK credentials
|
|
15607
15943
|
* @param persistenceKey - Override the default session persistence storage key
|
|
15608
15944
|
*/
|
|
15609
|
-
constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false) {
|
|
15945
|
+
constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false, emulatorOptions = null) {
|
|
15610
15946
|
this.secure = secure;
|
|
15611
15947
|
this.namespace = namespace;
|
|
15612
15948
|
this.webSocketOnly = webSocketOnly;
|
|
@@ -15614,6 +15950,7 @@ class RepoInfo {
|
|
|
15614
15950
|
this.persistenceKey = persistenceKey;
|
|
15615
15951
|
this.includeNamespaceInQueryParams = includeNamespaceInQueryParams;
|
|
15616
15952
|
this.isUsingEmulator = isUsingEmulator;
|
|
15953
|
+
this.emulatorOptions = emulatorOptions;
|
|
15617
15954
|
this._host = host.toLowerCase();
|
|
15618
15955
|
this._domain = this._host.substr(this._host.indexOf('.') + 1);
|
|
15619
15956
|
this.internalHost =
|
|
@@ -17100,7 +17437,9 @@ class Connection {
|
|
|
17100
17437
|
if (MESSAGE_DATA in controlData) {
|
|
17101
17438
|
const payload = controlData[MESSAGE_DATA];
|
|
17102
17439
|
if (cmd === SERVER_HELLO) {
|
|
17103
|
-
const handshakePayload =
|
|
17440
|
+
const handshakePayload = {
|
|
17441
|
+
...payload
|
|
17442
|
+
};
|
|
17104
17443
|
if (this.repoInfo_.isUsingEmulator) {
|
|
17105
17444
|
// Upon connecting, the emulator will pass the hostname that it's aware of, but we prefer the user's set hostname via `connectDatabaseEmulator` over what the emulator passes.
|
|
17106
17445
|
handshakePayload.h = this.repoInfo_.host;
|
|
@@ -18366,7 +18705,7 @@ class PersistentConnection extends ServerActions {
|
|
|
18366
18705
|
}
|
|
18367
18706
|
this.lastConnectionEstablishedTime_ = null;
|
|
18368
18707
|
}
|
|
18369
|
-
const timeSinceLastConnectAttempt = new Date().getTime() - this.lastConnectionAttemptTime_;
|
|
18708
|
+
const timeSinceLastConnectAttempt = Math.max(0, new Date().getTime() - this.lastConnectionAttemptTime_);
|
|
18370
18709
|
let reconnectDelay = Math.max(0, this.reconnectDelay_ - timeSinceLastConnectAttempt);
|
|
18371
18710
|
reconnectDelay = Math.random() * reconnectDelay;
|
|
18372
18711
|
this.log_('Trying to reconnect in ' + reconnectDelay + 'ms');
|
|
@@ -19908,9 +20247,9 @@ class IndexMap {
|
|
|
19908
20247
|
newIndex = fallbackObject;
|
|
19909
20248
|
}
|
|
19910
20249
|
const indexName = indexDefinition.toString();
|
|
19911
|
-
const newIndexSet =
|
|
20250
|
+
const newIndexSet = { ...this.indexSet_ };
|
|
19912
20251
|
newIndexSet[indexName] = indexDefinition;
|
|
19913
|
-
const newIndexes =
|
|
20252
|
+
const newIndexes = { ...this.indexes_ };
|
|
19914
20253
|
newIndexes[indexName] = newIndex;
|
|
19915
20254
|
return new IndexMap(newIndexes, newIndexSet);
|
|
19916
20255
|
}
|
|
@@ -21639,7 +21978,7 @@ class StatsListener {
|
|
|
21639
21978
|
}
|
|
21640
21979
|
get() {
|
|
21641
21980
|
const newStats = this.collection_.get();
|
|
21642
|
-
const delta =
|
|
21981
|
+
const delta = { ...newStats };
|
|
21643
21982
|
if (this.last_) {
|
|
21644
21983
|
each(this.last_, (stat, value) => {
|
|
21645
21984
|
delta[stat] = delta[stat] - value;
|
|
@@ -27123,10 +27462,13 @@ let useRestClient = false;
|
|
|
27123
27462
|
/**
|
|
27124
27463
|
* Update an existing `Repo` in place to point to a new host/port.
|
|
27125
27464
|
*/
|
|
27126
|
-
function repoManagerApplyEmulatorSettings(repo,
|
|
27127
|
-
|
|
27128
|
-
|
|
27129
|
-
|
|
27465
|
+
function repoManagerApplyEmulatorSettings(repo, hostAndPort, emulatorOptions, tokenProvider) {
|
|
27466
|
+
const portIndex = hostAndPort.lastIndexOf(':');
|
|
27467
|
+
const host = hostAndPort.substring(0, portIndex);
|
|
27468
|
+
const useSsl = isCloudWorkstation(host);
|
|
27469
|
+
repo.repoInfo_ = new RepoInfo(hostAndPort,
|
|
27470
|
+
/* secure= */ useSsl, repo.repoInfo_.namespace, repo.repoInfo_.webSocketOnly, repo.repoInfo_.nodeAdmin, repo.repoInfo_.persistenceKey, repo.repoInfo_.includeNamespaceInQueryParams,
|
|
27471
|
+
/*isUsingEmulator=*/ true, emulatorOptions);
|
|
27130
27472
|
if (tokenProvider) {
|
|
27131
27473
|
repo.authTokenProvider_ = tokenProvider;
|
|
27132
27474
|
}
|
|
@@ -27165,7 +27507,7 @@ function repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url, no
|
|
|
27165
27507
|
fatal('Database URL must point to the root of a Firebase Database ' +
|
|
27166
27508
|
'(not including a child path).');
|
|
27167
27509
|
}
|
|
27168
|
-
const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app
|
|
27510
|
+
const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app, appCheckProvider));
|
|
27169
27511
|
return new Database(repo, app);
|
|
27170
27512
|
}
|
|
27171
27513
|
/**
|
|
@@ -27281,10 +27623,17 @@ function getDatabase(app = getApp(), url) {
|
|
|
27281
27623
|
function connectDatabaseEmulator(db, host, port, options = {}) {
|
|
27282
27624
|
db = getModularInstance(db);
|
|
27283
27625
|
db._checkNotDeleted('useEmulator');
|
|
27626
|
+
const hostAndPort = `${host}:${port}`;
|
|
27627
|
+
const repo = db._repoInternal;
|
|
27284
27628
|
if (db._instanceStarted) {
|
|
27285
|
-
|
|
27629
|
+
// If the instance has already been started, then silenty fail if this function is called again
|
|
27630
|
+
// with the same parameters. If the parameters differ then assert.
|
|
27631
|
+
if (hostAndPort === db._repoInternal.repoInfo_.host &&
|
|
27632
|
+
deepEqual(options, repo.repoInfo_.emulatorOptions)) {
|
|
27633
|
+
return;
|
|
27634
|
+
}
|
|
27635
|
+
fatal('connectDatabaseEmulator() cannot initialize or alter the emulator configuration after the database instance has started.');
|
|
27286
27636
|
}
|
|
27287
|
-
const repo = db._repoInternal;
|
|
27288
27637
|
let tokenProvider = undefined;
|
|
27289
27638
|
if (repo.repoInfo_.nodeAdmin) {
|
|
27290
27639
|
if (options.mockUserToken) {
|
|
@@ -27298,8 +27647,13 @@ function connectDatabaseEmulator(db, host, port, options = {}) {
|
|
|
27298
27647
|
: createMockUserToken(options.mockUserToken, db.app.options.projectId);
|
|
27299
27648
|
tokenProvider = new EmulatorTokenProvider(token);
|
|
27300
27649
|
}
|
|
27650
|
+
// Workaround to get cookies in Firebase Studio
|
|
27651
|
+
if (isCloudWorkstation(host)) {
|
|
27652
|
+
void pingServer(host);
|
|
27653
|
+
updateEmulatorBanner('Database', true);
|
|
27654
|
+
}
|
|
27301
27655
|
// Modify the repo to apply emulator settings
|
|
27302
|
-
repoManagerApplyEmulatorSettings(repo,
|
|
27656
|
+
repoManagerApplyEmulatorSettings(repo, hostAndPort, options, tokenProvider);
|
|
27303
27657
|
}
|
|
27304
27658
|
|
|
27305
27659
|
/**
|
|
@@ -27327,8 +27681,8 @@ function registerDatabase(variant) {
|
|
|
27327
27681
|
return repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url);
|
|
27328
27682
|
}, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
27329
27683
|
registerVersion(name, version, variant);
|
|
27330
|
-
// BUILD_TARGET will be replaced by values like
|
|
27331
|
-
registerVersion(name, version, '
|
|
27684
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
27685
|
+
registerVersion(name, version, 'esm2020');
|
|
27332
27686
|
}
|
|
27333
27687
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27334
27688
|
PersistentConnection.prototype.simpleListen = function (pathString, onComplete) {
|
|
@@ -27370,12 +27724,11 @@ let databaseReadyQueue = [];
|
|
|
27370
27724
|
* Execute all queued callbacks now that database is ready
|
|
27371
27725
|
*/
|
|
27372
27726
|
function flushDatabaseReadyQueue(state) {
|
|
27373
|
-
var _a;
|
|
27374
27727
|
const queue = [...databaseReadyQueue];
|
|
27375
27728
|
databaseReadyQueue = []; // Clear queue before executing to avoid re-queuing
|
|
27376
27729
|
queue.forEach(callback => callback());
|
|
27377
27730
|
// Also notify the global callback if set
|
|
27378
|
-
|
|
27731
|
+
state.onFirebaseReady?.();
|
|
27379
27732
|
}
|
|
27380
27733
|
function initFirebase(state) {
|
|
27381
27734
|
try {
|
|
@@ -27439,7 +27792,7 @@ function isFirebaseAuthenticated(state) {
|
|
|
27439
27792
|
async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
27440
27793
|
if (!app && !initFirebase(state))
|
|
27441
27794
|
return;
|
|
27442
|
-
const {
|
|
27795
|
+
const { netskraflUserId, locale, projectId, loginMethod } = state;
|
|
27443
27796
|
auth = getAuth(app);
|
|
27444
27797
|
if (!auth) {
|
|
27445
27798
|
console.error("Failed to initialize Firebase Auth");
|
|
@@ -27454,14 +27807,14 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
|
27454
27807
|
logEvent("sign_up", {
|
|
27455
27808
|
locale,
|
|
27456
27809
|
method: loginMethod,
|
|
27457
|
-
userid:
|
|
27810
|
+
userid: netskraflUserId
|
|
27458
27811
|
});
|
|
27459
27812
|
}
|
|
27460
27813
|
// And always log a login event
|
|
27461
27814
|
logEvent("login", {
|
|
27462
27815
|
locale,
|
|
27463
27816
|
method: loginMethod,
|
|
27464
|
-
userid:
|
|
27817
|
+
userid: netskraflUserId
|
|
27465
27818
|
});
|
|
27466
27819
|
}
|
|
27467
27820
|
});
|
|
@@ -27480,7 +27833,7 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
|
27480
27833
|
if (!analytics) {
|
|
27481
27834
|
console.error("Failed to initialize Firebase Analytics");
|
|
27482
27835
|
}
|
|
27483
|
-
initPresence(projectId,
|
|
27836
|
+
initPresence(projectId, netskraflUserId, locale);
|
|
27484
27837
|
}
|
|
27485
27838
|
function initPresence(projectId, userId, locale) {
|
|
27486
27839
|
// Ensure that this user connection is recorded in Firebase
|
|
@@ -27643,7 +27996,6 @@ const checkCircuitBreakerReset = () => {
|
|
|
27643
27996
|
* - Request deduplication: Only one login attempt at a time
|
|
27644
27997
|
*/
|
|
27645
27998
|
const ensureAuthenticated = async (state) => {
|
|
27646
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
27647
27999
|
// Check and potentially reset circuit breaker
|
|
27648
28000
|
checkCircuitBreakerReset();
|
|
27649
28001
|
// Circuit breaker check - fail fast if too many recent failures
|
|
@@ -27708,22 +28060,22 @@ const ensureAuthenticated = async (state) => {
|
|
|
27708
28060
|
// Success! Update global state and persist authentication
|
|
27709
28061
|
// ========================================================================
|
|
27710
28062
|
// Update the user's ID to the internal one used by the backend and Firebase
|
|
27711
|
-
state.
|
|
27712
|
-
state.account = result.account || state.
|
|
28063
|
+
state.netskraflUserId = result.user_id || state.netskraflUserId;
|
|
28064
|
+
state.account = result.account || state.netskraflUserId;
|
|
27713
28065
|
state.userNick = result.nickname || state.userNick;
|
|
27714
28066
|
state.firebaseApiKey = result.firebase_api_key || state.firebaseApiKey;
|
|
27715
28067
|
// Load state flags and preferences
|
|
27716
|
-
state.beginner =
|
|
27717
|
-
state.fairPlay =
|
|
27718
|
-
state.ready =
|
|
27719
|
-
state.readyTimed =
|
|
27720
|
-
state.audio =
|
|
27721
|
-
state.fanfare =
|
|
27722
|
-
state.hasPaid =
|
|
28068
|
+
state.beginner = result.prefs?.beginner ?? true;
|
|
28069
|
+
state.fairPlay = result.prefs?.fairplay ?? false;
|
|
28070
|
+
state.ready = result.prefs?.ready ?? true;
|
|
28071
|
+
state.readyTimed = result.prefs?.ready_timed ?? true;
|
|
28072
|
+
state.audio = result.prefs?.audio ?? false;
|
|
28073
|
+
state.fanfare = result.prefs?.fanfare ?? false;
|
|
28074
|
+
state.hasPaid = result.prefs?.haspaid ?? false;
|
|
27723
28075
|
// Save authentication settings to sessionStorage
|
|
27724
28076
|
saveAuthSettings({
|
|
27725
28077
|
userEmail: state.userEmail,
|
|
27726
|
-
|
|
28078
|
+
netskraflUserId: state.netskraflUserId,
|
|
27727
28079
|
userNick: state.userNick,
|
|
27728
28080
|
firebaseApiKey: state.firebaseApiKey,
|
|
27729
28081
|
beginner: state.beginner,
|
|
@@ -27821,7 +28173,7 @@ const requestMoves = (state, options) => {
|
|
|
27821
28173
|
const headers = {
|
|
27822
28174
|
"Content-Type": "application/json; charset=UTF-8",
|
|
27823
28175
|
Authorization: `Bearer ${state.movesAccessKey}`,
|
|
27824
|
-
...options
|
|
28176
|
+
...options?.headers,
|
|
27825
28177
|
};
|
|
27826
28178
|
return m.request({
|
|
27827
28179
|
withCredentials: false,
|
|
@@ -28057,10 +28409,9 @@ const OnlinePresence = {
|
|
|
28057
28409
|
const UserId = {
|
|
28058
28410
|
// User identifier at top right, opens user preferences
|
|
28059
28411
|
view: (vnode) => {
|
|
28060
|
-
var _a;
|
|
28061
28412
|
const view = vnode.attrs.view;
|
|
28062
28413
|
const model = view.model;
|
|
28063
|
-
if (!
|
|
28414
|
+
if (!model.state?.netskraflUserId)
|
|
28064
28415
|
// Don't show the button if there is no logged-in user
|
|
28065
28416
|
return "";
|
|
28066
28417
|
return m(".userid", {
|
|
@@ -28073,13 +28424,25 @@ const UserId = {
|
|
|
28073
28424
|
}, [glyph("address-book"), nbsp(), model.state.userNick]);
|
|
28074
28425
|
}
|
|
28075
28426
|
};
|
|
28427
|
+
const ProModeIndicator = {
|
|
28428
|
+
// Display a Pro Mode (Keppnishamur) indicator using a lightbulb glyph
|
|
28429
|
+
// If header is true, always show grayed with title
|
|
28430
|
+
// Otherwise, the lightbulb is grayed if manual mode is not enabled
|
|
28431
|
+
view: (vnode) => {
|
|
28432
|
+
const { manual, header } = vnode.attrs;
|
|
28433
|
+
const title = ts("Keppnishamur");
|
|
28434
|
+
// Regular case: show grayed if not manual mode
|
|
28435
|
+
const highlight = (manual === true) && !header;
|
|
28436
|
+
const glyphAttrs = (highlight || header) ? { title } : {};
|
|
28437
|
+
return m("span.list-manual", glyphAttrs, glyph("lightbulb", undefined, !highlight));
|
|
28438
|
+
}
|
|
28439
|
+
};
|
|
28076
28440
|
const MultiSelection = (initialVnode) => {
|
|
28077
28441
|
// A multiple-selection div where users can click on child nodes
|
|
28078
28442
|
// to select them, giving them an addional selection class,
|
|
28079
28443
|
// typically .selected
|
|
28080
|
-
|
|
28081
|
-
|
|
28082
|
-
const defaultClass = (_b = initialVnode.attrs.defaultClass) !== null && _b !== void 0 ? _b : "";
|
|
28444
|
+
let sel = initialVnode.attrs.initialSelection ?? 0;
|
|
28445
|
+
const defaultClass = initialVnode.attrs.defaultClass ?? "";
|
|
28083
28446
|
const selectedClass = initialVnode.attrs.selectedClass || "selected";
|
|
28084
28447
|
return {
|
|
28085
28448
|
view: (vnode) => {
|
|
@@ -28126,10 +28489,9 @@ const HeaderLogo = {
|
|
|
28126
28489
|
};
|
|
28127
28490
|
const Toggler = () => {
|
|
28128
28491
|
function doToggle(togglerId, funcToggle) {
|
|
28129
|
-
var _a, _b;
|
|
28130
28492
|
// Perform the toggling, on a mouse click or keyboard input (space bar)
|
|
28131
|
-
const cls1 =
|
|
28132
|
-
const cls2 =
|
|
28493
|
+
const cls1 = document.querySelector("#" + togglerId + " #opt1")?.classList;
|
|
28494
|
+
const cls2 = document.querySelector("#" + togglerId + " #opt2")?.classList;
|
|
28133
28495
|
cls1 && cls1.toggle("selected");
|
|
28134
28496
|
cls2 && cls2.toggle("selected");
|
|
28135
28497
|
if (funcToggle !== undefined && cls2)
|
|
@@ -28183,19 +28545,16 @@ const TogglerReady = (initialVnode) => {
|
|
|
28183
28545
|
}
|
|
28184
28546
|
}
|
|
28185
28547
|
return {
|
|
28186
|
-
view: () => {
|
|
28187
|
-
|
|
28188
|
-
|
|
28189
|
-
|
|
28190
|
-
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
title: ts("Tek við áskorunum!"),
|
|
28197
|
-
});
|
|
28198
|
-
}
|
|
28548
|
+
view: () => m(Toggler, {
|
|
28549
|
+
id: "ready",
|
|
28550
|
+
state: model.state?.ready ?? false,
|
|
28551
|
+
tabindex: 2,
|
|
28552
|
+
opt1: nbsp(),
|
|
28553
|
+
opt2: glyph("thumbs-up"),
|
|
28554
|
+
funcToggle: toggleFunc,
|
|
28555
|
+
small: true,
|
|
28556
|
+
title: ts("Tek við áskorunum!"),
|
|
28557
|
+
})
|
|
28199
28558
|
};
|
|
28200
28559
|
};
|
|
28201
28560
|
const TogglerReadyTimed = (initialVnode) => {
|
|
@@ -28210,19 +28569,16 @@ const TogglerReadyTimed = (initialVnode) => {
|
|
|
28210
28569
|
}
|
|
28211
28570
|
}
|
|
28212
28571
|
return {
|
|
28213
|
-
view: () => {
|
|
28214
|
-
|
|
28215
|
-
|
|
28216
|
-
|
|
28217
|
-
|
|
28218
|
-
|
|
28219
|
-
|
|
28220
|
-
|
|
28221
|
-
|
|
28222
|
-
|
|
28223
|
-
title: ts("Til í viðureign með klukku!"),
|
|
28224
|
-
});
|
|
28225
|
-
}
|
|
28572
|
+
view: () => m(Toggler, {
|
|
28573
|
+
id: "timed",
|
|
28574
|
+
state: model.state?.readyTimed ?? false,
|
|
28575
|
+
tabindex: 3,
|
|
28576
|
+
opt1: nbsp(),
|
|
28577
|
+
opt2: glyph("time"),
|
|
28578
|
+
funcToggle: toggleFunc,
|
|
28579
|
+
small: true,
|
|
28580
|
+
title: ts("Til í viðureign með klukku!"),
|
|
28581
|
+
})
|
|
28226
28582
|
};
|
|
28227
28583
|
};
|
|
28228
28584
|
const TogglerAudio = (initialVnode) => {
|
|
@@ -28317,7 +28673,6 @@ const WaitDialog = {
|
|
|
28317
28673
|
state.firebasePath = "";
|
|
28318
28674
|
},
|
|
28319
28675
|
oncreate: (vnode) => {
|
|
28320
|
-
var _a, _b;
|
|
28321
28676
|
// Note: we must use a two-step initialization here,
|
|
28322
28677
|
// calling oninit() first and then oncreate(), since calls to
|
|
28323
28678
|
// m.redraw() - explicit or implicit via m.request() - from within
|
|
@@ -28327,7 +28682,7 @@ const WaitDialog = {
|
|
|
28327
28682
|
const { view, oppId, challengeKey: key } = attrs;
|
|
28328
28683
|
const { model } = view;
|
|
28329
28684
|
const globalState = model.state;
|
|
28330
|
-
const userId =
|
|
28685
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
28331
28686
|
if (!userId || !oppId)
|
|
28332
28687
|
return; // Should not happen
|
|
28333
28688
|
state.firebasePath = `user/${userId}/wait/${oppId}`;
|
|
@@ -28366,12 +28721,11 @@ const WaitDialog = {
|
|
|
28366
28721
|
});
|
|
28367
28722
|
},
|
|
28368
28723
|
view: (vnode) => {
|
|
28369
|
-
var _a, _b;
|
|
28370
28724
|
const { attrs, state } = vnode;
|
|
28371
28725
|
const { view, oppId, duration, challengeKey: key, oppNick, oppName } = attrs;
|
|
28372
28726
|
const { model } = view;
|
|
28373
28727
|
const globalState = model.state;
|
|
28374
|
-
const userId =
|
|
28728
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
28375
28729
|
if (!globalState)
|
|
28376
28730
|
return null;
|
|
28377
28731
|
const cancelWait = async () => {
|
|
@@ -28453,7 +28807,7 @@ const AcceptDialog = {
|
|
|
28453
28807
|
url: "/waitcheck",
|
|
28454
28808
|
body: { user: oppId, key }
|
|
28455
28809
|
});
|
|
28456
|
-
if (json
|
|
28810
|
+
if (json?.waiting) {
|
|
28457
28811
|
// Both players are now ready: Start the timed game.
|
|
28458
28812
|
// The newGame() call switches to a new route (/game),
|
|
28459
28813
|
// and all open dialogs are thereby closed automatically.
|
|
@@ -28516,12 +28870,11 @@ class WordChecker {
|
|
|
28516
28870
|
this.wordCheckCache = {};
|
|
28517
28871
|
}
|
|
28518
28872
|
ingestTwoLetterWords(locale, twoLetterWords) {
|
|
28519
|
-
var _a;
|
|
28520
28873
|
// Initialize the cache with a list of known two-letter words
|
|
28521
28874
|
// The two-letter word list contains a list of entries where each
|
|
28522
28875
|
// entry is an initial letter followed by a list of two-letter words
|
|
28523
28876
|
// starting with that letter.
|
|
28524
|
-
const cache =
|
|
28877
|
+
const cache = this.wordCheckCache[locale] ?? (this.wordCheckCache[locale] = {});
|
|
28525
28878
|
for (const [/* firstLetter */ , wordList] of twoLetterWords) {
|
|
28526
28879
|
for (const word of wordList) {
|
|
28527
28880
|
cache[word] = true;
|
|
@@ -28564,7 +28917,7 @@ class WordChecker {
|
|
|
28564
28917
|
words,
|
|
28565
28918
|
}
|
|
28566
28919
|
});
|
|
28567
|
-
if (
|
|
28920
|
+
if (response?.word === words[0] && response?.valid) {
|
|
28568
28921
|
// Looks like a valid response
|
|
28569
28922
|
if (!cache) {
|
|
28570
28923
|
// We didn't already have a cache for this locale
|
|
@@ -29207,7 +29560,6 @@ class BaseGame {
|
|
|
29207
29560
|
return c ? this.tiles[c] || null : null;
|
|
29208
29561
|
}
|
|
29209
29562
|
saveTiles() {
|
|
29210
|
-
var _a;
|
|
29211
29563
|
// Save the current unglued tile configuration to local storage
|
|
29212
29564
|
let tp = [];
|
|
29213
29565
|
const tilesPlaced = this.tilesPlaced();
|
|
@@ -29222,7 +29574,7 @@ class BaseGame {
|
|
|
29222
29574
|
if (sq in this.tiles)
|
|
29223
29575
|
tp.push({ sq, tile: this.tiles[sq].tile });
|
|
29224
29576
|
}
|
|
29225
|
-
|
|
29577
|
+
this.localStorage?.saveTiles(tp);
|
|
29226
29578
|
}
|
|
29227
29579
|
;
|
|
29228
29580
|
restoreTiles(savedTiles) {
|
|
@@ -29326,7 +29678,6 @@ const DEBUG_OVERTIME = 1 * 60.0;
|
|
|
29326
29678
|
const GAME_OVER = 99; // Error code corresponding to the Error class in skraflmechanics.py
|
|
29327
29679
|
class Game extends BaseGame {
|
|
29328
29680
|
constructor(uuid, srvGame, moveListener, state, maxOvertime) {
|
|
29329
|
-
var _a;
|
|
29330
29681
|
// Call parent constructor
|
|
29331
29682
|
super(uuid, state); // Default board_type: "standard"
|
|
29332
29683
|
// A class that represents a Netskrafl game instance on the client
|
|
@@ -29389,7 +29740,7 @@ class Game extends BaseGame {
|
|
|
29389
29740
|
return;
|
|
29390
29741
|
}
|
|
29391
29742
|
// Load previously saved tile positions from local storage, if any
|
|
29392
|
-
let savedTiles =
|
|
29743
|
+
let savedTiles = this.localStorage?.loadTiles() || [];
|
|
29393
29744
|
this.init(srvGame);
|
|
29394
29745
|
// Put tiles in the same position as they were when the player left the game
|
|
29395
29746
|
this.restoreTiles(savedTiles);
|
|
@@ -29595,7 +29946,7 @@ class Game extends BaseGame {
|
|
|
29595
29946
|
url: "/gamestate",
|
|
29596
29947
|
body: { game: this.uuid } // !!! FIXME: Add delete_zombie parameter
|
|
29597
29948
|
});
|
|
29598
|
-
if (!
|
|
29949
|
+
if (!result?.ok) {
|
|
29599
29950
|
// console.log("Game " + uuid + " could not be loaded");
|
|
29600
29951
|
}
|
|
29601
29952
|
else {
|
|
@@ -29939,8 +30290,7 @@ class Game extends BaseGame {
|
|
|
29939
30290
|
this.rack = rack;
|
|
29940
30291
|
}
|
|
29941
30292
|
rackAtMove(moveIndex) {
|
|
29942
|
-
|
|
29943
|
-
const numRacks = ((_a = this.racks) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
30293
|
+
const numRacks = this.racks?.length || 0;
|
|
29944
30294
|
if (!numRacks)
|
|
29945
30295
|
return "";
|
|
29946
30296
|
return this.racks[moveIndex >= numRacks ? numRacks - 1 : moveIndex];
|
|
@@ -30423,8 +30773,8 @@ class Riddle extends BaseGame {
|
|
|
30423
30773
|
// Initialize word checker
|
|
30424
30774
|
wordChecker.ingestTwoLetterWords(this.locale, this.two_letter_words[0]);
|
|
30425
30775
|
// Load persisted player moves from localStorage
|
|
30426
|
-
if (state.
|
|
30427
|
-
const persistedMoves = RiddlePersistence.loadLocalMoves(state.
|
|
30776
|
+
if (state.netskraflUserId) {
|
|
30777
|
+
const persistedMoves = RiddlePersistence.loadLocalMoves(state.netskraflUserId, date);
|
|
30428
30778
|
if (persistedMoves.length > 0) {
|
|
30429
30779
|
// Convert from IPlayerMove to RiddleWord format, preserving timestamps
|
|
30430
30780
|
this.playerMoves = persistedMoves.map(move => ({
|
|
@@ -30447,7 +30797,7 @@ class Riddle extends BaseGame {
|
|
|
30447
30797
|
}
|
|
30448
30798
|
async submitRiddleWord(move) {
|
|
30449
30799
|
const { state } = this;
|
|
30450
|
-
if (!state || !state.
|
|
30800
|
+
if (!state || !state.netskraflUserId)
|
|
30451
30801
|
return;
|
|
30452
30802
|
try {
|
|
30453
30803
|
await request(state, {
|
|
@@ -30456,9 +30806,9 @@ class Riddle extends BaseGame {
|
|
|
30456
30806
|
body: {
|
|
30457
30807
|
date: this.date,
|
|
30458
30808
|
locale: this.locale,
|
|
30459
|
-
userId: state.
|
|
30809
|
+
userId: state.netskraflUserId,
|
|
30460
30810
|
groupId: state.userGroupId || null,
|
|
30461
|
-
userDisplayName: state.userFullname || state.userNick ||
|
|
30811
|
+
userDisplayName: state.userFullname || state.userNick || "",
|
|
30462
30812
|
move,
|
|
30463
30813
|
}
|
|
30464
30814
|
});
|
|
@@ -30508,7 +30858,7 @@ class Riddle extends BaseGame {
|
|
|
30508
30858
|
if (!move)
|
|
30509
30859
|
return;
|
|
30510
30860
|
const { state } = this;
|
|
30511
|
-
if (!state || !state.
|
|
30861
|
+
if (!state || !state.netskraflUserId)
|
|
30512
30862
|
return;
|
|
30513
30863
|
// Save all moves to localStorage (local backup/cache)
|
|
30514
30864
|
// Convert RiddleWord[] to IPlayerMove[] for persistence
|
|
@@ -30518,7 +30868,7 @@ class Riddle extends BaseGame {
|
|
|
30518
30868
|
coord: m.coord,
|
|
30519
30869
|
timestamp: m.timestamp
|
|
30520
30870
|
}));
|
|
30521
|
-
RiddlePersistence.saveLocalMoves(state.
|
|
30871
|
+
RiddlePersistence.saveLocalMoves(state.netskraflUserId, this.date, movesToSave);
|
|
30522
30872
|
// If the move does not improve the personal best, we're done
|
|
30523
30873
|
if (move.score <= this.personalBestScore)
|
|
30524
30874
|
return;
|
|
@@ -30848,7 +31198,6 @@ class Model {
|
|
|
30848
31198
|
});
|
|
30849
31199
|
}
|
|
30850
31200
|
async loadGame(uuid, funcComplete, deleteZombie = false) {
|
|
30851
|
-
var _a;
|
|
30852
31201
|
// Fetch a Netskrafl game state from the server, given the game's UUID.
|
|
30853
31202
|
// If deleteZombie is true, we are loading a zombie game for
|
|
30854
31203
|
// inspection, so we tell the server to remove the zombie marker.
|
|
@@ -30868,12 +31217,12 @@ class Model {
|
|
|
30868
31217
|
game: uuid,
|
|
30869
31218
|
delete_zombie: deleteZombie
|
|
30870
31219
|
});
|
|
30871
|
-
if (!
|
|
31220
|
+
if (!result?.ok) {
|
|
30872
31221
|
// console.log("Game " + uuid + " could not be loaded");
|
|
30873
31222
|
}
|
|
30874
31223
|
else {
|
|
30875
31224
|
// Create a new game instance and load the state into it
|
|
30876
|
-
this.game = new Game(uuid, result.game, this, this.state,
|
|
31225
|
+
this.game = new Game(uuid, result.game, this, this.state, this.state?.runningLocal ? DEBUG_OVERTIME : MAX_OVERTIME);
|
|
30877
31226
|
// Successfully loaded: call the completion function, if given
|
|
30878
31227
|
// (this usually attaches the Firebase event listener)
|
|
30879
31228
|
funcComplete && funcComplete();
|
|
@@ -31180,13 +31529,12 @@ class Model {
|
|
|
31180
31529
|
}
|
|
31181
31530
|
}
|
|
31182
31531
|
async loadHelp() {
|
|
31183
|
-
var _a;
|
|
31184
31532
|
// Load the help screen HTML from the server
|
|
31185
31533
|
// (this is done the first time the help is displayed)
|
|
31186
31534
|
if (this.helpHTML !== null)
|
|
31187
31535
|
return; // Already loaded
|
|
31188
31536
|
try {
|
|
31189
|
-
const locale =
|
|
31537
|
+
const locale = this.state?.locale || "is_IS";
|
|
31190
31538
|
const result = await this.getText("/rawhelp?version=malstadur&locale=" + locale);
|
|
31191
31539
|
this.helpHTML = result;
|
|
31192
31540
|
}
|
|
@@ -31231,7 +31579,7 @@ class Model {
|
|
|
31231
31579
|
return;
|
|
31232
31580
|
try {
|
|
31233
31581
|
const result = await this.post("/saveuserprefs", user);
|
|
31234
|
-
if (result
|
|
31582
|
+
if (result?.ok) {
|
|
31235
31583
|
// User preferences modified successfully on the server:
|
|
31236
31584
|
// update the state variables that we're caching
|
|
31237
31585
|
const state = this.state;
|
|
@@ -31263,10 +31611,9 @@ class Model {
|
|
|
31263
31611
|
}
|
|
31264
31612
|
}
|
|
31265
31613
|
addChatMessage(game, from_userid, msg, ts) {
|
|
31266
|
-
var _a, _b;
|
|
31267
31614
|
// Add a chat message to the game's chat message list
|
|
31268
31615
|
if (this.game && this.game.uuid == game) {
|
|
31269
|
-
const userId =
|
|
31616
|
+
const userId = this.state?.netskraflUserId ?? "";
|
|
31270
31617
|
this.game.addChatMessage(from_userid, msg, ts, from_userid == userId);
|
|
31271
31618
|
// Returning true triggers a redraw
|
|
31272
31619
|
return true;
|
|
@@ -31326,10 +31673,9 @@ class Model {
|
|
|
31326
31673
|
}
|
|
31327
31674
|
}
|
|
31328
31675
|
notifyGameWon() {
|
|
31329
|
-
var _a;
|
|
31330
31676
|
// The user just won a game:
|
|
31331
31677
|
// play the "you-win" audio if fanfare is enabled
|
|
31332
|
-
if (
|
|
31678
|
+
if (this.user?.fanfare) {
|
|
31333
31679
|
playAudio(this.state, "you-win");
|
|
31334
31680
|
}
|
|
31335
31681
|
}
|
|
@@ -31420,7 +31766,7 @@ const FriendPromoteDialog = (initialVnode) => {
|
|
|
31420
31766
|
onclick: (ev) => {
|
|
31421
31767
|
ev.preventDefault();
|
|
31422
31768
|
logEvent("click_friend", {
|
|
31423
|
-
userid: state.
|
|
31769
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
31424
31770
|
});
|
|
31425
31771
|
// Navigate to the container-supplied subscription URL
|
|
31426
31772
|
window.location.href = state.subscriptionUrl;
|
|
@@ -31488,7 +31834,7 @@ const ChallengeDialog = () => {
|
|
|
31488
31834
|
m("div", { id: 'chall-15', tabindex: 3 }, [glyph("time"), t("2 x 15 mínútur")]),
|
|
31489
31835
|
m("div", { id: 'chall-20', tabindex: 4 }, [glyph("time"), t("2 x 20 mínútur")]),
|
|
31490
31836
|
m("div", { id: 'chall-25', tabindex: 5 }, [glyph("time"), t("2 x 25 mínútur")]),
|
|
31491
|
-
|
|
31837
|
+
state?.runningLocal ? // !!! TODO Debugging aid
|
|
31492
31838
|
m("div", { id: 'chall-3', tabindex: 6 }, [glyph("time"), t("2 x 3 mínútur")])
|
|
31493
31839
|
:
|
|
31494
31840
|
m("div", { id: 'chall-30', tabindex: 6 }, [glyph("time"), t("2 x 30 mínútur")])
|
|
@@ -31534,9 +31880,8 @@ const ChallengeDialog = () => {
|
|
|
31534
31880
|
title: ts("Skora á"),
|
|
31535
31881
|
tabindex: 9,
|
|
31536
31882
|
onclick: (ev) => {
|
|
31537
|
-
var _a, _b;
|
|
31538
31883
|
// Issue a new challenge
|
|
31539
|
-
let duration =
|
|
31884
|
+
let duration = document.querySelector("div.chall-time.selected")?.id.slice(6) ?? "0";
|
|
31540
31885
|
if (duration === "none")
|
|
31541
31886
|
duration = 0;
|
|
31542
31887
|
else
|
|
@@ -31579,7 +31924,7 @@ const RecentList = () => {
|
|
|
31579
31924
|
function durationDescription() {
|
|
31580
31925
|
// Format the game duration
|
|
31581
31926
|
let duration = "";
|
|
31582
|
-
if (!item.duration) {
|
|
31927
|
+
if (!item.prefs?.duration) {
|
|
31583
31928
|
// Regular (non-timed) game
|
|
31584
31929
|
if (item.days || item.hours || item.minutes) {
|
|
31585
31930
|
if (item.days > 1)
|
|
@@ -31608,7 +31953,7 @@ const RecentList = () => {
|
|
|
31608
31953
|
// This was a timed game
|
|
31609
31954
|
duration = [
|
|
31610
31955
|
m("span.timed-btn", { title: ts('Viðureign með klukku') }),
|
|
31611
|
-
" 2 x " + item.duration + ts(" mínútur")
|
|
31956
|
+
" 2 x " + item.prefs.duration + ts(" mínútur")
|
|
31612
31957
|
];
|
|
31613
31958
|
}
|
|
31614
31959
|
return duration;
|
|
@@ -31658,7 +32003,7 @@ const RecentList = () => {
|
|
|
31658
32003
|
m("div.list-elo-adj", eloAdjHuman),
|
|
31659
32004
|
m("div.list-elo-adj", eloAdj),
|
|
31660
32005
|
m("span.list-duration", durationDescription()),
|
|
31661
|
-
m(
|
|
32006
|
+
m(ProModeIndicator, { manual: item.prefs?.manual })
|
|
31662
32007
|
]));
|
|
31663
32008
|
}
|
|
31664
32009
|
return {
|
|
@@ -31669,6 +32014,295 @@ const RecentList = () => {
|
|
|
31669
32014
|
};
|
|
31670
32015
|
};
|
|
31671
32016
|
|
|
32017
|
+
/*
|
|
32018
|
+
|
|
32019
|
+
Gamelist.ts
|
|
32020
|
+
|
|
32021
|
+
List of games in progress display component
|
|
32022
|
+
|
|
32023
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32024
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32025
|
+
|
|
32026
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32027
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32028
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32029
|
+
|
|
32030
|
+
*/
|
|
32031
|
+
const Hint = {
|
|
32032
|
+
view: (vnode) => {
|
|
32033
|
+
const { loadingGameList, loadingAllLists, gameList } = vnode.attrs;
|
|
32034
|
+
// Show some help if the user has no games in progress
|
|
32035
|
+
if (loadingGameList || loadingAllLists || (gameList !== null && gameList.length > 0))
|
|
32036
|
+
// Either we have games in progress or the game list is being loaded
|
|
32037
|
+
return "";
|
|
32038
|
+
return m(".hint", { style: { display: "block" } }, [
|
|
32039
|
+
m("p", [
|
|
32040
|
+
"Ef þig vantar einhvern til að skrafla við, veldu flipann ",
|
|
32041
|
+
m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
|
|
32042
|
+
" og skoraðu á tölvuþjarka - ",
|
|
32043
|
+
glyph("cog"), nbsp(), m("b", "Amlóða"),
|
|
32044
|
+
", ",
|
|
32045
|
+
glyph("cog"), nbsp(), m("b", "Miðlung"),
|
|
32046
|
+
" eða ",
|
|
32047
|
+
glyph("cog"), nbsp(), m("b", "Fullsterkan"),
|
|
32048
|
+
" - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
|
|
32049
|
+
" sem þar er að finna til að skora á."
|
|
32050
|
+
]),
|
|
32051
|
+
m("p", [
|
|
32052
|
+
"Þú stofnar áskorun með því að smella á bendi-teiknið ",
|
|
32053
|
+
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
32054
|
+
" vinstra megin við nafn andstæðingsins."
|
|
32055
|
+
]),
|
|
32056
|
+
m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
|
|
32057
|
+
" hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
|
|
32058
|
+
m("p.no-mobile-block", [
|
|
32059
|
+
m(m.route.Link, { href: "/help" }, "Hjálp"),
|
|
32060
|
+
" má fá með því að smella á bláa ",
|
|
32061
|
+
glyph("info-sign"), nbsp(), "-", nbsp(),
|
|
32062
|
+
"teiknið hér til vinstri."
|
|
32063
|
+
]),
|
|
32064
|
+
m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
|
|
32065
|
+
"örvarmerkið efst vinstra megin við skraflborðið.")
|
|
32066
|
+
]);
|
|
32067
|
+
}
|
|
32068
|
+
};
|
|
32069
|
+
const GameListEntry = () => {
|
|
32070
|
+
// Generate a list item about a game in progress (or recently finished)
|
|
32071
|
+
function vwOpp(item) {
|
|
32072
|
+
const arg = item.oppid === null ? [glyph("cog"), nbsp(), item.opp] : item.opp;
|
|
32073
|
+
return m("span.list-opp", { title: item.fullname }, arg);
|
|
32074
|
+
}
|
|
32075
|
+
function vwTurn(item) {
|
|
32076
|
+
let turnText = "";
|
|
32077
|
+
let flagClass = "";
|
|
32078
|
+
if (item.my_turn) {
|
|
32079
|
+
turnText = ts("Þú átt leik");
|
|
32080
|
+
}
|
|
32081
|
+
else if (item.zombie) {
|
|
32082
|
+
turnText = ts("Viðureign lokið");
|
|
32083
|
+
flagClass = ".zombie";
|
|
32084
|
+
}
|
|
32085
|
+
else {
|
|
32086
|
+
// {opponent}'s move
|
|
32087
|
+
turnText = ts("opp_move", { opponent: item.opp });
|
|
32088
|
+
flagClass = ".grayed";
|
|
32089
|
+
}
|
|
32090
|
+
return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
|
|
32091
|
+
}
|
|
32092
|
+
function vwOverdue(item) {
|
|
32093
|
+
if (item.overdue)
|
|
32094
|
+
return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
|
|
32095
|
+
return glyphGrayed("hourglass");
|
|
32096
|
+
}
|
|
32097
|
+
function vwTileCount(item) {
|
|
32098
|
+
const winLose = item.sc0 < item.sc1 ? ".losing" : "";
|
|
32099
|
+
return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
|
|
32100
|
+
}
|
|
32101
|
+
return {
|
|
32102
|
+
view: (vnode) => {
|
|
32103
|
+
const { view, item, i } = vnode.attrs;
|
|
32104
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32105
|
+
m(m.route.Link, { href: gameUrl(item.url) }, [
|
|
32106
|
+
vwTurn(item),
|
|
32107
|
+
m("span.list-overdue", vwOverdue(item)),
|
|
32108
|
+
m("span.list-ts-short", item.ts),
|
|
32109
|
+
vwOpp(item)
|
|
32110
|
+
]),
|
|
32111
|
+
m(UserInfoButton, {
|
|
32112
|
+
view,
|
|
32113
|
+
item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
|
|
32114
|
+
}),
|
|
32115
|
+
m("span.list-s0", item.sc0),
|
|
32116
|
+
m("span.list-colon", ":"),
|
|
32117
|
+
m("span.list-s1", item.sc1),
|
|
32118
|
+
m("span.list-tc", vwTileCount(item)),
|
|
32119
|
+
m(ProModeIndicator, { manual: item.prefs?.manual })
|
|
32120
|
+
]);
|
|
32121
|
+
}
|
|
32122
|
+
};
|
|
32123
|
+
};
|
|
32124
|
+
const GameList = () => {
|
|
32125
|
+
// Shows a list of games in progress, obtained from view.model.gameList
|
|
32126
|
+
return {
|
|
32127
|
+
view: (vnode) => {
|
|
32128
|
+
const { view, id } = vnode.attrs;
|
|
32129
|
+
const { model } = view;
|
|
32130
|
+
const { gameList, loadingGameList, loadingAllLists } = model;
|
|
32131
|
+
// Trigger loading of game list if needed
|
|
32132
|
+
if (gameList === null)
|
|
32133
|
+
model.loadGameList();
|
|
32134
|
+
return [
|
|
32135
|
+
m(".listitem.listheader", [
|
|
32136
|
+
m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
|
|
32137
|
+
m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
|
|
32138
|
+
mt("span.list-ts-short", "Síðasti leikur"),
|
|
32139
|
+
mt("span.list-opp", "Andstæðingur"),
|
|
32140
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32141
|
+
mt("span.list-scorehdr", "Staða"),
|
|
32142
|
+
mt("span.list-tc", "Framvinda"),
|
|
32143
|
+
m(ProModeIndicator, { header: true }),
|
|
32144
|
+
]),
|
|
32145
|
+
m("div", { id }, !gameList ? "" : gameList.map((item, i) => m(GameListEntry, { view, item, i }))),
|
|
32146
|
+
m(Hint, { loadingGameList, loadingAllLists, gameList }),
|
|
32147
|
+
];
|
|
32148
|
+
}
|
|
32149
|
+
};
|
|
32150
|
+
};
|
|
32151
|
+
|
|
32152
|
+
/*
|
|
32153
|
+
|
|
32154
|
+
Challengelist.ts
|
|
32155
|
+
|
|
32156
|
+
List of challenges (received and sent) display component
|
|
32157
|
+
|
|
32158
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32159
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32160
|
+
|
|
32161
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32162
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32163
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32164
|
+
|
|
32165
|
+
*/
|
|
32166
|
+
const ChallengeListEntry = (initialVnode) => {
|
|
32167
|
+
// Generate a list item about a pending challenge (issued or received)
|
|
32168
|
+
const { view, item, i } = initialVnode.attrs;
|
|
32169
|
+
const { model } = view;
|
|
32170
|
+
function challengeDescription(json) {
|
|
32171
|
+
/* Return a human-readable string describing a challenge
|
|
32172
|
+
according to the enclosed preferences */
|
|
32173
|
+
if (!json || json.duration === undefined || json.duration === 0)
|
|
32174
|
+
/* Normal unbounded (untimed) game */
|
|
32175
|
+
return t("Venjuleg ótímabundin viðureign");
|
|
32176
|
+
return t("with_clock", { duration: json.duration.toString() });
|
|
32177
|
+
}
|
|
32178
|
+
function markChallenge(ev) {
|
|
32179
|
+
// Clicked the icon at the beginning of the line,
|
|
32180
|
+
// to decline a received challenge or retract an issued challenge
|
|
32181
|
+
ev.preventDefault();
|
|
32182
|
+
if (item.received) {
|
|
32183
|
+
view.actions.declineChallenge(item.userid, item.key);
|
|
32184
|
+
}
|
|
32185
|
+
else {
|
|
32186
|
+
view.actions.retractChallenge(item.userid, item.key);
|
|
32187
|
+
}
|
|
32188
|
+
}
|
|
32189
|
+
function clickChallenge(ev) {
|
|
32190
|
+
// Clicked the hotspot area to accept a received challenge
|
|
32191
|
+
ev.preventDefault();
|
|
32192
|
+
if (!model.moreGamesAllowed()) {
|
|
32193
|
+
// User must be a friend to be able to accept more challenges
|
|
32194
|
+
const state = model.state;
|
|
32195
|
+
if (state) {
|
|
32196
|
+
logEvent("hit_game_limit", {
|
|
32197
|
+
userid: state.netskraflUserId,
|
|
32198
|
+
locale: state.locale,
|
|
32199
|
+
limit: model.maxFreeGames
|
|
32200
|
+
});
|
|
32201
|
+
}
|
|
32202
|
+
// Promote a subscription to Netskrafl/Explo
|
|
32203
|
+
view.showFriendPromo();
|
|
32204
|
+
return;
|
|
32205
|
+
}
|
|
32206
|
+
if (item.received) {
|
|
32207
|
+
if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
|
|
32208
|
+
// Timed game: display a modal wait dialog
|
|
32209
|
+
view.pushDialog("wait", {
|
|
32210
|
+
oppId: item.userid,
|
|
32211
|
+
oppNick: item.opp,
|
|
32212
|
+
oppName: item.fullname,
|
|
32213
|
+
duration: item.prefs.duration,
|
|
32214
|
+
challengeKey: item.key,
|
|
32215
|
+
});
|
|
32216
|
+
else
|
|
32217
|
+
// Ask the server to create a new game and route to it
|
|
32218
|
+
view.actions.startNewGame(item.userid, false);
|
|
32219
|
+
}
|
|
32220
|
+
else {
|
|
32221
|
+
// Clicking on a sent challenge, i.e. a timed game
|
|
32222
|
+
// where the opponent is waiting and ready to start
|
|
32223
|
+
view.showAcceptDialog(item.userid, item.opp, item.key);
|
|
32224
|
+
}
|
|
32225
|
+
}
|
|
32226
|
+
return {
|
|
32227
|
+
view: () => {
|
|
32228
|
+
const oppReady = !item.received && item.opp_ready &&
|
|
32229
|
+
item.prefs && item.prefs.duration !== undefined &&
|
|
32230
|
+
item.prefs.duration > 0;
|
|
32231
|
+
const clickable = item.received || oppReady;
|
|
32232
|
+
const descr = challengeDescription(item.prefs);
|
|
32233
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32234
|
+
m("span.list-ch", { onclick: markChallenge }, item.received ?
|
|
32235
|
+
glyph("thumbs-down", { title: ts("Hafna") })
|
|
32236
|
+
:
|
|
32237
|
+
glyph("hand-right", { title: ts("Afturkalla") })),
|
|
32238
|
+
m(clickable ? "a.flex" : "span.flex", clickable ? {
|
|
32239
|
+
href: "#",
|
|
32240
|
+
onclick: clickChallenge,
|
|
32241
|
+
class: oppReady ? "opp-ready" : ""
|
|
32242
|
+
} : {}, [
|
|
32243
|
+
m("span.list-ts", item.ts),
|
|
32244
|
+
m("span.list-nick", { title: item.fullname }, item.opp),
|
|
32245
|
+
m("span.list-chall", [
|
|
32246
|
+
item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
|
|
32247
|
+
item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
|
|
32248
|
+
descr
|
|
32249
|
+
])
|
|
32250
|
+
]),
|
|
32251
|
+
m(UserInfoButton, {
|
|
32252
|
+
view,
|
|
32253
|
+
item: {
|
|
32254
|
+
userid: item.userid,
|
|
32255
|
+
nick: item.opp,
|
|
32256
|
+
fullname: item.fullname,
|
|
32257
|
+
}
|
|
32258
|
+
}),
|
|
32259
|
+
]);
|
|
32260
|
+
}
|
|
32261
|
+
};
|
|
32262
|
+
};
|
|
32263
|
+
const ChallengeList = {
|
|
32264
|
+
// Shows a list of challenges (received or sent), obtained from view.model.challengeList
|
|
32265
|
+
view: (vnode) => {
|
|
32266
|
+
const { view, showReceived } = vnode.attrs;
|
|
32267
|
+
const { model } = view;
|
|
32268
|
+
if (!model.state)
|
|
32269
|
+
return [];
|
|
32270
|
+
// Trigger loading of challenge list if needed
|
|
32271
|
+
if (model.challengeList === null)
|
|
32272
|
+
model.loadChallengeList();
|
|
32273
|
+
let cList = [];
|
|
32274
|
+
if (model.challengeList)
|
|
32275
|
+
cList = showReceived ?
|
|
32276
|
+
model.challengeList.filter((item) => item.received) :
|
|
32277
|
+
model.challengeList.filter((item) => !item.received);
|
|
32278
|
+
const listContent = m("div", { id: showReceived ? 'chall-received' : 'chall-sent' }, cList.map((item, i) => m(ChallengeListEntry, { view, item, i })));
|
|
32279
|
+
if (showReceived)
|
|
32280
|
+
// Challenges received
|
|
32281
|
+
return [
|
|
32282
|
+
m(".listitem.listheader", [
|
|
32283
|
+
m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
|
|
32284
|
+
mt("span.list-ts", "Hvenær"),
|
|
32285
|
+
mt("span.list-nick", "Áskorandi"),
|
|
32286
|
+
mt("span.list-chall", "Hvernig"),
|
|
32287
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32288
|
+
]),
|
|
32289
|
+
listContent
|
|
32290
|
+
];
|
|
32291
|
+
else
|
|
32292
|
+
// Challenges sent
|
|
32293
|
+
return [
|
|
32294
|
+
m(".listitem.listheader", [
|
|
32295
|
+
m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
|
|
32296
|
+
mt("span.list-ts", "Hvenær"),
|
|
32297
|
+
mt("span.list-nick", "Andstæðingur"),
|
|
32298
|
+
mt("span.list-chall", "Hvernig"),
|
|
32299
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32300
|
+
]),
|
|
32301
|
+
listContent
|
|
32302
|
+
];
|
|
32303
|
+
}
|
|
32304
|
+
};
|
|
32305
|
+
|
|
31672
32306
|
/*
|
|
31673
32307
|
|
|
31674
32308
|
EloPage.ts
|
|
@@ -31764,7 +32398,6 @@ const EloList = (initialVnode) => {
|
|
|
31764
32398
|
view: (vnode) => {
|
|
31765
32399
|
function itemize(item, i) {
|
|
31766
32400
|
// Generate a list item about a user in an Elo ranking table
|
|
31767
|
-
var _a;
|
|
31768
32401
|
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
31769
32402
|
function rankStr(rank, ref) {
|
|
31770
32403
|
// Return a rank string or dash if no rank or not meaningful
|
|
@@ -31775,7 +32408,7 @@ const EloList = (initialVnode) => {
|
|
|
31775
32408
|
}
|
|
31776
32409
|
let nick = item.nick;
|
|
31777
32410
|
let ch = m("span.list-ch", nbsp());
|
|
31778
|
-
const userId =
|
|
32411
|
+
const userId = state?.netskraflUserId ?? "";
|
|
31779
32412
|
if (item.userid != userId && !item.inactive) {
|
|
31780
32413
|
ch = m(ChallengeButton, { view: outerView, item });
|
|
31781
32414
|
}
|
|
@@ -31809,6 +32442,135 @@ const EloList = (initialVnode) => {
|
|
|
31809
32442
|
};
|
|
31810
32443
|
};
|
|
31811
32444
|
|
|
32445
|
+
/*
|
|
32446
|
+
|
|
32447
|
+
Userlist.ts
|
|
32448
|
+
|
|
32449
|
+
User list display component
|
|
32450
|
+
|
|
32451
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32452
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32453
|
+
|
|
32454
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32455
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32456
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32457
|
+
|
|
32458
|
+
*/
|
|
32459
|
+
const UserList = () => {
|
|
32460
|
+
// Shows a list of users, obtained from view.model.userList
|
|
32461
|
+
function itemize(view, item, i) {
|
|
32462
|
+
// Generate a list item about a user
|
|
32463
|
+
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
32464
|
+
let fullname = [];
|
|
32465
|
+
// Online and accepting challenges
|
|
32466
|
+
if (item.ready && !isRobot) {
|
|
32467
|
+
fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
|
|
32468
|
+
fullname.push(nbsp());
|
|
32469
|
+
}
|
|
32470
|
+
// Willing to accept challenges for timed games
|
|
32471
|
+
if (item.ready_timed) {
|
|
32472
|
+
fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
|
|
32473
|
+
fullname.push(nbsp());
|
|
32474
|
+
}
|
|
32475
|
+
// Fair play commitment
|
|
32476
|
+
if (item.fairplay) {
|
|
32477
|
+
fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
|
|
32478
|
+
fullname.push(nbsp());
|
|
32479
|
+
}
|
|
32480
|
+
fullname.push(item.fullname);
|
|
32481
|
+
function fav() {
|
|
32482
|
+
if (isRobot)
|
|
32483
|
+
return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
|
|
32484
|
+
return m("span.list-fav", {
|
|
32485
|
+
title: ts("Uppáhald"),
|
|
32486
|
+
onclick: (ev) => {
|
|
32487
|
+
view.actions.toggleFavorite(item.userid, item.fav);
|
|
32488
|
+
item.fav = !item.fav;
|
|
32489
|
+
ev.preventDefault();
|
|
32490
|
+
}
|
|
32491
|
+
}, glyph(item.fav ? "star" : "star-empty"));
|
|
32492
|
+
}
|
|
32493
|
+
function userLink() {
|
|
32494
|
+
if (isRobot)
|
|
32495
|
+
return m("a", {
|
|
32496
|
+
href: "",
|
|
32497
|
+
onclick: (ev) => {
|
|
32498
|
+
// Start a new game against the robot
|
|
32499
|
+
view.actions.startRobotGame(item.userid);
|
|
32500
|
+
ev.preventDefault();
|
|
32501
|
+
}
|
|
32502
|
+
}, [
|
|
32503
|
+
m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
|
|
32504
|
+
m("span.list-fullname-robot", fullname)
|
|
32505
|
+
]);
|
|
32506
|
+
else
|
|
32507
|
+
return [
|
|
32508
|
+
m("span.list-nick", item.nick),
|
|
32509
|
+
m("span.list-fullname", fullname),
|
|
32510
|
+
m("span.list-human-elo", item.human_elo)
|
|
32511
|
+
];
|
|
32512
|
+
}
|
|
32513
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32514
|
+
m(ChallengeButton, { view, item }),
|
|
32515
|
+
fav(),
|
|
32516
|
+
userLink(),
|
|
32517
|
+
m(UserInfoButton, { view, item }),
|
|
32518
|
+
]);
|
|
32519
|
+
}
|
|
32520
|
+
return {
|
|
32521
|
+
view: (vnode) => {
|
|
32522
|
+
const { view, id } = vnode.attrs;
|
|
32523
|
+
const { model } = view;
|
|
32524
|
+
// The type of list to show; by default it's 'robots'
|
|
32525
|
+
const query = model.userListCriteria?.query ?? "";
|
|
32526
|
+
const spec = model.userListCriteria?.spec ?? "";
|
|
32527
|
+
const loadingElo = model.eloRatingSpec === undefined;
|
|
32528
|
+
const listType = query || "robots";
|
|
32529
|
+
if (listType === "elo" || loadingElo)
|
|
32530
|
+
// Show Elo list
|
|
32531
|
+
return [
|
|
32532
|
+
m(EloPage, {
|
|
32533
|
+
id: "elolist",
|
|
32534
|
+
view: view,
|
|
32535
|
+
spec: model.eloRatingSpec ?? null,
|
|
32536
|
+
key: "elopage",
|
|
32537
|
+
})
|
|
32538
|
+
];
|
|
32539
|
+
// Show normal user list
|
|
32540
|
+
let list = [];
|
|
32541
|
+
if (model.userList === undefined) ;
|
|
32542
|
+
else if (model.userList === null || query !== listType) {
|
|
32543
|
+
view.actions.loadUsersByType(listType, true);
|
|
32544
|
+
}
|
|
32545
|
+
else {
|
|
32546
|
+
list = model.userList;
|
|
32547
|
+
}
|
|
32548
|
+
const nothingFound = list.length === 0 && listType === "search" && spec !== "";
|
|
32549
|
+
const robotList = listType === "robots";
|
|
32550
|
+
return [
|
|
32551
|
+
m(".listitem.listheader", [
|
|
32552
|
+
m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
|
|
32553
|
+
m("span.list-fav", glyph("star-empty", { title: ts('Uppáhald') })),
|
|
32554
|
+
mt("span.list-nick", "Einkenni"),
|
|
32555
|
+
mt("span.list-fullname", "Nafn og merki"),
|
|
32556
|
+
robotList ? "" : mt("span.list-human-elo[id='usr-list-elo']", "Elo"),
|
|
32557
|
+
robotList ? "" : mt("span.list-info-hdr[id='usr-list-info']", "Ferill"),
|
|
32558
|
+
]),
|
|
32559
|
+
m("div", { id }, list.map((item, i) => itemize(view, item, i))),
|
|
32560
|
+
// Show indicator if search didn't find any users matching the criteria
|
|
32561
|
+
nothingFound ?
|
|
32562
|
+
m("div", { id: "user-no-match", style: { display: "block" } }, [
|
|
32563
|
+
glyph("search"),
|
|
32564
|
+
" ",
|
|
32565
|
+
m("span", { id: "search-prefix" }, spec),
|
|
32566
|
+
t(" finnst ekki")
|
|
32567
|
+
])
|
|
32568
|
+
: undefined
|
|
32569
|
+
];
|
|
32570
|
+
}
|
|
32571
|
+
};
|
|
32572
|
+
};
|
|
32573
|
+
|
|
31812
32574
|
/*
|
|
31813
32575
|
|
|
31814
32576
|
Statsdisplay.ts
|
|
@@ -31835,7 +32597,6 @@ const StatsDisplay = () => {
|
|
|
31835
32597
|
}
|
|
31836
32598
|
return {
|
|
31837
32599
|
view: (vnode) => {
|
|
31838
|
-
var _a, _b;
|
|
31839
32600
|
// Display statistics about this user
|
|
31840
32601
|
var s = vnode.attrs.ownStats;
|
|
31841
32602
|
var winRatio = 0, winRatioHuman = 0;
|
|
@@ -31860,13 +32621,13 @@ const StatsDisplay = () => {
|
|
|
31860
32621
|
m(".option.small" + (sel === 2 ? ".selected" : ""), { id: 'opt2' }, glyph("cog"))
|
|
31861
32622
|
]),
|
|
31862
32623
|
sel === 1 ? m("div", { id: 'own-stats-human', className: 'stats-box', style: { display: "inline-block" } }, [
|
|
31863
|
-
m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat(
|
|
32624
|
+
m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat(s.locale_elo?.human_elo, "crown") : ""),
|
|
31864
32625
|
m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.human_games, "th") : ""),
|
|
31865
32626
|
m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatioHuman, "bookmark", "%")),
|
|
31866
32627
|
m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScoreHuman, "dashboard"))
|
|
31867
32628
|
]) : "",
|
|
31868
32629
|
sel === 2 ? m("div", { id: 'own-stats-all', className: 'stats-box', style: { display: "inline-block" } }, [
|
|
31869
|
-
m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat(
|
|
32630
|
+
m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat(s.locale_elo?.elo, "crown") : ""),
|
|
31870
32631
|
m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.games, "th") : ""),
|
|
31871
32632
|
m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatio, "bookmark", "%")),
|
|
31872
32633
|
m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScore, "dashboard"))
|
|
@@ -31898,7 +32659,6 @@ const SearchButton = (initialVnode) => {
|
|
|
31898
32659
|
let spec = ""; // The current search pattern
|
|
31899
32660
|
let promise = undefined;
|
|
31900
32661
|
function newSearch() {
|
|
31901
|
-
var _a, _b;
|
|
31902
32662
|
// There may have been a change of search parameters: react
|
|
31903
32663
|
if (promise !== undefined) {
|
|
31904
32664
|
// There was a previous promise, now obsolete: make it
|
|
@@ -31906,13 +32666,13 @@ const SearchButton = (initialVnode) => {
|
|
|
31906
32666
|
promise.result = false;
|
|
31907
32667
|
promise = undefined;
|
|
31908
32668
|
}
|
|
31909
|
-
let sel =
|
|
32669
|
+
let sel = model.userListCriteria?.query || "robots";
|
|
31910
32670
|
if (sel !== "search") {
|
|
31911
32671
|
// Not already in a search: load the user list immediately
|
|
31912
32672
|
view.actions.searchUsers(spec, true);
|
|
31913
32673
|
return;
|
|
31914
32674
|
}
|
|
31915
|
-
if (spec ===
|
|
32675
|
+
if (spec === model.userListCriteria?.spec)
|
|
31916
32676
|
// We're already looking at the same search spec: done
|
|
31917
32677
|
return;
|
|
31918
32678
|
// We're changing the search spec.
|
|
@@ -31944,18 +32704,16 @@ const SearchButton = (initialVnode) => {
|
|
|
31944
32704
|
}
|
|
31945
32705
|
return {
|
|
31946
32706
|
view: () => {
|
|
31947
|
-
|
|
31948
|
-
const sel = ((_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) || "robots";
|
|
32707
|
+
const sel = model.userListCriteria?.query || "robots";
|
|
31949
32708
|
return m(".user-cat[id='user-search']", [
|
|
31950
32709
|
glyph("search", {
|
|
31951
32710
|
id: 'search',
|
|
31952
32711
|
className: (sel == "search" ? "shown" : ""),
|
|
31953
32712
|
onclick: () => {
|
|
31954
|
-
var _a;
|
|
31955
32713
|
// Reset the search pattern when clicking the search icon
|
|
31956
32714
|
spec = "";
|
|
31957
32715
|
newSearch();
|
|
31958
|
-
|
|
32716
|
+
document.getElementById("search-id")?.focus();
|
|
31959
32717
|
}
|
|
31960
32718
|
}),
|
|
31961
32719
|
nbsp(),
|
|
@@ -32058,8 +32816,7 @@ function makeTabs(view, id, createFunc, wireHrefs, vnode) {
|
|
|
32058
32816
|
ev.preventDefault();
|
|
32059
32817
|
};
|
|
32060
32818
|
const clickUserPrefs = (ev) => {
|
|
32061
|
-
|
|
32062
|
-
if ((_a = model === null || model === void 0 ? void 0 : model.state) === null || _a === void 0 ? void 0 : _a.userId)
|
|
32819
|
+
if (model?.state?.netskraflUserId)
|
|
32063
32820
|
// Don't show the userprefs if no user logged in
|
|
32064
32821
|
view.pushDialog("userprefs");
|
|
32065
32822
|
ev.preventDefault();
|
|
@@ -32109,8 +32866,7 @@ function updateTabVisibility(vnode) {
|
|
|
32109
32866
|
const selected = vnode.state.selected;
|
|
32110
32867
|
const lis = vnode.state.lis;
|
|
32111
32868
|
vnode.state.ids.map((id, i) => {
|
|
32112
|
-
|
|
32113
|
-
(_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.setAttribute("style", "display: " +
|
|
32869
|
+
document.getElementById(id)?.setAttribute("style", "display: " +
|
|
32114
32870
|
(i == selected ? "block" : "none"));
|
|
32115
32871
|
lis[i].classList.toggle("ui-tabs-active", i === selected);
|
|
32116
32872
|
lis[i].classList.toggle("ui-state-active", i === selected);
|
|
@@ -32194,551 +32950,219 @@ const BestDisplay = () => {
|
|
|
32194
32950
|
For further information, see https://github.com/mideind/Netskrafl
|
|
32195
32951
|
|
|
32196
32952
|
*/
|
|
32197
|
-
|
|
32198
|
-
|
|
32199
|
-
|
|
32200
|
-
|
|
32201
|
-
|
|
32202
|
-
|
|
32203
|
-
|
|
32204
|
-
|
|
32205
|
-
|
|
32206
|
-
|
|
32207
|
-
|
|
32208
|
-
|
|
32209
|
-
|
|
32210
|
-
|
|
32211
|
-
|
|
32212
|
-
|
|
32213
|
-
|
|
32214
|
-
|
|
32215
|
-
|
|
32216
|
-
|
|
32217
|
-
|
|
32218
|
-
|
|
32219
|
-
|
|
32220
|
-
|
|
32221
|
-
|
|
32222
|
-
|
|
32223
|
-
|
|
32224
|
-
|
|
32225
|
-
|
|
32226
|
-
|
|
32227
|
-
|
|
32228
|
-
|
|
32229
|
-
|
|
32230
|
-
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
|
|
32234
|
-
|
|
32235
|
-
|
|
32236
|
-
|
|
32237
|
-
|
|
32238
|
-
|
|
32239
|
-
|
|
32240
|
-
|
|
32241
|
-
|
|
32242
|
-
|
|
32243
|
-
|
|
32244
|
-
|
|
32245
|
-
turnText = ts("Viðureign lokið");
|
|
32246
|
-
flagClass = ".zombie";
|
|
32247
|
-
}
|
|
32248
|
-
else {
|
|
32249
|
-
// {opponent}'s move
|
|
32250
|
-
turnText = ts("opp_move", { opponent: item.opp });
|
|
32251
|
-
flagClass = ".grayed";
|
|
32252
|
-
}
|
|
32253
|
-
return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
|
|
32254
|
-
}
|
|
32255
|
-
function vwOverdue() {
|
|
32256
|
-
if (item.overdue)
|
|
32257
|
-
return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
|
|
32258
|
-
return glyphGrayed("hourglass");
|
|
32259
|
-
}
|
|
32260
|
-
function vwTileCount() {
|
|
32261
|
-
const winLose = item.sc0 < item.sc1 ? ".losing" : "";
|
|
32262
|
-
return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
|
|
32263
|
-
}
|
|
32264
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32265
|
-
m(m.route.Link, { href: gameUrl(item.url) }, [
|
|
32266
|
-
vwTurn(),
|
|
32267
|
-
m("span.list-overdue", vwOverdue()),
|
|
32268
|
-
m("span.list-ts-short", item.ts),
|
|
32269
|
-
vwOpp()
|
|
32270
|
-
]),
|
|
32271
|
-
m(UserInfoButton, {
|
|
32272
|
-
view,
|
|
32273
|
-
item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
|
|
32274
|
-
}),
|
|
32275
|
-
m("span.list-s0", item.sc0),
|
|
32276
|
-
m("span.list-colon", ":"),
|
|
32277
|
-
m("span.list-s1", item.sc1),
|
|
32278
|
-
m("span.list-tc", vwTileCount()),
|
|
32279
|
-
m("span.list-manual", item.manual
|
|
32280
|
-
? glyph("lightbulb", { title: ts("Keppnishamur") })
|
|
32281
|
-
: glyphGrayed("lightbulb"))
|
|
32282
|
-
]);
|
|
32283
|
-
});
|
|
32284
|
-
}
|
|
32285
|
-
if (model.gameList === null)
|
|
32286
|
-
model.loadGameList();
|
|
32287
|
-
return m("div", { id: 'gamelist' }, viewGameList());
|
|
32288
|
-
}
|
|
32289
|
-
function vwHint() {
|
|
32290
|
-
// Show some help if the user has no games in progress
|
|
32291
|
-
if (model.loadingGameList
|
|
32292
|
-
|| model.loadingAllLists
|
|
32293
|
-
|| (model.gameList !== null && model.gameList.length > 0))
|
|
32294
|
-
// Either we have games in progress or the game list is being loaded
|
|
32295
|
-
return "";
|
|
32296
|
-
return m(".hint", { style: { display: "block" } }, [
|
|
32297
|
-
m("p", [
|
|
32298
|
-
"Ef þig vantar einhvern til að skrafla við, veldu flipann ",
|
|
32299
|
-
m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
|
|
32300
|
-
" og skoraðu á tölvuþjarka - ",
|
|
32301
|
-
glyph("cog"), nbsp(), m("b", "Amlóða"),
|
|
32302
|
-
", ",
|
|
32303
|
-
glyph("cog"), nbsp(), m("b", "Miðlung"),
|
|
32304
|
-
" eða ",
|
|
32305
|
-
glyph("cog"), nbsp(), m("b", "Fullsterkan"),
|
|
32306
|
-
" - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
|
|
32307
|
-
" sem þar er að finna til að skora á."
|
|
32308
|
-
]),
|
|
32309
|
-
m("p", [
|
|
32310
|
-
"Þú stofnar áskorun með því að smella á bendi-teiknið ",
|
|
32311
|
-
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
32312
|
-
" vinstra megin við nafn andstæðingsins."
|
|
32313
|
-
]),
|
|
32314
|
-
m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
|
|
32315
|
-
" hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
|
|
32316
|
-
m("p.no-mobile-block", [
|
|
32317
|
-
m(m.route.Link, { href: "/help" }, "Hjálp"),
|
|
32318
|
-
" má fá með því að smella á bláa ",
|
|
32319
|
-
glyph("info-sign"), nbsp(), "-", nbsp(),
|
|
32320
|
-
"teiknið hér til vinstri."
|
|
32321
|
-
]),
|
|
32322
|
-
m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
|
|
32323
|
-
"örvarmerkið efst vinstra megin við skraflborðið.")
|
|
32324
|
-
]);
|
|
32325
|
-
}
|
|
32326
|
-
return [
|
|
32327
|
-
m(".listitem.listheader", [
|
|
32328
|
-
m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
|
|
32329
|
-
m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
|
|
32330
|
-
mt("span.list-ts-short", "Síðasti leikur"),
|
|
32331
|
-
mt("span.list-opp", "Andstæðingur"),
|
|
32332
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32333
|
-
mt("span.list-scorehdr", "Staða"),
|
|
32334
|
-
mt("span.list-tc", "Framvinda"),
|
|
32335
|
-
m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
|
|
32336
|
-
]),
|
|
32337
|
-
vwList(),
|
|
32338
|
-
vwHint()
|
|
32339
|
-
];
|
|
32340
|
-
}
|
|
32341
|
-
function vwChallenges(showReceived) {
|
|
32342
|
-
function vwList() {
|
|
32343
|
-
if (!model.state)
|
|
32344
|
-
return undefined;
|
|
32345
|
-
const state = model.state;
|
|
32346
|
-
function itemize(item, i) {
|
|
32347
|
-
// Generate a list item about a pending challenge (issued or received)
|
|
32348
|
-
function challengeDescription(json) {
|
|
32349
|
-
/* Return a human-readable string describing a challenge
|
|
32350
|
-
according to the enclosed preferences */
|
|
32351
|
-
if (!json || json.duration === undefined || json.duration === 0)
|
|
32352
|
-
/* Normal unbounded (untimed) game */
|
|
32353
|
-
return t("Venjuleg ótímabundin viðureign");
|
|
32354
|
-
return t("with_clock", { duration: json.duration.toString() });
|
|
32355
|
-
}
|
|
32356
|
-
function markChallenge(ev) {
|
|
32357
|
-
// Clicked the icon at the beginning of the line,
|
|
32358
|
-
// to decline a received challenge or retract an issued challenge
|
|
32359
|
-
ev.preventDefault();
|
|
32360
|
-
if (item.received) {
|
|
32361
|
-
view.actions.declineChallenge(item.userid, item.key);
|
|
32362
|
-
}
|
|
32363
|
-
else {
|
|
32364
|
-
view.actions.retractChallenge(item.userid, item.key);
|
|
32365
|
-
}
|
|
32366
|
-
}
|
|
32367
|
-
function clickChallenge(ev) {
|
|
32368
|
-
// Clicked the hotspot area to accept a received challenge
|
|
32369
|
-
ev.preventDefault();
|
|
32370
|
-
if (!model.moreGamesAllowed()) {
|
|
32371
|
-
// User must be a friend to be able to accept more challenges
|
|
32372
|
-
logEvent("hit_game_limit", {
|
|
32373
|
-
userid: state.userId,
|
|
32374
|
-
locale: state.locale,
|
|
32375
|
-
limit: model.maxFreeGames
|
|
32376
|
-
});
|
|
32377
|
-
// Promote a subscription to Netskrafl/Explo
|
|
32378
|
-
view.showFriendPromo();
|
|
32379
|
-
return;
|
|
32380
|
-
}
|
|
32381
|
-
if (item.received) {
|
|
32382
|
-
if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
|
|
32383
|
-
// Timed game: display a modal wait dialog
|
|
32384
|
-
view.pushDialog("wait", {
|
|
32385
|
-
oppId: item.userid,
|
|
32386
|
-
oppNick: item.opp,
|
|
32387
|
-
oppName: item.fullname,
|
|
32388
|
-
duration: item.prefs.duration,
|
|
32389
|
-
challengeKey: item.key,
|
|
32390
|
-
});
|
|
32391
|
-
else
|
|
32392
|
-
// Ask the server to create a new game and route to it
|
|
32393
|
-
view.actions.startNewGame(item.userid, false);
|
|
32394
|
-
}
|
|
32395
|
-
else {
|
|
32396
|
-
// Clicking on a sent challenge, i.e. a timed game
|
|
32397
|
-
// where the opponent is waiting and ready to start
|
|
32398
|
-
view.showAcceptDialog(item.userid, item.opp, item.key);
|
|
32399
|
-
}
|
|
32400
|
-
}
|
|
32401
|
-
const oppReady = !item.received && item.opp_ready &&
|
|
32402
|
-
item.prefs && item.prefs.duration !== undefined &&
|
|
32403
|
-
item.prefs.duration > 0;
|
|
32404
|
-
const clickable = item.received || oppReady;
|
|
32405
|
-
const descr = challengeDescription(item.prefs);
|
|
32406
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32407
|
-
m("span.list-ch", { onclick: markChallenge }, item.received ?
|
|
32408
|
-
glyph("thumbs-down", { title: ts("Hafna") })
|
|
32409
|
-
:
|
|
32410
|
-
glyph("hand-right", { title: ts("Afturkalla") })),
|
|
32411
|
-
m(clickable ? "a.flex" : "span.flex", clickable ? {
|
|
32412
|
-
href: "#",
|
|
32413
|
-
onclick: clickChallenge,
|
|
32414
|
-
class: oppReady ? "opp-ready" : ""
|
|
32415
|
-
} : {}, [
|
|
32416
|
-
m("span.list-ts", item.ts),
|
|
32417
|
-
m("span.list-nick", { title: item.fullname }, item.opp),
|
|
32418
|
-
m("span.list-chall", [
|
|
32419
|
-
item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
|
|
32420
|
-
item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
|
|
32421
|
-
descr
|
|
32422
|
-
])
|
|
32423
|
-
]),
|
|
32424
|
-
m(UserInfoButton, {
|
|
32425
|
-
view,
|
|
32426
|
-
item: {
|
|
32427
|
-
userid: item.userid,
|
|
32428
|
-
nick: item.opp,
|
|
32429
|
-
fullname: item.fullname,
|
|
32430
|
-
}
|
|
32431
|
-
}),
|
|
32432
|
-
]);
|
|
32433
|
-
}
|
|
32434
|
-
let cList = [];
|
|
32435
|
-
if (model.challengeList)
|
|
32436
|
-
cList = showReceived ?
|
|
32437
|
-
model.challengeList.filter((item) => item.received) :
|
|
32438
|
-
model.challengeList.filter((item) => !item.received);
|
|
32439
|
-
return m("div", {
|
|
32440
|
-
id: showReceived ? 'chall-received' : 'chall-sent',
|
|
32441
|
-
oninit: () => {
|
|
32442
|
-
if (model.challengeList === null)
|
|
32443
|
-
model.loadChallengeList();
|
|
32444
|
-
}
|
|
32445
|
-
}, cList.map(itemize));
|
|
32446
|
-
}
|
|
32447
|
-
const n = vwList();
|
|
32448
|
-
if (!n)
|
|
32449
|
-
return [];
|
|
32450
|
-
if (showReceived)
|
|
32451
|
-
// Challenges received
|
|
32452
|
-
return [
|
|
32453
|
-
m(".listitem.listheader", [
|
|
32454
|
-
m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
|
|
32455
|
-
mt("span.list-ts", "Hvenær"),
|
|
32456
|
-
mt("span.list-nick", "Áskorandi"),
|
|
32457
|
-
mt("span.list-chall", "Hvernig"),
|
|
32458
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32459
|
-
]),
|
|
32460
|
-
n
|
|
32461
|
-
];
|
|
32462
|
-
else
|
|
32463
|
-
// Challenges sent
|
|
32464
|
-
return [
|
|
32465
|
-
m(".listitem.listheader", [
|
|
32466
|
-
m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
|
|
32467
|
-
mt("span.list-ts", "Hvenær"),
|
|
32468
|
-
mt("span.list-nick", "Andstæðingur"),
|
|
32469
|
-
mt("span.list-chall", "Hvernig"),
|
|
32470
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32471
|
-
]),
|
|
32472
|
-
n
|
|
32473
|
-
];
|
|
32474
|
-
}
|
|
32475
|
-
function vwRecentList() {
|
|
32476
|
-
function vwList() {
|
|
32477
|
-
if (model.recentList === null)
|
|
32478
|
-
model.loadRecentList();
|
|
32479
|
-
return m(RecentList, {
|
|
32480
|
-
view,
|
|
32481
|
-
id: "recentlist",
|
|
32482
|
-
recentList: model.recentList || [],
|
|
32483
|
-
});
|
|
32484
|
-
}
|
|
32485
|
-
return [
|
|
32486
|
-
m(".listitem.listheader", [
|
|
32487
|
-
m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
|
|
32488
|
-
mt("span.list-ts-short", "Viðureign lauk"),
|
|
32489
|
-
mt("span.list-nick", "Andstæðingur"),
|
|
32490
|
-
mt("span.list-scorehdr", "Úrslit"),
|
|
32491
|
-
m("span.list-elo-hdr", [
|
|
32492
|
-
m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
|
|
32493
|
-
t("Elo"),
|
|
32494
|
-
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32495
|
-
]),
|
|
32496
|
-
mt("span.list-duration", "Lengd"),
|
|
32497
|
-
m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
|
|
32953
|
+
// Local components for MainTabs
|
|
32954
|
+
const MainTabHeader = {
|
|
32955
|
+
// Tab navigation headers with game/challenge counts
|
|
32956
|
+
view: (vnode) => {
|
|
32957
|
+
const { view } = vnode.attrs;
|
|
32958
|
+
const { model } = view;
|
|
32959
|
+
const { numGames, numChallenges } = model;
|
|
32960
|
+
return [
|
|
32961
|
+
m(HeaderLogo, { hidden: true }),
|
|
32962
|
+
m("ul", [
|
|
32963
|
+
m("li", m("a[href='#tabs-1']", [
|
|
32964
|
+
glyph("th"), m("span.tab-legend", t("Viðureignir")),
|
|
32965
|
+
m("span", {
|
|
32966
|
+
id: 'numgames',
|
|
32967
|
+
style: numGames ? 'display: inline-block' : ''
|
|
32968
|
+
}, numGames)
|
|
32969
|
+
])),
|
|
32970
|
+
m("li", m("a[href='#tabs-2']", [
|
|
32971
|
+
glyph("hand-right"), m("span.tab-legend", t("Áskoranir")),
|
|
32972
|
+
// Blink if we have timed games where the opponent is ready
|
|
32973
|
+
m("span" + (model.oppReady ? ".opp-ready" : ""), {
|
|
32974
|
+
id: "numchallenges",
|
|
32975
|
+
style: numChallenges ? 'display: inline-block' : ''
|
|
32976
|
+
}, numChallenges)
|
|
32977
|
+
])),
|
|
32978
|
+
m("li", m("a[href='#tabs-3']", [glyph("user"), m("span.tab-legend", t("Andstæðingar"))])),
|
|
32979
|
+
m("li.no-mobile-list", m("a[href='#tabs-4']", [glyph("bookmark"), m("span.tab-legend", t("Ferill"))]))
|
|
32980
|
+
])
|
|
32981
|
+
];
|
|
32982
|
+
}
|
|
32983
|
+
};
|
|
32984
|
+
const RecentListWithHeader = {
|
|
32985
|
+
// Recent games list with header
|
|
32986
|
+
view: (vnode) => {
|
|
32987
|
+
const { view } = vnode.attrs;
|
|
32988
|
+
const { model } = view;
|
|
32989
|
+
if (model.recentList === null)
|
|
32990
|
+
model.loadRecentList();
|
|
32991
|
+
return [
|
|
32992
|
+
m(".listitem.listheader", [
|
|
32993
|
+
m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
|
|
32994
|
+
mt("span.list-ts-short", "Viðureign lauk"),
|
|
32995
|
+
mt("span.list-nick", "Andstæðingur"),
|
|
32996
|
+
mt("span.list-scorehdr", "Úrslit"),
|
|
32997
|
+
m("span.list-elo-hdr", [
|
|
32998
|
+
m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
|
|
32999
|
+
t("Elo"),
|
|
33000
|
+
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32498
33001
|
]),
|
|
32499
|
-
|
|
32500
|
-
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
|
|
32514
|
-
|
|
32515
|
-
|
|
32516
|
-
|
|
32517
|
-
|
|
32518
|
-
|
|
32519
|
-
|
|
32520
|
-
|
|
32521
|
-
}
|
|
32522
|
-
ev.preventDefault();
|
|
33002
|
+
mt("span.list-duration", "Lengd"),
|
|
33003
|
+
m(ProModeIndicator, { header: true })
|
|
33004
|
+
]),
|
|
33005
|
+
m(RecentList, { view, id: "recentlist", recentList: model.recentList || [] })
|
|
33006
|
+
];
|
|
33007
|
+
}
|
|
33008
|
+
};
|
|
33009
|
+
const UserButton = {
|
|
33010
|
+
// User list type button (robots, fav, alike, elo)
|
|
33011
|
+
view: (vnode) => {
|
|
33012
|
+
const { view, buttonId, icon, text } = vnode.attrs;
|
|
33013
|
+
const { model } = view;
|
|
33014
|
+
const sel = model.userListCriteria?.query || "robots";
|
|
33015
|
+
return m("span", {
|
|
33016
|
+
className: (buttonId === sel ? "shown" : ""),
|
|
33017
|
+
id: buttonId,
|
|
33018
|
+
onclick: (ev) => {
|
|
33019
|
+
if (buttonId === "elo") {
|
|
33020
|
+
// Load Elo rating list, defaulting to the human-only rating
|
|
33021
|
+
model.userListCriteria = { query: "elo", spec: "human" };
|
|
33022
|
+
model.userList = null;
|
|
33023
|
+
view.actions.withSpinner(() => model.loadEloRatingList("human"));
|
|
32523
33024
|
}
|
|
32524
|
-
|
|
32525
|
-
|
|
32526
|
-
|
|
32527
|
-
|
|
32528
|
-
|
|
32529
|
-
function itemize(item, i) {
|
|
32530
|
-
// Generate a list item about a user
|
|
32531
|
-
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
32532
|
-
let fullname = [];
|
|
32533
|
-
// Online and accepting challenges
|
|
32534
|
-
if (item.ready && !isRobot) {
|
|
32535
|
-
fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
|
|
32536
|
-
fullname.push(nbsp());
|
|
32537
|
-
}
|
|
32538
|
-
// Willing to accept challenges for timed games
|
|
32539
|
-
if (item.ready_timed) {
|
|
32540
|
-
fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
|
|
32541
|
-
fullname.push(nbsp());
|
|
32542
|
-
}
|
|
32543
|
-
// Fair play commitment
|
|
32544
|
-
if (item.fairplay) {
|
|
32545
|
-
fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
|
|
32546
|
-
fullname.push(nbsp());
|
|
32547
|
-
}
|
|
32548
|
-
fullname.push(item.fullname);
|
|
32549
|
-
function fav() {
|
|
32550
|
-
if (isRobot)
|
|
32551
|
-
return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
|
|
32552
|
-
return m("span.list-fav", {
|
|
32553
|
-
title: ts("Uppáhald"),
|
|
32554
|
-
onclick: (ev) => {
|
|
32555
|
-
view.actions.toggleFavorite(item.userid, item.fav);
|
|
32556
|
-
item.fav = !item.fav;
|
|
32557
|
-
ev.preventDefault();
|
|
32558
|
-
}
|
|
32559
|
-
}, glyph(item.fav ? "star" : "star-empty"));
|
|
32560
|
-
}
|
|
32561
|
-
function userLink() {
|
|
32562
|
-
if (isRobot)
|
|
32563
|
-
return m("a", {
|
|
32564
|
-
href: "",
|
|
32565
|
-
onclick: (ev) => {
|
|
32566
|
-
// Start a new game against the robot
|
|
32567
|
-
view.actions.startRobotGame(item.userid);
|
|
32568
|
-
ev.preventDefault();
|
|
32569
|
-
}
|
|
32570
|
-
}, [
|
|
32571
|
-
m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
|
|
32572
|
-
m("span.list-fullname-robot", fullname)
|
|
32573
|
-
]);
|
|
32574
|
-
else
|
|
32575
|
-
return m.fragment({}, [
|
|
32576
|
-
m("span.list-nick", item.nick),
|
|
32577
|
-
m("span.list-fullname", fullname),
|
|
32578
|
-
m("span.list-human-elo", item.human_elo)
|
|
32579
|
-
]);
|
|
32580
|
-
}
|
|
32581
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32582
|
-
m(ChallengeButton, { view, item }),
|
|
32583
|
-
fav(),
|
|
32584
|
-
userLink(),
|
|
32585
|
-
m(UserInfoButton, { view, item }),
|
|
32586
|
-
]);
|
|
33025
|
+
else {
|
|
33026
|
+
// Load user list
|
|
33027
|
+
view.actions.loadUsersByType(buttonId, true);
|
|
33028
|
+
model.eloRatingSpec = null;
|
|
33029
|
+
model.eloRatingList = null;
|
|
32587
33030
|
}
|
|
32588
|
-
|
|
32589
|
-
}
|
|
32590
|
-
// The type of list to show; by default it's 'robots'
|
|
32591
|
-
const query = (_b = (_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) !== null && _b !== void 0 ? _b : "";
|
|
32592
|
-
const spec = (_d = (_c = model.userListCriteria) === null || _c === void 0 ? void 0 : _c.spec) !== null && _d !== void 0 ? _d : "";
|
|
32593
|
-
const loadingElo = model.eloRatingSpec === undefined;
|
|
32594
|
-
const listType = query || "robots";
|
|
32595
|
-
if (listType === "elo" || loadingElo)
|
|
32596
|
-
// Show Elo list
|
|
32597
|
-
return [
|
|
32598
|
-
m(EloPage, {
|
|
32599
|
-
id: "elolist",
|
|
32600
|
-
view: view,
|
|
32601
|
-
spec: (_e = model.eloRatingSpec) !== null && _e !== void 0 ? _e : null,
|
|
32602
|
-
key: "elopage",
|
|
32603
|
-
})
|
|
32604
|
-
];
|
|
32605
|
-
// Show normal user list
|
|
32606
|
-
let list = [];
|
|
32607
|
-
if (model.userList === undefined) ;
|
|
32608
|
-
else if (model.userList === null || query !== listType) {
|
|
32609
|
-
view.actions.loadUsersByType(listType, true);
|
|
32610
|
-
}
|
|
32611
|
-
else {
|
|
32612
|
-
list = model.userList;
|
|
33031
|
+
ev.preventDefault();
|
|
32613
33032
|
}
|
|
32614
|
-
|
|
32615
|
-
|
|
32616
|
-
|
|
32617
|
-
|
|
32618
|
-
|
|
32619
|
-
|
|
32620
|
-
|
|
32621
|
-
|
|
32622
|
-
|
|
32623
|
-
|
|
32624
|
-
|
|
32625
|
-
|
|
32626
|
-
|
|
32627
|
-
|
|
32628
|
-
|
|
32629
|
-
|
|
32630
|
-
|
|
32631
|
-
|
|
32632
|
-
|
|
32633
|
-
|
|
32634
|
-
|
|
32635
|
-
|
|
32636
|
-
}
|
|
32637
|
-
|
|
32638
|
-
|
|
32639
|
-
|
|
32640
|
-
|
|
32641
|
-
|
|
32642
|
-
|
|
32643
|
-
}
|
|
32644
|
-
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
32648
|
-
|
|
32649
|
-
|
|
32650
|
-
|
|
32651
|
-
|
|
32652
|
-
|
|
32653
|
-
|
|
32654
|
-
|
|
32655
|
-
|
|
32656
|
-
|
|
32657
|
-
|
|
32658
|
-
|
|
32659
|
-
|
|
32660
|
-
|
|
32661
|
-
|
|
32662
|
-
|
|
32663
|
-
|
|
32664
|
-
|
|
32665
|
-
|
|
32666
|
-
|
|
32667
|
-
|
|
32668
|
-
|
|
32669
|
-
|
|
32670
|
-
|
|
32671
|
-
|
|
32672
|
-
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
|
|
32678
|
-
|
|
32679
|
-
|
|
32680
|
-
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
|
|
32684
|
-
|
|
32685
|
-
|
|
32686
|
-
|
|
32687
|
-
|
|
32688
|
-
|
|
32689
|
-
|
|
32690
|
-
|
|
32691
|
-
|
|
32692
|
-
" ",
|
|
32693
|
-
vwUserButton("alike", "resize-small", ts("Svipaðir")),
|
|
32694
|
-
" ",
|
|
32695
|
-
vwUserButton("elo", "crown", ts("Topp 100"))
|
|
32696
|
-
]),
|
|
32697
|
-
m(SearchButton, { view })
|
|
32698
|
-
]),
|
|
32699
|
-
vwUserList()
|
|
32700
|
-
]);
|
|
32701
|
-
}
|
|
32702
|
-
function tab4() {
|
|
32703
|
-
// Recent games and statistics
|
|
32704
|
-
return m("div", { id: 'tabs-4' }, [
|
|
32705
|
-
vwStats(),
|
|
32706
|
-
vwBest(),
|
|
32707
|
-
mt("p.no-mobile-block", [
|
|
32708
|
-
mt("strong", "Nýlegar viðureignir þínar"),
|
|
32709
|
-
"click_to_review" // "Click on a game to review it"
|
|
33033
|
+
}, [glyph(icon, { style: { padding: 0 } }), nbsp(), text]);
|
|
33034
|
+
}
|
|
33035
|
+
};
|
|
33036
|
+
const OwnStats = {
|
|
33037
|
+
// User's own statistics summary
|
|
33038
|
+
view: (vnode) => {
|
|
33039
|
+
const { view } = vnode.attrs;
|
|
33040
|
+
const { model } = view;
|
|
33041
|
+
const { ownStats } = model;
|
|
33042
|
+
if (ownStats === null)
|
|
33043
|
+
model.loadOwnStats();
|
|
33044
|
+
return ownStats ? m(StatsDisplay, { view, id: 'own-stats', ownStats }) : null;
|
|
33045
|
+
}
|
|
33046
|
+
};
|
|
33047
|
+
const OwnBest = {
|
|
33048
|
+
// User's own best game and word scores
|
|
33049
|
+
view: (vnode) => {
|
|
33050
|
+
const { view } = vnode.attrs;
|
|
33051
|
+
const { model } = view;
|
|
33052
|
+
const { ownStats } = model;
|
|
33053
|
+
if (ownStats === null)
|
|
33054
|
+
model.loadOwnStats();
|
|
33055
|
+
return ownStats ? m(BestDisplay, { id: 'own-best', ownStats, myself: true }) : null;
|
|
33056
|
+
}
|
|
33057
|
+
};
|
|
33058
|
+
const Tab1 = {
|
|
33059
|
+
// Ongoing games tab
|
|
33060
|
+
view: (vnode) => {
|
|
33061
|
+
const { view } = vnode.attrs;
|
|
33062
|
+
return m("div", { id: 'tabs-1' }, [
|
|
33063
|
+
mt("p.no-mobile-block", [
|
|
33064
|
+
mt("strong", "Viðureignir sem standa yfir"),
|
|
33065
|
+
"click_on_game", // "Click on a game to view it and make a move if"
|
|
33066
|
+
glyph("flag"),
|
|
33067
|
+
" þú átt leik"
|
|
33068
|
+
]),
|
|
33069
|
+
m(GameList, { view, id: 'gamelist' })
|
|
33070
|
+
]);
|
|
33071
|
+
}
|
|
33072
|
+
};
|
|
33073
|
+
const Tab2 = {
|
|
33074
|
+
// Challenges received and sent tab
|
|
33075
|
+
view: (vnode) => {
|
|
33076
|
+
const { view } = vnode.attrs;
|
|
33077
|
+
return m("div", { id: 'tabs-2' }, [
|
|
33078
|
+
mt("p.no-mobile-block", [
|
|
33079
|
+
mt("strong", "Skorað á þig"),
|
|
33080
|
+
"click_on_challenge", // "Click on a challenge to accept it and start a game, or on"
|
|
33081
|
+
glyph("thumbs-down", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
33082
|
+
" til að hafna henni"
|
|
33083
|
+
]),
|
|
33084
|
+
m(ChallengeList, { view, showReceived: true }),
|
|
33085
|
+
mt("p.no-mobile-block", [
|
|
33086
|
+
mt("strong", "Þú skorar á aðra"),
|
|
33087
|
+
" - smelltu á ",
|
|
33088
|
+
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
33089
|
+
" til að afturkalla áskorun"
|
|
33090
|
+
]),
|
|
33091
|
+
m(ChallengeList, { view, showReceived: false })
|
|
33092
|
+
]);
|
|
33093
|
+
}
|
|
33094
|
+
};
|
|
33095
|
+
const Tab3 = {
|
|
33096
|
+
// Opponent lists tab
|
|
33097
|
+
view: (vnode) => {
|
|
33098
|
+
const { view } = vnode.attrs;
|
|
33099
|
+
return m("div", { id: 'tabs-3' }, [
|
|
33100
|
+
m("div", { id: 'initials' }, [
|
|
33101
|
+
m(".user-cat[id='user-headings']", [
|
|
33102
|
+
m(UserButton, { view, buttonId: "robots", icon: "cog", text: ts("Þjarkar") }),
|
|
33103
|
+
" ",
|
|
33104
|
+
m(UserButton, { view, buttonId: "fav", icon: "star", text: ts("Uppáhalds") }),
|
|
33105
|
+
" ",
|
|
33106
|
+
m(UserButton, { view, buttonId: "live", icon: "flash", text: ts("Álínis") }),
|
|
33107
|
+
" ",
|
|
33108
|
+
m(UserButton, { view, buttonId: "alike", icon: "resize-small", text: ts("Svipaðir") }),
|
|
33109
|
+
" ",
|
|
33110
|
+
m(UserButton, { view, buttonId: "elo", icon: "crown", text: ts("Topp 100") })
|
|
32710
33111
|
]),
|
|
32711
|
-
|
|
32712
|
-
])
|
|
32713
|
-
|
|
33112
|
+
m(SearchButton, { view })
|
|
33113
|
+
]),
|
|
33114
|
+
m(UserList, { view, id: 'userlist' })
|
|
33115
|
+
]);
|
|
33116
|
+
}
|
|
33117
|
+
};
|
|
33118
|
+
const Tab4 = {
|
|
33119
|
+
// Recent games and statistics tab
|
|
33120
|
+
view: (vnode) => {
|
|
33121
|
+
const { view } = vnode.attrs;
|
|
33122
|
+
return m("div", { id: 'tabs-4' }, [
|
|
33123
|
+
m(OwnStats, { view }),
|
|
33124
|
+
m(OwnBest, { view }),
|
|
33125
|
+
mt("p.no-mobile-block", [
|
|
33126
|
+
mt("strong", "Nýlegar viðureignir þínar"),
|
|
33127
|
+
"click_to_review" // "Click on a game to review it"
|
|
33128
|
+
]),
|
|
33129
|
+
m(RecentListWithHeader, { view })
|
|
33130
|
+
]);
|
|
33131
|
+
}
|
|
33132
|
+
};
|
|
33133
|
+
const MainTabs = {
|
|
33134
|
+
// Main tabs component containing all tabs and their content
|
|
33135
|
+
view: (vnode) => {
|
|
33136
|
+
const { view } = vnode.attrs;
|
|
32714
33137
|
return m(".tabbed-page", m("div", { id: 'main-tabs' }, [
|
|
32715
|
-
|
|
33138
|
+
m(MainTabHeader, { view }),
|
|
32716
33139
|
m("div.tab-scroll-area", [
|
|
32717
|
-
|
|
32718
|
-
|
|
32719
|
-
|
|
32720
|
-
|
|
33140
|
+
m(Tab1, { view }),
|
|
33141
|
+
m(Tab2, { view }),
|
|
33142
|
+
m(Tab4, { view }),
|
|
33143
|
+
m(Tab3, { view }),
|
|
32721
33144
|
]),
|
|
32722
33145
|
]));
|
|
32723
33146
|
}
|
|
32724
|
-
|
|
32725
|
-
|
|
32726
|
-
|
|
32727
|
-
|
|
32728
|
-
|
|
32729
|
-
|
|
32730
|
-
|
|
32731
|
-
|
|
32732
|
-
|
|
32733
|
-
|
|
32734
|
-
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
},
|
|
32739
|
-
|
|
32740
|
-
|
|
32741
|
-
|
|
33147
|
+
};
|
|
33148
|
+
const Main = {
|
|
33149
|
+
// Main screen with tabs
|
|
33150
|
+
view: (vnode) => {
|
|
33151
|
+
const { view } = vnode.attrs;
|
|
33152
|
+
return [
|
|
33153
|
+
m(LeftLogo, { hidden: true }), // No legend, scale up by 50%
|
|
33154
|
+
m(UserId, { view }),
|
|
33155
|
+
m(Info),
|
|
33156
|
+
m(TogglerReady, { view }),
|
|
33157
|
+
m(TogglerReadyTimed, { view }),
|
|
33158
|
+
m("div", {
|
|
33159
|
+
oncreate: (vnode) => {
|
|
33160
|
+
makeTabs(view, "main-tabs", undefined, false, vnode);
|
|
33161
|
+
},
|
|
33162
|
+
onupdate: updateSelection
|
|
33163
|
+
}, m(MainTabs, { view })),
|
|
33164
|
+
];
|
|
33165
|
+
}
|
|
32742
33166
|
};
|
|
32743
33167
|
|
|
32744
33168
|
/*
|
|
@@ -32771,13 +33195,12 @@ const _updateStats = (attrs, state) => {
|
|
|
32771
33195
|
});
|
|
32772
33196
|
};
|
|
32773
33197
|
const _updateRecentList = (attrs, state) => {
|
|
32774
|
-
var _a, _b;
|
|
32775
33198
|
// Fetch the recent game list of the given user
|
|
32776
33199
|
if (state.loadingRecentList)
|
|
32777
33200
|
return;
|
|
32778
33201
|
state.loadingRecentList = true;
|
|
32779
33202
|
const { model } = attrs.view;
|
|
32780
|
-
model.loadUserRecentList(attrs.userid, state.versusAll ? null :
|
|
33203
|
+
model.loadUserRecentList(attrs.userid, state.versusAll ? null : model.state?.netskraflUserId ?? "", (json) => {
|
|
32781
33204
|
if (json && json.result === 0)
|
|
32782
33205
|
state.recentList = json.recentlist;
|
|
32783
33206
|
else
|
|
@@ -32832,10 +33255,9 @@ const UserInfoDialog = {
|
|
|
32832
33255
|
m(".usr-info-fav", {
|
|
32833
33256
|
title: ts('Uppáhald'),
|
|
32834
33257
|
onclick: (ev) => {
|
|
32835
|
-
var _a;
|
|
32836
33258
|
// Toggle the favorite setting
|
|
32837
33259
|
ev.preventDefault();
|
|
32838
|
-
view.actions.toggleFavorite(vnode.attrs.userid,
|
|
33260
|
+
view.actions.toggleFavorite(vnode.attrs.userid, stats.favorite ?? false);
|
|
32839
33261
|
stats.favorite = !stats.favorite;
|
|
32840
33262
|
}
|
|
32841
33263
|
}, stats.favorite ? glyph("star") : glyph("star-empty"))
|
|
@@ -32865,7 +33287,7 @@ const UserInfoDialog = {
|
|
|
32865
33287
|
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32866
33288
|
]),
|
|
32867
33289
|
mt("span.list-duration", "Lengd"),
|
|
32868
|
-
m(
|
|
33290
|
+
m(ProModeIndicator, { header: true })
|
|
32869
33291
|
]),
|
|
32870
33292
|
m(RecentList, { view, id: 'usr-recent', recentList }), // Recent game list
|
|
32871
33293
|
m(StatsDisplay, { view, id: 'usr-stats', ownStats: stats }),
|
|
@@ -32904,8 +33326,7 @@ const UserPrefsDialog = (initialVnode) => {
|
|
|
32904
33326
|
m(".errinput", [glyph("arrow-up"), nbsp(), err[propname] || ""]) : "";
|
|
32905
33327
|
}
|
|
32906
33328
|
function getToggle(elemId) {
|
|
32907
|
-
|
|
32908
|
-
const cls2 = (_a = document.querySelector("#" + elemId + "-toggler #opt2")) === null || _a === void 0 ? void 0 : _a.classList;
|
|
33329
|
+
const cls2 = document.querySelector("#" + elemId + "-toggler #opt2")?.classList;
|
|
32909
33330
|
if (!cls2)
|
|
32910
33331
|
return false;
|
|
32911
33332
|
return cls2.contains("selected");
|
|
@@ -33016,7 +33437,7 @@ const UserPrefsDialog = (initialVnode) => {
|
|
|
33016
33437
|
// Open the container's subscription page
|
|
33017
33438
|
ev.preventDefault();
|
|
33018
33439
|
logEvent("click_friend", {
|
|
33019
|
-
userid: state.
|
|
33440
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
33020
33441
|
});
|
|
33021
33442
|
// Navigate to the container-supplied URL
|
|
33022
33443
|
window.location.href = state.subscriptionUrl;
|
|
@@ -33452,7 +33873,7 @@ const Buttons = {
|
|
|
33452
33873
|
sc && r.push(sc);
|
|
33453
33874
|
}
|
|
33454
33875
|
// Is the server processing a move?
|
|
33455
|
-
if (game
|
|
33876
|
+
if (game?.moveInProgress) {
|
|
33456
33877
|
r.push(m(".waitmove", m(".animated-waitmove", m(AnimatedNetskraflLogo, {
|
|
33457
33878
|
withCircle: false,
|
|
33458
33879
|
}))));
|
|
@@ -33494,7 +33915,6 @@ class DragManager {
|
|
|
33494
33915
|
return (p.x >= rect.left) && (p.x < rect.right) && (p.y >= rect.top) && (p.y < rect.bottom);
|
|
33495
33916
|
}
|
|
33496
33917
|
constructor(e, dropHandler) {
|
|
33497
|
-
var _a, _b;
|
|
33498
33918
|
this.parentElement = null;
|
|
33499
33919
|
this.parentRect = null;
|
|
33500
33920
|
this.offsetX = 0;
|
|
@@ -33515,7 +33935,7 @@ class DragManager {
|
|
|
33515
33935
|
this.draggedElement = dragged;
|
|
33516
33936
|
this.dropHandler = dropHandler;
|
|
33517
33937
|
this.parentElement = dragged.parentElement;
|
|
33518
|
-
this.parentRect =
|
|
33938
|
+
this.parentRect = this.parentElement?.getBoundingClientRect() ?? null;
|
|
33519
33939
|
// Find out the bounding rectangle of the element
|
|
33520
33940
|
// before starting to apply modifications
|
|
33521
33941
|
const rect = dragged.getBoundingClientRect();
|
|
@@ -33873,7 +34293,6 @@ const DropTargetSquare = {
|
|
|
33873
34293
|
const RackTiles = {
|
|
33874
34294
|
// A rack of 7 tiles (tiles only, no buttons)
|
|
33875
34295
|
view: (vnode) => {
|
|
33876
|
-
var _a;
|
|
33877
34296
|
const { view, game, review } = vnode.attrs;
|
|
33878
34297
|
// If review==true, this is a review rack
|
|
33879
34298
|
// that is not a drop target and whose color reflects the
|
|
@@ -33882,7 +34301,7 @@ const RackTiles = {
|
|
|
33882
34301
|
return undefined;
|
|
33883
34302
|
const { model } = view;
|
|
33884
34303
|
let r = [];
|
|
33885
|
-
const reviewMove =
|
|
34304
|
+
const reviewMove = model.reviewMove ?? 0;
|
|
33886
34305
|
// Avoid flicker when paging through game review screen,
|
|
33887
34306
|
// as model.reviewMove becomes undefined or 0 while we
|
|
33888
34307
|
// are fetching the next move to display
|
|
@@ -34076,8 +34495,7 @@ const PlayerName = (initialVnode) => {
|
|
|
34076
34495
|
const { view } = initialVnode.attrs;
|
|
34077
34496
|
const model = view.model;
|
|
34078
34497
|
function lookAtPlayer(game, ev, player, side) {
|
|
34079
|
-
|
|
34080
|
-
if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen))
|
|
34498
|
+
if (!(model.state?.uiFullscreen))
|
|
34081
34499
|
// Don't do anything on mobile, and allow the click
|
|
34082
34500
|
// to propagate to the parent
|
|
34083
34501
|
return;
|
|
@@ -34292,14 +34710,13 @@ const Chat = (initialVnode) => {
|
|
|
34292
34710
|
return str;
|
|
34293
34711
|
}
|
|
34294
34712
|
function chatMessages() {
|
|
34295
|
-
var _a, _b;
|
|
34296
34713
|
let r = [];
|
|
34297
|
-
if (
|
|
34714
|
+
if (game?.chatLoading || !game?.messages)
|
|
34298
34715
|
return r;
|
|
34299
34716
|
var key = 0;
|
|
34300
|
-
const userId =
|
|
34717
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
34301
34718
|
for (const msg of game.messages) {
|
|
34302
|
-
let p = player
|
|
34719
|
+
let p = player ?? 0;
|
|
34303
34720
|
if (msg.from_userid != userId)
|
|
34304
34721
|
p = 1 - p;
|
|
34305
34722
|
const mTs = makeTimestamp(msg.ts, key);
|
|
@@ -34337,13 +34754,13 @@ const Chat = (initialVnode) => {
|
|
|
34337
34754
|
setInput("msg", "");
|
|
34338
34755
|
}
|
|
34339
34756
|
}
|
|
34340
|
-
const numMessages =
|
|
34757
|
+
const numMessages = game?.messages ? game.messages.length : 0;
|
|
34341
34758
|
return {
|
|
34342
34759
|
view: () => m(".chat-container", {
|
|
34343
34760
|
style: "z-index: 6" // Appear on top of board on mobile
|
|
34344
34761
|
// key: uuid
|
|
34345
34762
|
}, [
|
|
34346
|
-
m(".chat-area" + (
|
|
34763
|
+
m(".chat-area" + (game?.showClock() ? ".with-clock" : ""), {
|
|
34347
34764
|
id: 'chat-area',
|
|
34348
34765
|
// Make sure that we see the bottom-most chat message
|
|
34349
34766
|
oncreate: scrollChatToBottom,
|
|
@@ -34433,7 +34850,7 @@ const MoveListItem = (initialVnode) => {
|
|
|
34433
34850
|
else {
|
|
34434
34851
|
// Show a friend promotion dialog
|
|
34435
34852
|
logEvent("click_review", {
|
|
34436
|
-
userid: state.
|
|
34853
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
34437
34854
|
});
|
|
34438
34855
|
view.showFriendPromo();
|
|
34439
34856
|
}
|
|
@@ -34617,7 +35034,7 @@ const Movelist = (initialVnode) => {
|
|
|
34617
35034
|
onupdate: () => { setTimeout(scrollMovelistToBottom); }
|
|
34618
35035
|
}, movelist()),
|
|
34619
35036
|
// Show the bag here on mobile
|
|
34620
|
-
|
|
35037
|
+
state?.uiFullscreen ? "" : m(Bag, { bag, newbag })
|
|
34621
35038
|
])
|
|
34622
35039
|
};
|
|
34623
35040
|
};
|
|
@@ -34788,7 +35205,7 @@ const RightColumn = (initialVnode) => {
|
|
|
34788
35205
|
break;
|
|
34789
35206
|
}
|
|
34790
35207
|
const tabgrp = m(TabGroup, { view });
|
|
34791
|
-
return m(".right-area" + (
|
|
35208
|
+
return m(".right-area" + (game?.showClock() ? ".with-clock" : ""), component ? [tabgrp, component] : [tabgrp]);
|
|
34792
35209
|
}
|
|
34793
35210
|
return {
|
|
34794
35211
|
view: () => m(".rightcol", [
|
|
@@ -34879,14 +35296,14 @@ const GameView = {
|
|
|
34879
35296
|
m(RightColumn, { view }),
|
|
34880
35297
|
m(BoardArea, { view, game }),
|
|
34881
35298
|
// The bag is visible in fullscreen
|
|
34882
|
-
|
|
35299
|
+
state?.uiFullscreen ? m(Bag, { bag: bag, newbag: newbag }) : "",
|
|
34883
35300
|
game.askingForBlank ? m(BlankDialog, { game }) : ""
|
|
34884
35301
|
]),
|
|
34885
35302
|
// The left margin stuff: back button, square color help, info/help button
|
|
34886
35303
|
// These elements appear after the game container, since we want
|
|
34887
35304
|
// them to be above it in the z-order
|
|
34888
35305
|
m(LeftLogo),
|
|
34889
|
-
|
|
35306
|
+
state?.beginner ? m(Beginner, { view, showClose: true }) : "",
|
|
34890
35307
|
m(Info),
|
|
34891
35308
|
]);
|
|
34892
35309
|
}
|
|
@@ -35097,7 +35514,6 @@ const vwBestMove = (view, moveIndex, bestMoveIndex, move, info) => {
|
|
|
35097
35514
|
}
|
|
35098
35515
|
};
|
|
35099
35516
|
const vwScoreReview = (view, moveIndex) => {
|
|
35100
|
-
var _a;
|
|
35101
35517
|
// Shows the score of the current move within a game review screen
|
|
35102
35518
|
const game = view.model.game;
|
|
35103
35519
|
if (!game)
|
|
@@ -35111,7 +35527,7 @@ const vwScoreReview = (view, moveIndex) => {
|
|
|
35111
35527
|
return undefined;
|
|
35112
35528
|
let sc = [".score"];
|
|
35113
35529
|
if (moveIndex > 0) {
|
|
35114
|
-
if (moveIndex % 2 === (
|
|
35530
|
+
if (moveIndex % 2 === (game.player ?? 0))
|
|
35115
35531
|
// Opponent's move
|
|
35116
35532
|
sc.push("opponent");
|
|
35117
35533
|
else
|
|
@@ -35259,12 +35675,11 @@ const vwStatsReview = (view) => {
|
|
|
35259
35675
|
]);
|
|
35260
35676
|
};
|
|
35261
35677
|
const vwButtonsReview = (view, moveIndex) => {
|
|
35262
|
-
var _a, _b;
|
|
35263
35678
|
// The navigation buttons below the board on the review screen
|
|
35264
35679
|
const model = view.model;
|
|
35265
35680
|
const game = model.game;
|
|
35266
|
-
const numMoves =
|
|
35267
|
-
const gameUuid =
|
|
35681
|
+
const numMoves = game?.moves.length ?? 0;
|
|
35682
|
+
const gameUuid = game?.uuid ?? "";
|
|
35268
35683
|
let r = [];
|
|
35269
35684
|
if (!gameUuid)
|
|
35270
35685
|
return r;
|
|
@@ -35337,13 +35752,12 @@ const Review = (initialVnode) => {
|
|
|
35337
35752
|
}
|
|
35338
35753
|
return {
|
|
35339
35754
|
view: () => {
|
|
35340
|
-
var _a;
|
|
35341
35755
|
let r = [];
|
|
35342
35756
|
const { model } = view;
|
|
35343
35757
|
const { game } = model;
|
|
35344
35758
|
if (game) {
|
|
35345
35759
|
// Create a list of major elements that we're showing
|
|
35346
|
-
const moveIndex =
|
|
35760
|
+
const moveIndex = model.reviewMove ?? 0;
|
|
35347
35761
|
const bestMoves = model.bestMoves || [];
|
|
35348
35762
|
r.push(vwRightColumn(game, moveIndex, bestMoves));
|
|
35349
35763
|
r.push(m(BoardReview, { view, moveIndex }));
|
|
@@ -35498,12 +35912,11 @@ class View {
|
|
|
35498
35912
|
this.actions.hideSpinner();
|
|
35499
35913
|
}
|
|
35500
35914
|
notifyMediaChange() {
|
|
35501
|
-
var _a, _b;
|
|
35502
35915
|
// The view is changing, between mobile and fullscreen
|
|
35503
35916
|
// and/or between portrait and landscape: ensure that
|
|
35504
35917
|
// we don't end up with a selected game tab that is not visible
|
|
35505
35918
|
const model = this.model;
|
|
35506
|
-
if (
|
|
35919
|
+
if (model.state?.uiFullscreen || model.state?.uiLandscape) {
|
|
35507
35920
|
// In this case, there is no board tab:
|
|
35508
35921
|
// show the movelist
|
|
35509
35922
|
if (this.setSelectedTab("movelist"))
|
|
@@ -35540,7 +35953,7 @@ class View {
|
|
|
35540
35953
|
}
|
|
35541
35954
|
this.boardScale = 1.0;
|
|
35542
35955
|
const boardParent = document.getElementById("board-parent");
|
|
35543
|
-
const board = boardParent
|
|
35956
|
+
const board = boardParent?.children[0];
|
|
35544
35957
|
if (board) {
|
|
35545
35958
|
board.style.transition = 'none';
|
|
35546
35959
|
board.style.transform = `translate(0px, 0px) scale(1)`;
|
|
@@ -35549,7 +35962,6 @@ class View {
|
|
|
35549
35962
|
boardParent.scrollTo(0, 0);
|
|
35550
35963
|
}
|
|
35551
35964
|
updateScale() {
|
|
35552
|
-
var _a;
|
|
35553
35965
|
const model = this.model;
|
|
35554
35966
|
// Use either the regular game or the riddle (Gáta Dagsins)
|
|
35555
35967
|
const game = model.game || model.riddle;
|
|
@@ -35560,7 +35972,7 @@ class View {
|
|
|
35560
35972
|
// taking clamping into account to ensure that the board always fills
|
|
35561
35973
|
// the viewport.
|
|
35562
35974
|
const boardParent = document.getElementById("board-parent");
|
|
35563
|
-
const board = boardParent
|
|
35975
|
+
const board = boardParent?.children[0];
|
|
35564
35976
|
if (!board || !boardParent)
|
|
35565
35977
|
return;
|
|
35566
35978
|
const offset = 3;
|
|
@@ -35570,7 +35982,7 @@ class View {
|
|
|
35570
35982
|
const c = coord(row, col);
|
|
35571
35983
|
// board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
35572
35984
|
const el = document.getElementById("sq_" + c);
|
|
35573
|
-
const elRect = el
|
|
35985
|
+
const elRect = el?.getBoundingClientRect();
|
|
35574
35986
|
const boardRect = boardParent.getBoundingClientRect();
|
|
35575
35987
|
if (elRect && boardRect) {
|
|
35576
35988
|
// Get the dimensions of the scrollable area
|
|
@@ -35612,7 +36024,7 @@ class View {
|
|
|
35612
36024
|
}, 350);
|
|
35613
36025
|
}
|
|
35614
36026
|
};
|
|
35615
|
-
if (!game ||
|
|
36027
|
+
if (!game || model.state?.uiFullscreen || game.moveInProgress) {
|
|
35616
36028
|
// No game or we're in full screen mode: always 100% scale
|
|
35617
36029
|
// Also, as soon as a move is being processed by the server, we zoom out
|
|
35618
36030
|
this.boardScale = 1.0; // Needs to be done before setTimeout() call
|
|
@@ -35669,12 +36081,10 @@ class View {
|
|
|
35669
36081
|
function wireQuestions(vnode) {
|
|
35670
36082
|
// Clicking on a question brings the corresponding answer into view
|
|
35671
36083
|
// This is achieved by wiring up all contained a[href="#faq-*"] links
|
|
35672
|
-
var _a;
|
|
35673
36084
|
function showAnswer(ev, href) {
|
|
35674
|
-
var _a;
|
|
35675
36085
|
// this points to the vnode
|
|
35676
36086
|
vnode.state.selected = 1; // FAQ tab
|
|
35677
|
-
|
|
36087
|
+
vnode.dom.querySelector(href)?.scrollIntoView();
|
|
35678
36088
|
ev.preventDefault();
|
|
35679
36089
|
}
|
|
35680
36090
|
const anchors = vnode.dom.querySelectorAll("a");
|
|
@@ -35688,7 +36098,7 @@ class View {
|
|
|
35688
36098
|
// Go to the FAQ tab and scroll the requested question into view
|
|
35689
36099
|
selectTab(vnode, 1);
|
|
35690
36100
|
vnode.state.selected = 1; // FAQ tab
|
|
35691
|
-
|
|
36101
|
+
vnode.dom.querySelector("#faq-" + faqNumber.toString())?.scrollIntoView();
|
|
35692
36102
|
}
|
|
35693
36103
|
}
|
|
35694
36104
|
// Output literal HTML obtained from rawhelp.html on the server
|
|
@@ -35766,14 +36176,13 @@ class Actions {
|
|
|
35766
36176
|
// when view is available
|
|
35767
36177
|
}
|
|
35768
36178
|
onNavigateTo(routeName, params, view) {
|
|
35769
|
-
var _a, _b;
|
|
35770
36179
|
// We have navigated to a new route
|
|
35771
36180
|
// If navigating to something other than help,
|
|
35772
36181
|
// we need to have a logged-in user
|
|
35773
36182
|
const model = this.model;
|
|
35774
36183
|
model.routeName = routeName;
|
|
35775
36184
|
model.params = params;
|
|
35776
|
-
const uuid =
|
|
36185
|
+
const uuid = params.uuid ?? "";
|
|
35777
36186
|
if (routeName == "game") {
|
|
35778
36187
|
// New game route: initiate loading of the game into the model
|
|
35779
36188
|
if (model.game !== null) {
|
|
@@ -35783,10 +36192,9 @@ class Actions {
|
|
|
35783
36192
|
const deleteZombie = params.zombie === "1";
|
|
35784
36193
|
// Load the game, and attach it to the Firebase listener once it's loaded
|
|
35785
36194
|
model.loadGame(uuid, () => {
|
|
35786
|
-
var _a;
|
|
35787
36195
|
this.attachListenerToGame(uuid, view);
|
|
35788
36196
|
setTimeout(scrollMovelistToBottom);
|
|
35789
|
-
if (!
|
|
36197
|
+
if (!model.state?.uiFullscreen)
|
|
35790
36198
|
// Mobile UI: show board tab
|
|
35791
36199
|
view.setSelectedTab("board");
|
|
35792
36200
|
}, deleteZombie);
|
|
@@ -35814,10 +36222,9 @@ class Actions {
|
|
|
35814
36222
|
// Different game than we had before: load it, and then
|
|
35815
36223
|
// fetch the best moves
|
|
35816
36224
|
model.loadGame(uuid, () => {
|
|
35817
|
-
var _a;
|
|
35818
36225
|
model.loadBestMoves(move);
|
|
35819
36226
|
setTimeout(scrollMovelistToBottom);
|
|
35820
|
-
if (!
|
|
36227
|
+
if (!model.state?.uiFullscreen)
|
|
35821
36228
|
// Mobile UI: show board tab
|
|
35822
36229
|
view.setSelectedTab("board");
|
|
35823
36230
|
});
|
|
@@ -35835,7 +36242,7 @@ class Actions {
|
|
|
35835
36242
|
model.game.cleanup();
|
|
35836
36243
|
model.game = null;
|
|
35837
36244
|
}
|
|
35838
|
-
const locale =
|
|
36245
|
+
const locale = model.state?.locale || "is_IS";
|
|
35839
36246
|
if (routeName == "help") {
|
|
35840
36247
|
// Make sure that the help HTML is loaded upon first use
|
|
35841
36248
|
model.loadHelp();
|
|
@@ -35867,7 +36274,6 @@ class Actions {
|
|
|
35867
36274
|
this.model.handleUserMoveMessage(json, firstAttach);
|
|
35868
36275
|
}
|
|
35869
36276
|
onChatMessage(json, firstAttach, view) {
|
|
35870
|
-
var _a;
|
|
35871
36277
|
// Handle an incoming chat message
|
|
35872
36278
|
if (firstAttach) ;
|
|
35873
36279
|
else {
|
|
@@ -35879,7 +36285,7 @@ class Actions {
|
|
|
35879
36285
|
// - User has audio enabled
|
|
35880
36286
|
// - Message is from opponent (not from current user)
|
|
35881
36287
|
const { state } = this.model;
|
|
35882
|
-
const userId =
|
|
36288
|
+
const userId = state.netskraflUserId ?? "";
|
|
35883
36289
|
if (state.audio && json.from_userid !== userId) {
|
|
35884
36290
|
playAudio(state, "new-msg");
|
|
35885
36291
|
}
|
|
@@ -35983,26 +36389,26 @@ class Actions {
|
|
|
35983
36389
|
}
|
|
35984
36390
|
attachListenerToUser() {
|
|
35985
36391
|
const state = this.model.state;
|
|
35986
|
-
// console.log(`attachListenerToUser():
|
|
35987
|
-
if (!state || !state.
|
|
36392
|
+
// console.log(`attachListenerToUser(): netskraflUserId=${state?.netskraflUserId}`);
|
|
36393
|
+
if (!state || !state.netskraflUserId)
|
|
35988
36394
|
return;
|
|
35989
36395
|
// Listen to challenge and move notifications separately
|
|
35990
|
-
attachFirebaseListener(`user/${state.
|
|
35991
|
-
attachFirebaseListener(`user/${state.
|
|
36396
|
+
attachFirebaseListener(`user/${state.netskraflUserId}/challenge`, (json, firstAttach) => this.onUserChallengeMessage(json, firstAttach));
|
|
36397
|
+
attachFirebaseListener(`user/${state.netskraflUserId}/move`, (json, firstAttach) => this.onUserMoveMessage(json, firstAttach));
|
|
35992
36398
|
}
|
|
35993
36399
|
detachListenerFromUser() {
|
|
35994
36400
|
// Stop listening to Firebase notifications for the current user
|
|
35995
36401
|
const state = this.model.state;
|
|
35996
|
-
// console.log(`detachListenerFromUser():
|
|
35997
|
-
if (state && state.
|
|
35998
|
-
detachFirebaseListener('user/' + state.
|
|
36402
|
+
// console.log(`detachListenerFromUser(): netskraflUserId=${state?.netskraflUserId}`);
|
|
36403
|
+
if (state && state.netskraflUserId)
|
|
36404
|
+
detachFirebaseListener('user/' + state.netskraflUserId);
|
|
35999
36405
|
}
|
|
36000
36406
|
attachListenerToGame(uuid, view) {
|
|
36001
36407
|
// Listen to Firebase events on the /game/[gameId]/[userId] path
|
|
36002
36408
|
const state = this.model.state;
|
|
36003
36409
|
if (!uuid || !state)
|
|
36004
36410
|
return;
|
|
36005
|
-
const basepath = `game/${uuid}/${state.
|
|
36411
|
+
const basepath = `game/${uuid}/${state.netskraflUserId}/`;
|
|
36006
36412
|
// New moves
|
|
36007
36413
|
attachFirebaseListener(basepath + "move", (json, firstAttach) => this.onMoveMessage(json, firstAttach));
|
|
36008
36414
|
// New chat messages
|
|
@@ -36013,7 +36419,7 @@ class Actions {
|
|
|
36013
36419
|
const state = this.model.state;
|
|
36014
36420
|
if (!uuid || !state)
|
|
36015
36421
|
return;
|
|
36016
|
-
const basepath = `game/${uuid}/${state.
|
|
36422
|
+
const basepath = `game/${uuid}/${state.netskraflUserId}/`;
|
|
36017
36423
|
detachFirebaseListener(basepath + "move");
|
|
36018
36424
|
detachFirebaseListener(basepath + "chat");
|
|
36019
36425
|
}
|
|
@@ -36038,7 +36444,6 @@ class Actions {
|
|
|
36038
36444
|
}
|
|
36039
36445
|
// Challenge Management Actions
|
|
36040
36446
|
async handleChallenge(parameters) {
|
|
36041
|
-
var _a;
|
|
36042
36447
|
// Reject or retract a challenge
|
|
36043
36448
|
if (!this.model.state)
|
|
36044
36449
|
return;
|
|
@@ -36048,9 +36453,9 @@ class Actions {
|
|
|
36048
36453
|
url: "/challenge",
|
|
36049
36454
|
body: parameters
|
|
36050
36455
|
});
|
|
36051
|
-
if (
|
|
36456
|
+
if (json?.result === 0) {
|
|
36052
36457
|
// Log the change of challenge status (issue/decline/retract/accept)
|
|
36053
|
-
const locale =
|
|
36458
|
+
const locale = this.model.state?.locale || "is_IS";
|
|
36054
36459
|
var p = { locale };
|
|
36055
36460
|
if (parameters.duration !== undefined)
|
|
36056
36461
|
p.duration = parameters.duration;
|
|
@@ -36096,7 +36501,6 @@ class Actions {
|
|
|
36096
36501
|
}
|
|
36097
36502
|
// Game Management Actions
|
|
36098
36503
|
async startNewGame(oppid, reverse = false) {
|
|
36099
|
-
var _a;
|
|
36100
36504
|
// Ask the server to initiate a new game against the given opponent
|
|
36101
36505
|
if (!this.model.state)
|
|
36102
36506
|
return;
|
|
@@ -36113,9 +36517,9 @@ class Actions {
|
|
|
36113
36517
|
body: rqBody
|
|
36114
36518
|
};
|
|
36115
36519
|
const json = await request(this.model.state, rq);
|
|
36116
|
-
if (json
|
|
36520
|
+
if (json?.ok) {
|
|
36117
36521
|
// Log the new game event
|
|
36118
|
-
const locale =
|
|
36522
|
+
const locale = this.model.state?.locale || "is_IS";
|
|
36119
36523
|
logEvent("new_game", {
|
|
36120
36524
|
uuid: json.uuid,
|
|
36121
36525
|
timed: reverse,
|
|
@@ -36205,14 +36609,14 @@ class Actions {
|
|
|
36205
36609
|
url: "/cancelplan",
|
|
36206
36610
|
body: {}
|
|
36207
36611
|
});
|
|
36208
|
-
if (json
|
|
36612
|
+
if (json?.ok) {
|
|
36209
36613
|
// Successfully cancelled: immediately update the friend and hasPaid state
|
|
36210
36614
|
user.friend = false;
|
|
36211
36615
|
state.hasPaid = false;
|
|
36212
36616
|
state.plan = "";
|
|
36213
36617
|
// Log a friendship cancellation event
|
|
36214
36618
|
logEvent("cancel_plan", {
|
|
36215
|
-
userid: state.
|
|
36619
|
+
userid: state.netskraflUserId,
|
|
36216
36620
|
locale: state.locale,
|
|
36217
36621
|
// Add plan identifiers here
|
|
36218
36622
|
plan: "friend"
|
|
@@ -36231,29 +36635,29 @@ class Actions {
|
|
|
36231
36635
|
// Listen to global best score
|
|
36232
36636
|
attachFirebaseListener(basePath + "best", (json, firstAttach) => this.onRiddleGlobalScoreUpdate(json, firstAttach));
|
|
36233
36637
|
// Listen to group scores (if user has a group)
|
|
36234
|
-
if (state
|
|
36638
|
+
if (state?.userGroupId) {
|
|
36235
36639
|
attachFirebaseListener(basePath + `group/${state.userGroupId}/best`, (json, firstAttach) => this.onRiddleGroupScoreUpdate(json, firstAttach));
|
|
36236
36640
|
}
|
|
36237
36641
|
// Listen to global leaderboard
|
|
36238
36642
|
attachFirebaseListener(basePath + "leaders", (json, firstAttach) => this.onLeaderboardUpdate(json, firstAttach));
|
|
36239
36643
|
// Listen to user stats (if user is logged in)
|
|
36240
|
-
if (state
|
|
36241
|
-
attachFirebaseListener(`gatadagsins/users/${locale}/${state.
|
|
36644
|
+
if (state?.netskraflUserId) {
|
|
36645
|
+
attachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`, (json, firstAttach) => this.onUserStatsUpdate(json, firstAttach));
|
|
36242
36646
|
// Listen to personal best move (entire achievement object)
|
|
36243
|
-
attachFirebaseListener(basePath + `achievements/${state.
|
|
36647
|
+
attachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`, (json, firstAttach) => this.onPersonalBestScoreUpdate(json, firstAttach));
|
|
36244
36648
|
}
|
|
36245
36649
|
}
|
|
36246
36650
|
detachListenerFromRiddle(date, locale) {
|
|
36247
36651
|
const { state } = this.model;
|
|
36248
36652
|
const basePath = `gatadagsins/${date}/${locale}/`;
|
|
36249
36653
|
detachFirebaseListener(basePath + "best");
|
|
36250
|
-
if (state
|
|
36654
|
+
if (state?.userGroupId) {
|
|
36251
36655
|
detachFirebaseListener(basePath + `group/${state.userGroupId}/best`);
|
|
36252
36656
|
}
|
|
36253
36657
|
detachFirebaseListener(basePath + "leaders");
|
|
36254
|
-
if (state
|
|
36255
|
-
detachFirebaseListener(`gatadagsins/users/${locale}/${state.
|
|
36256
|
-
detachFirebaseListener(basePath + `achievements/${state.
|
|
36658
|
+
if (state?.netskraflUserId) {
|
|
36659
|
+
detachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`);
|
|
36660
|
+
detachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`);
|
|
36257
36661
|
}
|
|
36258
36662
|
}
|
|
36259
36663
|
onRiddleGlobalScoreUpdate(json, firstAttach) {
|
|
@@ -36461,7 +36865,6 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36461
36865
|
}, []);
|
|
36462
36866
|
*/
|
|
36463
36867
|
useEffect(() => {
|
|
36464
|
-
var _a;
|
|
36465
36868
|
// Load the Netskrafl (Mithril) UI for a new user
|
|
36466
36869
|
if (!userEmail)
|
|
36467
36870
|
return;
|
|
@@ -36471,7 +36874,7 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36471
36874
|
return;
|
|
36472
36875
|
}
|
|
36473
36876
|
const elemId = `netskrafl-user-${userEmail}`;
|
|
36474
|
-
if (
|
|
36877
|
+
if (container.firstElementChild?.id === elemId) {
|
|
36475
36878
|
// The Netskrafl UI is already correctly mounted
|
|
36476
36879
|
return;
|
|
36477
36880
|
}
|
|
@@ -36497,8 +36900,8 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36497
36900
|
// when the component is unmounted
|
|
36498
36901
|
// console.log("Dismounting Netskrafl UI for user", userEmail);
|
|
36499
36902
|
const container = document.getElementById("netskrafl-container");
|
|
36500
|
-
const div = container
|
|
36501
|
-
if (
|
|
36903
|
+
const div = container?.firstElementChild;
|
|
36904
|
+
if (div?.id === elemId) {
|
|
36502
36905
|
document.body.appendChild(div);
|
|
36503
36906
|
}
|
|
36504
36907
|
};
|
|
@@ -37139,7 +37542,7 @@ const RightSideTabs = () => {
|
|
|
37139
37542
|
return m(".gatadagsins-right-side-tabs", "");
|
|
37140
37543
|
}
|
|
37141
37544
|
// Check if current user is on the leaderboard
|
|
37142
|
-
const currentUserId =
|
|
37545
|
+
const currentUserId = state?.netskraflUserId || "";
|
|
37143
37546
|
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37144
37547
|
const tabs = [
|
|
37145
37548
|
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
@@ -37178,7 +37581,7 @@ const RightSideTabs = () => {
|
|
|
37178
37581
|
// Leaderboard tab
|
|
37179
37582
|
activeTab === "leaderboard" ? m(LeaderboardView, {
|
|
37180
37583
|
leaderboard: view.model.leaderboard || [],
|
|
37181
|
-
currentUserId:
|
|
37584
|
+
currentUserId: state?.netskraflUserId || "",
|
|
37182
37585
|
date: riddle.date,
|
|
37183
37586
|
loading: false
|
|
37184
37587
|
}) : null
|
|
@@ -37355,7 +37758,7 @@ const StatsModal = () => {
|
|
|
37355
37758
|
return null;
|
|
37356
37759
|
}
|
|
37357
37760
|
// Check if current user is on the leaderboard
|
|
37358
|
-
const currentUserId =
|
|
37761
|
+
const currentUserId = state?.netskraflUserId || "";
|
|
37359
37762
|
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37360
37763
|
const tabs = [
|
|
37361
37764
|
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
@@ -37401,7 +37804,7 @@ const StatsModal = () => {
|
|
|
37401
37804
|
}) : null,
|
|
37402
37805
|
activeTab === "leaderboard" ? m(LeaderboardView, {
|
|
37403
37806
|
leaderboard: view.model.leaderboard || [],
|
|
37404
|
-
currentUserId:
|
|
37807
|
+
currentUserId: state?.netskraflUserId || "",
|
|
37405
37808
|
date: riddle.date,
|
|
37406
37809
|
loading: false
|
|
37407
37810
|
}) : null
|
|
@@ -37429,8 +37832,7 @@ const StatsModal = () => {
|
|
|
37429
37832
|
*/
|
|
37430
37833
|
const MAX_MOVES_TO_DISPLAY = 10;
|
|
37431
37834
|
const currentMoveState = (riddle) => {
|
|
37432
|
-
|
|
37433
|
-
const thisPlayer = ((_a = riddle.model.state) === null || _a === void 0 ? void 0 : _a.userId) || "";
|
|
37835
|
+
const thisPlayer = riddle.model.state?.netskraflUserId || "";
|
|
37434
37836
|
const { bestPossibleScore, // The highest score achievable for this riddle
|
|
37435
37837
|
globalBestScore, // The best score achieved by any player
|
|
37436
37838
|
groupBestScore, // The best score achieved within the player's group
|
|
@@ -37451,7 +37853,7 @@ const currentMoveState = (riddle) => {
|
|
|
37451
37853
|
// Check whether we need to add or annotate the global best score
|
|
37452
37854
|
if (globalBestScore && globalBestScore.score > 0) {
|
|
37453
37855
|
const { score, word, coord } = globalBestScore;
|
|
37454
|
-
if ((
|
|
37856
|
+
if ((selectedMoves[0]?.score ?? 0) >= score) {
|
|
37455
37857
|
// This player has made a move that scores the same
|
|
37456
37858
|
// or better as the top score: mark the move
|
|
37457
37859
|
selectedMoves[0].isGlobalBestScore = true;
|
|
@@ -37493,7 +37895,6 @@ const GataDagsins$1 = () => {
|
|
|
37493
37895
|
showStatsModal = false;
|
|
37494
37896
|
},
|
|
37495
37897
|
view: (vnode) => {
|
|
37496
|
-
var _a;
|
|
37497
37898
|
const { view } = vnode.attrs;
|
|
37498
37899
|
const { model } = view;
|
|
37499
37900
|
const { riddle } = model;
|
|
@@ -37528,7 +37929,7 @@ const GataDagsins$1 = () => {
|
|
|
37528
37929
|
// These elements appear after the main container for proper z-order
|
|
37529
37930
|
// m(LeftLogo), // Currently no need for the logo for Gáta Dagsins
|
|
37530
37931
|
// Show the Beginner component if the user is a beginner
|
|
37531
|
-
|
|
37932
|
+
model.state?.beginner ? m(Beginner, { view, showClose: false }) : "",
|
|
37532
37933
|
// Custom Info button for GataDagsins that shows help dialog
|
|
37533
37934
|
m(".info", { title: ts("Upplýsingar og hjálp") }, m("a.iconlink", { href: "#", onclick: (e) => { e.preventDefault(); toggleHelp(); } }, glyph("info-sign"))),
|
|
37534
37935
|
// Help dialog and backdrop
|
|
@@ -37637,7 +38038,6 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37637
38038
|
const completeState = makeGlobalState({ ...state, tokenExpired });
|
|
37638
38039
|
const { userEmail } = completeState;
|
|
37639
38040
|
useEffect(() => {
|
|
37640
|
-
var _a;
|
|
37641
38041
|
// Load the Gáta Dagsins (Mithril) UI for a new user
|
|
37642
38042
|
if (!userEmail)
|
|
37643
38043
|
return;
|
|
@@ -37647,7 +38047,7 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37647
38047
|
return;
|
|
37648
38048
|
}
|
|
37649
38049
|
const elemId = `gatadagsins-user-${userEmail}`;
|
|
37650
|
-
if (
|
|
38050
|
+
if (container.firstElementChild?.id === elemId) {
|
|
37651
38051
|
// The Gáta Dagsins UI is already correctly mounted
|
|
37652
38052
|
return;
|
|
37653
38053
|
}
|
|
@@ -37673,8 +38073,8 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37673
38073
|
// when the component is unmounted
|
|
37674
38074
|
// console.log("Dismounting Gáta Dagsins UI for user", userEmail);
|
|
37675
38075
|
const container = document.getElementById("gatadagsins-container");
|
|
37676
|
-
const div = container
|
|
37677
|
-
if (
|
|
38076
|
+
const div = container?.firstElementChild;
|
|
38077
|
+
if (div?.id === elemId) {
|
|
37678
38078
|
document.body.appendChild(div);
|
|
37679
38079
|
}
|
|
37680
38080
|
};
|