@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/cjs/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const DEFAULT_STATE = {
|
|
|
12
12
|
measurementId: "",
|
|
13
13
|
account: "",
|
|
14
14
|
userEmail: "",
|
|
15
|
-
|
|
15
|
+
netskraflUserId: "",
|
|
16
16
|
userNick: "",
|
|
17
17
|
userFullname: "",
|
|
18
18
|
locale: "is_IS",
|
|
@@ -76,8 +76,8 @@ const saveAuthSettings = (settings) => {
|
|
|
76
76
|
// Only add optional fields if they are defined
|
|
77
77
|
if (settings.account !== undefined)
|
|
78
78
|
filteredSettings.account = settings.account;
|
|
79
|
-
if (settings.
|
|
80
|
-
filteredSettings.
|
|
79
|
+
if (settings.netskraflUserId !== undefined)
|
|
80
|
+
filteredSettings.netskraflUserId = settings.netskraflUserId;
|
|
81
81
|
if (settings.userNick !== undefined)
|
|
82
82
|
filteredSettings.userNick = settings.userNick;
|
|
83
83
|
if (settings.firebaseApiKey !== undefined)
|
|
@@ -132,7 +132,6 @@ const clearAuthSettings = () => {
|
|
|
132
132
|
};
|
|
133
133
|
// Apply persisted settings to a GlobalState object
|
|
134
134
|
const applyPersistedSettings = (state) => {
|
|
135
|
-
var _a, _b, _c, _d, _e, _f;
|
|
136
135
|
const persisted = loadAuthSettings();
|
|
137
136
|
if (!persisted) {
|
|
138
137
|
return state;
|
|
@@ -148,18 +147,18 @@ const applyPersistedSettings = (state) => {
|
|
|
148
147
|
return {
|
|
149
148
|
...state,
|
|
150
149
|
// Only apply persisted values if current values are defaults
|
|
151
|
-
account:
|
|
152
|
-
|
|
150
|
+
account: persisted.account || state.account || state.netskraflUserId,
|
|
151
|
+
netskraflUserId: persisted.netskraflUserId || state.netskraflUserId,
|
|
153
152
|
// userNick is special: the container provides a "guess", but the backend login
|
|
154
153
|
// returns the authoritative nickname, which should persist across re-initializations
|
|
155
154
|
userNick: persisted.userNick || state.userNick,
|
|
156
155
|
firebaseApiKey: state.firebaseApiKey || persisted.firebaseApiKey || state.firebaseApiKey,
|
|
157
|
-
beginner:
|
|
158
|
-
fairPlay:
|
|
159
|
-
ready:
|
|
160
|
-
readyTimed:
|
|
161
|
-
audio:
|
|
162
|
-
fanfare:
|
|
156
|
+
beginner: persisted.beginner ?? state.beginner,
|
|
157
|
+
fairPlay: persisted.fairPlay ?? state.fairPlay,
|
|
158
|
+
ready: persisted.ready ?? state.ready,
|
|
159
|
+
readyTimed: persisted.readyTimed ?? state.readyTimed,
|
|
160
|
+
audio: persisted.audio ?? state.audio,
|
|
161
|
+
fanfare: persisted.fanfare ?? state.fanfare,
|
|
163
162
|
};
|
|
164
163
|
};
|
|
165
164
|
|
|
@@ -2439,7 +2438,7 @@ async function loadMessages(state, locale) {
|
|
|
2439
2438
|
});
|
|
2440
2439
|
setLocale(locale, messages);
|
|
2441
2440
|
}
|
|
2442
|
-
catch
|
|
2441
|
+
catch {
|
|
2443
2442
|
setLocale(locale, {});
|
|
2444
2443
|
}
|
|
2445
2444
|
}
|
|
@@ -2803,7 +2802,7 @@ function escapeHtml(string) {
|
|
|
2803
2802
|
"'": ''',
|
|
2804
2803
|
"/": '/'
|
|
2805
2804
|
};
|
|
2806
|
-
return String(string).replace(/[&<>"'/]/g, (s) =>
|
|
2805
|
+
return String(string).replace(/[&<>"'/]/g, (s) => entityMap[s] ?? "");
|
|
2807
2806
|
}
|
|
2808
2807
|
function getUrlVars(url) {
|
|
2809
2808
|
// Get values from a URL query string
|
|
@@ -2940,6 +2939,8 @@ const loginUserByEmail = async (state) => {
|
|
|
2940
2939
|
});
|
|
2941
2940
|
};
|
|
2942
2941
|
|
|
2942
|
+
const getDefaultsFromPostinstall = () => (undefined);
|
|
2943
|
+
|
|
2943
2944
|
/**
|
|
2944
2945
|
* @license
|
|
2945
2946
|
* Copyright 2017 Google LLC
|
|
@@ -3488,7 +3489,8 @@ const getDefaultsFromCookie = () => {
|
|
|
3488
3489
|
*/
|
|
3489
3490
|
const getDefaults = () => {
|
|
3490
3491
|
try {
|
|
3491
|
-
return (
|
|
3492
|
+
return (getDefaultsFromPostinstall() ||
|
|
3493
|
+
getDefaultsFromGlobal() ||
|
|
3492
3494
|
getDefaultsFromEnvVariable() ||
|
|
3493
3495
|
getDefaultsFromCookie());
|
|
3494
3496
|
}
|
|
@@ -3509,7 +3511,7 @@ const getDefaults = () => {
|
|
|
3509
3511
|
* @returns a URL host formatted like `127.0.0.1:9999` or `[::1]:4000` if available
|
|
3510
3512
|
* @public
|
|
3511
3513
|
*/
|
|
3512
|
-
const getDefaultEmulatorHost = (productName) =>
|
|
3514
|
+
const getDefaultEmulatorHost = (productName) => getDefaults()?.emulatorHosts?.[productName];
|
|
3513
3515
|
/**
|
|
3514
3516
|
* Returns emulator hostname and port stored in the __FIREBASE_DEFAULTS__ object
|
|
3515
3517
|
* for the given product.
|
|
@@ -3539,13 +3541,13 @@ const getDefaultEmulatorHostnameAndPort = (productName) => {
|
|
|
3539
3541
|
* Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object.
|
|
3540
3542
|
* @public
|
|
3541
3543
|
*/
|
|
3542
|
-
const getDefaultAppConfig = () =>
|
|
3544
|
+
const getDefaultAppConfig = () => getDefaults()?.config;
|
|
3543
3545
|
/**
|
|
3544
3546
|
* Returns an experimental setting on the __FIREBASE_DEFAULTS__ object (properties
|
|
3545
3547
|
* prefixed by "_")
|
|
3546
3548
|
* @public
|
|
3547
3549
|
*/
|
|
3548
|
-
const getExperimentalSetting = (name) =>
|
|
3550
|
+
const getExperimentalSetting = (name) => getDefaults()?.[`_${name}`];
|
|
3549
3551
|
|
|
3550
3552
|
/**
|
|
3551
3553
|
* @license
|
|
@@ -3602,6 +3604,53 @@ class Deferred {
|
|
|
3602
3604
|
}
|
|
3603
3605
|
}
|
|
3604
3606
|
|
|
3607
|
+
/**
|
|
3608
|
+
* @license
|
|
3609
|
+
* Copyright 2025 Google LLC
|
|
3610
|
+
*
|
|
3611
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3612
|
+
* you may not use this file except in compliance with the License.
|
|
3613
|
+
* You may obtain a copy of the License at
|
|
3614
|
+
*
|
|
3615
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3616
|
+
*
|
|
3617
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3618
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3619
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3620
|
+
* See the License for the specific language governing permissions and
|
|
3621
|
+
* limitations under the License.
|
|
3622
|
+
*/
|
|
3623
|
+
/**
|
|
3624
|
+
* Checks whether host is a cloud workstation or not.
|
|
3625
|
+
* @public
|
|
3626
|
+
*/
|
|
3627
|
+
function isCloudWorkstation(url) {
|
|
3628
|
+
// `isCloudWorkstation` is called without protocol in certain connect*Emulator functions
|
|
3629
|
+
// In HTTP request builders, it's called with the protocol.
|
|
3630
|
+
// If called with protocol prefix, it's a valid URL, so we extract the hostname
|
|
3631
|
+
// If called without, we assume the string is the hostname.
|
|
3632
|
+
try {
|
|
3633
|
+
const host = url.startsWith('http://') || url.startsWith('https://')
|
|
3634
|
+
? new URL(url).hostname
|
|
3635
|
+
: url;
|
|
3636
|
+
return host.endsWith('.cloudworkstations.dev');
|
|
3637
|
+
}
|
|
3638
|
+
catch {
|
|
3639
|
+
return false;
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
/**
|
|
3643
|
+
* Makes a fetch request to the given server.
|
|
3644
|
+
* Mostly used for forwarding cookies in Firebase Studio.
|
|
3645
|
+
* @public
|
|
3646
|
+
*/
|
|
3647
|
+
async function pingServer(endpoint) {
|
|
3648
|
+
const result = await fetch(endpoint, {
|
|
3649
|
+
credentials: 'include'
|
|
3650
|
+
});
|
|
3651
|
+
return result.ok;
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3605
3654
|
/**
|
|
3606
3655
|
* @license
|
|
3607
3656
|
* Copyright 2021 Google LLC
|
|
@@ -3633,12 +3682,22 @@ function createMockUserToken(token, projectId) {
|
|
|
3633
3682
|
if (!sub) {
|
|
3634
3683
|
throw new Error("mockUserToken must contain 'sub' or 'user_id' field!");
|
|
3635
3684
|
}
|
|
3636
|
-
const payload =
|
|
3685
|
+
const payload = {
|
|
3637
3686
|
// Set all required fields to decent defaults
|
|
3638
|
-
iss: `https://securetoken.google.com/${project}`,
|
|
3687
|
+
iss: `https://securetoken.google.com/${project}`,
|
|
3688
|
+
aud: project,
|
|
3689
|
+
iat,
|
|
3690
|
+
exp: iat + 3600,
|
|
3691
|
+
auth_time: iat,
|
|
3692
|
+
sub,
|
|
3693
|
+
user_id: sub,
|
|
3694
|
+
firebase: {
|
|
3639
3695
|
sign_in_provider: 'custom',
|
|
3640
3696
|
identities: {}
|
|
3641
|
-
}
|
|
3697
|
+
},
|
|
3698
|
+
// Override with user options
|
|
3699
|
+
...token
|
|
3700
|
+
};
|
|
3642
3701
|
// Unsecured JWTs use the empty string as a signature.
|
|
3643
3702
|
const signature = '';
|
|
3644
3703
|
return [
|
|
@@ -3647,6 +3706,152 @@ function createMockUserToken(token, projectId) {
|
|
|
3647
3706
|
signature
|
|
3648
3707
|
].join('.');
|
|
3649
3708
|
}
|
|
3709
|
+
const emulatorStatus = {};
|
|
3710
|
+
// Checks whether any products are running on an emulator
|
|
3711
|
+
function getEmulatorSummary() {
|
|
3712
|
+
const summary = {
|
|
3713
|
+
prod: [],
|
|
3714
|
+
emulator: []
|
|
3715
|
+
};
|
|
3716
|
+
for (const key of Object.keys(emulatorStatus)) {
|
|
3717
|
+
if (emulatorStatus[key]) {
|
|
3718
|
+
summary.emulator.push(key);
|
|
3719
|
+
}
|
|
3720
|
+
else {
|
|
3721
|
+
summary.prod.push(key);
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
return summary;
|
|
3725
|
+
}
|
|
3726
|
+
function getOrCreateEl(id) {
|
|
3727
|
+
let parentDiv = document.getElementById(id);
|
|
3728
|
+
let created = false;
|
|
3729
|
+
if (!parentDiv) {
|
|
3730
|
+
parentDiv = document.createElement('div');
|
|
3731
|
+
parentDiv.setAttribute('id', id);
|
|
3732
|
+
created = true;
|
|
3733
|
+
}
|
|
3734
|
+
return { created, element: parentDiv };
|
|
3735
|
+
}
|
|
3736
|
+
let previouslyDismissed = false;
|
|
3737
|
+
/**
|
|
3738
|
+
* Updates Emulator Banner. Primarily used for Firebase Studio
|
|
3739
|
+
* @param name
|
|
3740
|
+
* @param isRunningEmulator
|
|
3741
|
+
* @public
|
|
3742
|
+
*/
|
|
3743
|
+
function updateEmulatorBanner(name, isRunningEmulator) {
|
|
3744
|
+
if (typeof window === 'undefined' ||
|
|
3745
|
+
typeof document === 'undefined' ||
|
|
3746
|
+
!isCloudWorkstation(window.location.host) ||
|
|
3747
|
+
emulatorStatus[name] === isRunningEmulator ||
|
|
3748
|
+
emulatorStatus[name] || // If already set to use emulator, can't go back to prod.
|
|
3749
|
+
previouslyDismissed) {
|
|
3750
|
+
return;
|
|
3751
|
+
}
|
|
3752
|
+
emulatorStatus[name] = isRunningEmulator;
|
|
3753
|
+
function prefixedId(id) {
|
|
3754
|
+
return `__firebase__banner__${id}`;
|
|
3755
|
+
}
|
|
3756
|
+
const bannerId = '__firebase__banner';
|
|
3757
|
+
const summary = getEmulatorSummary();
|
|
3758
|
+
const showError = summary.prod.length > 0;
|
|
3759
|
+
function tearDown() {
|
|
3760
|
+
const element = document.getElementById(bannerId);
|
|
3761
|
+
if (element) {
|
|
3762
|
+
element.remove();
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
function setupBannerStyles(bannerEl) {
|
|
3766
|
+
bannerEl.style.display = 'flex';
|
|
3767
|
+
bannerEl.style.background = '#7faaf0';
|
|
3768
|
+
bannerEl.style.position = 'fixed';
|
|
3769
|
+
bannerEl.style.bottom = '5px';
|
|
3770
|
+
bannerEl.style.left = '5px';
|
|
3771
|
+
bannerEl.style.padding = '.5em';
|
|
3772
|
+
bannerEl.style.borderRadius = '5px';
|
|
3773
|
+
bannerEl.style.alignItems = 'center';
|
|
3774
|
+
}
|
|
3775
|
+
function setupIconStyles(prependIcon, iconId) {
|
|
3776
|
+
prependIcon.setAttribute('width', '24');
|
|
3777
|
+
prependIcon.setAttribute('id', iconId);
|
|
3778
|
+
prependIcon.setAttribute('height', '24');
|
|
3779
|
+
prependIcon.setAttribute('viewBox', '0 0 24 24');
|
|
3780
|
+
prependIcon.setAttribute('fill', 'none');
|
|
3781
|
+
prependIcon.style.marginLeft = '-6px';
|
|
3782
|
+
}
|
|
3783
|
+
function setupCloseBtn() {
|
|
3784
|
+
const closeBtn = document.createElement('span');
|
|
3785
|
+
closeBtn.style.cursor = 'pointer';
|
|
3786
|
+
closeBtn.style.marginLeft = '16px';
|
|
3787
|
+
closeBtn.style.fontSize = '24px';
|
|
3788
|
+
closeBtn.innerHTML = ' ×';
|
|
3789
|
+
closeBtn.onclick = () => {
|
|
3790
|
+
previouslyDismissed = true;
|
|
3791
|
+
tearDown();
|
|
3792
|
+
};
|
|
3793
|
+
return closeBtn;
|
|
3794
|
+
}
|
|
3795
|
+
function setupLinkStyles(learnMoreLink, learnMoreId) {
|
|
3796
|
+
learnMoreLink.setAttribute('id', learnMoreId);
|
|
3797
|
+
learnMoreLink.innerText = 'Learn more';
|
|
3798
|
+
learnMoreLink.href =
|
|
3799
|
+
'https://firebase.google.com/docs/studio/preview-apps#preview-backend';
|
|
3800
|
+
learnMoreLink.setAttribute('target', '__blank');
|
|
3801
|
+
learnMoreLink.style.paddingLeft = '5px';
|
|
3802
|
+
learnMoreLink.style.textDecoration = 'underline';
|
|
3803
|
+
}
|
|
3804
|
+
function setupDom() {
|
|
3805
|
+
const banner = getOrCreateEl(bannerId);
|
|
3806
|
+
const firebaseTextId = prefixedId('text');
|
|
3807
|
+
const firebaseText = document.getElementById(firebaseTextId) || document.createElement('span');
|
|
3808
|
+
const learnMoreId = prefixedId('learnmore');
|
|
3809
|
+
const learnMoreLink = document.getElementById(learnMoreId) ||
|
|
3810
|
+
document.createElement('a');
|
|
3811
|
+
const prependIconId = prefixedId('preprendIcon');
|
|
3812
|
+
const prependIcon = document.getElementById(prependIconId) ||
|
|
3813
|
+
document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
3814
|
+
if (banner.created) {
|
|
3815
|
+
// update styles
|
|
3816
|
+
const bannerEl = banner.element;
|
|
3817
|
+
setupBannerStyles(bannerEl);
|
|
3818
|
+
setupLinkStyles(learnMoreLink, learnMoreId);
|
|
3819
|
+
const closeBtn = setupCloseBtn();
|
|
3820
|
+
setupIconStyles(prependIcon, prependIconId);
|
|
3821
|
+
bannerEl.append(prependIcon, firebaseText, learnMoreLink, closeBtn);
|
|
3822
|
+
document.body.appendChild(bannerEl);
|
|
3823
|
+
}
|
|
3824
|
+
if (showError) {
|
|
3825
|
+
firebaseText.innerText = `Preview backend disconnected.`;
|
|
3826
|
+
prependIcon.innerHTML = `<g clip-path="url(#clip0_6013_33858)">
|
|
3827
|
+
<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"/>
|
|
3828
|
+
</g>
|
|
3829
|
+
<defs>
|
|
3830
|
+
<clipPath id="clip0_6013_33858">
|
|
3831
|
+
<rect width="24" height="24" fill="white"/>
|
|
3832
|
+
</clipPath>
|
|
3833
|
+
</defs>`;
|
|
3834
|
+
}
|
|
3835
|
+
else {
|
|
3836
|
+
prependIcon.innerHTML = `<g clip-path="url(#clip0_6083_34804)">
|
|
3837
|
+
<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"/>
|
|
3838
|
+
</g>
|
|
3839
|
+
<defs>
|
|
3840
|
+
<clipPath id="clip0_6083_34804">
|
|
3841
|
+
<rect width="24" height="24" fill="white"/>
|
|
3842
|
+
</clipPath>
|
|
3843
|
+
</defs>`;
|
|
3844
|
+
firebaseText.innerText = 'Preview backend running in this workspace.';
|
|
3845
|
+
}
|
|
3846
|
+
firebaseText.setAttribute('id', firebaseTextId);
|
|
3847
|
+
}
|
|
3848
|
+
if (document.readyState === 'loading') {
|
|
3849
|
+
window.addEventListener('DOMContentLoaded', setupDom);
|
|
3850
|
+
}
|
|
3851
|
+
else {
|
|
3852
|
+
setupDom();
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3650
3855
|
|
|
3651
3856
|
/**
|
|
3652
3857
|
* @license
|
|
@@ -3764,8 +3969,7 @@ function validateIndexedDBOpenable() {
|
|
|
3764
3969
|
preExist = false;
|
|
3765
3970
|
};
|
|
3766
3971
|
request.onerror = () => {
|
|
3767
|
-
|
|
3768
|
-
reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');
|
|
3972
|
+
reject(request.error?.message || '');
|
|
3769
3973
|
};
|
|
3770
3974
|
}
|
|
3771
3975
|
catch (error) {
|
|
@@ -4867,10 +5071,9 @@ class Provider {
|
|
|
4867
5071
|
return this.instancesDeferred.get(normalizedIdentifier).promise;
|
|
4868
5072
|
}
|
|
4869
5073
|
getImmediate(options) {
|
|
4870
|
-
var _a;
|
|
4871
5074
|
// if multipleInstances is not supported, use the default name
|
|
4872
|
-
const normalizedIdentifier = this.normalizeInstanceIdentifier(options
|
|
4873
|
-
const optional =
|
|
5075
|
+
const normalizedIdentifier = this.normalizeInstanceIdentifier(options?.identifier);
|
|
5076
|
+
const optional = options?.optional ?? false;
|
|
4874
5077
|
if (this.isInitialized(normalizedIdentifier) ||
|
|
4875
5078
|
this.shouldAutoInitialize()) {
|
|
4876
5079
|
try {
|
|
@@ -5002,9 +5205,9 @@ class Provider {
|
|
|
5002
5205
|
* @returns a function to unregister the callback
|
|
5003
5206
|
*/
|
|
5004
5207
|
onInit(callback, identifier) {
|
|
5005
|
-
var _a;
|
|
5006
5208
|
const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);
|
|
5007
|
-
const existingCallbacks =
|
|
5209
|
+
const existingCallbacks = this.onInitCallbacks.get(normalizedIdentifier) ??
|
|
5210
|
+
new Set();
|
|
5008
5211
|
existingCallbacks.add(callback);
|
|
5009
5212
|
this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks);
|
|
5010
5213
|
const existingInstance = this.instances.get(normalizedIdentifier);
|
|
@@ -5028,7 +5231,7 @@ class Provider {
|
|
|
5028
5231
|
try {
|
|
5029
5232
|
callback(instance, identifier);
|
|
5030
5233
|
}
|
|
5031
|
-
catch
|
|
5234
|
+
catch {
|
|
5032
5235
|
// ignore errors in the onInit callback
|
|
5033
5236
|
}
|
|
5034
5237
|
}
|
|
@@ -5057,7 +5260,7 @@ class Provider {
|
|
|
5057
5260
|
try {
|
|
5058
5261
|
this.component.onInstanceCreated(this.container, instanceIdentifier, instance);
|
|
5059
5262
|
}
|
|
5060
|
-
catch
|
|
5263
|
+
catch {
|
|
5061
5264
|
// ignore errors in the onInstanceCreatedCallback
|
|
5062
5265
|
}
|
|
5063
5266
|
}
|
|
@@ -5624,11 +5827,11 @@ class PlatformLoggerServiceImpl {
|
|
|
5624
5827
|
*/
|
|
5625
5828
|
function isVersionServiceProvider(provider) {
|
|
5626
5829
|
const component = provider.getComponent();
|
|
5627
|
-
return
|
|
5830
|
+
return component?.type === "VERSION" /* ComponentType.VERSION */;
|
|
5628
5831
|
}
|
|
5629
5832
|
|
|
5630
5833
|
const name$q = "@firebase/app";
|
|
5631
|
-
const version$1$1 = "0.
|
|
5834
|
+
const version$1$1 = "0.14.5";
|
|
5632
5835
|
|
|
5633
5836
|
/**
|
|
5634
5837
|
* @license
|
|
@@ -5694,12 +5897,12 @@ const name$4$1 = "@firebase/storage-compat";
|
|
|
5694
5897
|
|
|
5695
5898
|
const name$3$1 = "@firebase/firestore";
|
|
5696
5899
|
|
|
5697
|
-
const name$2$1 = "@firebase/
|
|
5900
|
+
const name$2$1 = "@firebase/ai";
|
|
5698
5901
|
|
|
5699
5902
|
const name$1$1 = "@firebase/firestore-compat";
|
|
5700
5903
|
|
|
5701
5904
|
const name$r = "firebase";
|
|
5702
|
-
const version$5 = "
|
|
5905
|
+
const version$5 = "12.5.0";
|
|
5703
5906
|
|
|
5704
5907
|
/**
|
|
5705
5908
|
* @license
|
|
@@ -5848,6 +6051,9 @@ function _getProvider(app, name) {
|
|
|
5848
6051
|
* @internal
|
|
5849
6052
|
*/
|
|
5850
6053
|
function _isFirebaseServerApp(obj) {
|
|
6054
|
+
if (obj === null || obj === undefined) {
|
|
6055
|
+
return false;
|
|
6056
|
+
}
|
|
5851
6057
|
return obj.settings !== undefined;
|
|
5852
6058
|
}
|
|
5853
6059
|
|
|
@@ -5906,8 +6112,8 @@ const ERROR_FACTORY$2 = new ErrorFactory('app', 'Firebase', ERRORS$1);
|
|
|
5906
6112
|
class FirebaseAppImpl {
|
|
5907
6113
|
constructor(options, config, container) {
|
|
5908
6114
|
this._isDeleted = false;
|
|
5909
|
-
this._options =
|
|
5910
|
-
this._config =
|
|
6115
|
+
this._options = { ...options };
|
|
6116
|
+
this._config = { ...config };
|
|
5911
6117
|
this._name = config.name;
|
|
5912
6118
|
this._automaticDataCollectionEnabled =
|
|
5913
6119
|
config.automaticDataCollectionEnabled;
|
|
@@ -5982,7 +6188,11 @@ function initializeApp(_options, rawConfig = {}) {
|
|
|
5982
6188
|
const name = rawConfig;
|
|
5983
6189
|
rawConfig = { name };
|
|
5984
6190
|
}
|
|
5985
|
-
const config =
|
|
6191
|
+
const config = {
|
|
6192
|
+
name: DEFAULT_ENTRY_NAME,
|
|
6193
|
+
automaticDataCollectionEnabled: true,
|
|
6194
|
+
...rawConfig
|
|
6195
|
+
};
|
|
5986
6196
|
const name = config.name;
|
|
5987
6197
|
if (typeof name !== 'string' || !name) {
|
|
5988
6198
|
throw ERROR_FACTORY$2.create("bad-app-name" /* AppError.BAD_APP_NAME */, {
|
|
@@ -6060,10 +6270,9 @@ function getApp(name = DEFAULT_ENTRY_NAME) {
|
|
|
6060
6270
|
* @public
|
|
6061
6271
|
*/
|
|
6062
6272
|
function registerVersion(libraryKeyOrName, version, variant) {
|
|
6063
|
-
var _a;
|
|
6064
6273
|
// TODO: We can use this check to whitelist strings when/if we set up
|
|
6065
6274
|
// a good whitelist system.
|
|
6066
|
-
let library =
|
|
6275
|
+
let library = PLATFORM_LOG_STRING[libraryKeyOrName] ?? libraryKeyOrName;
|
|
6067
6276
|
if (variant) {
|
|
6068
6277
|
library += `-${variant}`;
|
|
6069
6278
|
}
|
|
@@ -6154,7 +6363,7 @@ async function readHeartbeatsFromIndexedDB(app) {
|
|
|
6154
6363
|
}
|
|
6155
6364
|
else {
|
|
6156
6365
|
const idbGetError = ERROR_FACTORY$2.create("idb-get" /* AppError.IDB_GET */, {
|
|
6157
|
-
originalErrorMessage: e
|
|
6366
|
+
originalErrorMessage: e?.message
|
|
6158
6367
|
});
|
|
6159
6368
|
logger$2.warn(idbGetError.message);
|
|
6160
6369
|
}
|
|
@@ -6174,7 +6383,7 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
|
|
|
6174
6383
|
}
|
|
6175
6384
|
else {
|
|
6176
6385
|
const idbGetError = ERROR_FACTORY$2.create("idb-set" /* AppError.IDB_WRITE */, {
|
|
6177
|
-
originalErrorMessage: e
|
|
6386
|
+
originalErrorMessage: e?.message
|
|
6178
6387
|
});
|
|
6179
6388
|
logger$2.warn(idbGetError.message);
|
|
6180
6389
|
}
|
|
@@ -6201,8 +6410,7 @@ function computeKey(app) {
|
|
|
6201
6410
|
* limitations under the License.
|
|
6202
6411
|
*/
|
|
6203
6412
|
const MAX_HEADER_BYTES = 1024;
|
|
6204
|
-
|
|
6205
|
-
const STORED_HEARTBEAT_RETENTION_MAX_MILLIS = 30 * 24 * 60 * 60 * 1000;
|
|
6413
|
+
const MAX_NUM_STORED_HEARTBEATS = 30;
|
|
6206
6414
|
class HeartbeatServiceImpl {
|
|
6207
6415
|
constructor(container) {
|
|
6208
6416
|
this.container = container;
|
|
@@ -6231,7 +6439,6 @@ class HeartbeatServiceImpl {
|
|
|
6231
6439
|
* already logged, subsequent calls to this function in the same day will be ignored.
|
|
6232
6440
|
*/
|
|
6233
6441
|
async triggerHeartbeat() {
|
|
6234
|
-
var _a, _b;
|
|
6235
6442
|
try {
|
|
6236
6443
|
const platformLogger = this.container
|
|
6237
6444
|
.getProvider('platform-logger')
|
|
@@ -6240,10 +6447,10 @@ class HeartbeatServiceImpl {
|
|
|
6240
6447
|
// service, not the browser user agent.
|
|
6241
6448
|
const agent = platformLogger.getPlatformInfoString();
|
|
6242
6449
|
const date = getUTCDateString();
|
|
6243
|
-
if (
|
|
6450
|
+
if (this._heartbeatsCache?.heartbeats == null) {
|
|
6244
6451
|
this._heartbeatsCache = await this._heartbeatsCachePromise;
|
|
6245
6452
|
// If we failed to construct a heartbeats cache, then return immediately.
|
|
6246
|
-
if (
|
|
6453
|
+
if (this._heartbeatsCache?.heartbeats == null) {
|
|
6247
6454
|
return;
|
|
6248
6455
|
}
|
|
6249
6456
|
}
|
|
@@ -6256,14 +6463,13 @@ class HeartbeatServiceImpl {
|
|
|
6256
6463
|
else {
|
|
6257
6464
|
// There is no entry for this date. Create one.
|
|
6258
6465
|
this._heartbeatsCache.heartbeats.push({ date, agent });
|
|
6466
|
+
// If the number of stored heartbeats exceeds the maximum number of stored heartbeats, remove the heartbeat with the earliest date.
|
|
6467
|
+
// 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.
|
|
6468
|
+
if (this._heartbeatsCache.heartbeats.length > MAX_NUM_STORED_HEARTBEATS) {
|
|
6469
|
+
const earliestHeartbeatIdx = getEarliestHeartbeatIdx(this._heartbeatsCache.heartbeats);
|
|
6470
|
+
this._heartbeatsCache.heartbeats.splice(earliestHeartbeatIdx, 1);
|
|
6471
|
+
}
|
|
6259
6472
|
}
|
|
6260
|
-
// Remove entries older than 30 days.
|
|
6261
|
-
this._heartbeatsCache.heartbeats =
|
|
6262
|
-
this._heartbeatsCache.heartbeats.filter(singleDateHeartbeat => {
|
|
6263
|
-
const hbTimestamp = new Date(singleDateHeartbeat.date).valueOf();
|
|
6264
|
-
const now = Date.now();
|
|
6265
|
-
return now - hbTimestamp <= STORED_HEARTBEAT_RETENTION_MAX_MILLIS;
|
|
6266
|
-
});
|
|
6267
6473
|
return this._storage.overwrite(this._heartbeatsCache);
|
|
6268
6474
|
}
|
|
6269
6475
|
catch (e) {
|
|
@@ -6278,13 +6484,12 @@ class HeartbeatServiceImpl {
|
|
|
6278
6484
|
* returns an empty string.
|
|
6279
6485
|
*/
|
|
6280
6486
|
async getHeartbeatsHeader() {
|
|
6281
|
-
var _a;
|
|
6282
6487
|
try {
|
|
6283
6488
|
if (this._heartbeatsCache === null) {
|
|
6284
6489
|
await this._heartbeatsCachePromise;
|
|
6285
6490
|
}
|
|
6286
6491
|
// If it's still null or the array is empty, there is no data to send.
|
|
6287
|
-
if (
|
|
6492
|
+
if (this._heartbeatsCache?.heartbeats == null ||
|
|
6288
6493
|
this._heartbeatsCache.heartbeats.length === 0) {
|
|
6289
6494
|
return '';
|
|
6290
6495
|
}
|
|
@@ -6385,7 +6590,7 @@ class HeartbeatStorageImpl {
|
|
|
6385
6590
|
}
|
|
6386
6591
|
else {
|
|
6387
6592
|
const idbHeartbeatObject = await readHeartbeatsFromIndexedDB(this.app);
|
|
6388
|
-
if (idbHeartbeatObject
|
|
6593
|
+
if (idbHeartbeatObject?.heartbeats) {
|
|
6389
6594
|
return idbHeartbeatObject;
|
|
6390
6595
|
}
|
|
6391
6596
|
else {
|
|
@@ -6395,7 +6600,6 @@ class HeartbeatStorageImpl {
|
|
|
6395
6600
|
}
|
|
6396
6601
|
// overwrite the storage with the provided heartbeats
|
|
6397
6602
|
async overwrite(heartbeatsObject) {
|
|
6398
|
-
var _a;
|
|
6399
6603
|
const canUseIndexedDB = await this._canUseIndexedDBPromise;
|
|
6400
6604
|
if (!canUseIndexedDB) {
|
|
6401
6605
|
return;
|
|
@@ -6403,14 +6607,14 @@ class HeartbeatStorageImpl {
|
|
|
6403
6607
|
else {
|
|
6404
6608
|
const existingHeartbeatsObject = await this.read();
|
|
6405
6609
|
return writeHeartbeatsToIndexedDB(this.app, {
|
|
6406
|
-
lastSentHeartbeatDate:
|
|
6610
|
+
lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
|
|
6611
|
+
existingHeartbeatsObject.lastSentHeartbeatDate,
|
|
6407
6612
|
heartbeats: heartbeatsObject.heartbeats
|
|
6408
6613
|
});
|
|
6409
6614
|
}
|
|
6410
6615
|
}
|
|
6411
6616
|
// add heartbeats
|
|
6412
6617
|
async add(heartbeatsObject) {
|
|
6413
|
-
var _a;
|
|
6414
6618
|
const canUseIndexedDB = await this._canUseIndexedDBPromise;
|
|
6415
6619
|
if (!canUseIndexedDB) {
|
|
6416
6620
|
return;
|
|
@@ -6418,7 +6622,8 @@ class HeartbeatStorageImpl {
|
|
|
6418
6622
|
else {
|
|
6419
6623
|
const existingHeartbeatsObject = await this.read();
|
|
6420
6624
|
return writeHeartbeatsToIndexedDB(this.app, {
|
|
6421
|
-
lastSentHeartbeatDate:
|
|
6625
|
+
lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ??
|
|
6626
|
+
existingHeartbeatsObject.lastSentHeartbeatDate,
|
|
6422
6627
|
heartbeats: [
|
|
6423
6628
|
...existingHeartbeatsObject.heartbeats,
|
|
6424
6629
|
...heartbeatsObject.heartbeats
|
|
@@ -6438,6 +6643,24 @@ function countBytes(heartbeatsCache) {
|
|
|
6438
6643
|
// heartbeatsCache wrapper properties
|
|
6439
6644
|
JSON.stringify({ version: 2, heartbeats: heartbeatsCache })).length;
|
|
6440
6645
|
}
|
|
6646
|
+
/**
|
|
6647
|
+
* Returns the index of the heartbeat with the earliest date.
|
|
6648
|
+
* If the heartbeats array is empty, -1 is returned.
|
|
6649
|
+
*/
|
|
6650
|
+
function getEarliestHeartbeatIdx(heartbeats) {
|
|
6651
|
+
if (heartbeats.length === 0) {
|
|
6652
|
+
return -1;
|
|
6653
|
+
}
|
|
6654
|
+
let earliestHeartbeatIdx = 0;
|
|
6655
|
+
let earliestHeartbeatDate = heartbeats[0].date;
|
|
6656
|
+
for (let i = 1; i < heartbeats.length; i++) {
|
|
6657
|
+
if (heartbeats[i].date < earliestHeartbeatDate) {
|
|
6658
|
+
earliestHeartbeatDate = heartbeats[i].date;
|
|
6659
|
+
earliestHeartbeatIdx = i;
|
|
6660
|
+
}
|
|
6661
|
+
}
|
|
6662
|
+
return earliestHeartbeatIdx;
|
|
6663
|
+
}
|
|
6441
6664
|
|
|
6442
6665
|
/**
|
|
6443
6666
|
* @license
|
|
@@ -6460,8 +6683,8 @@ function registerCoreComponents(variant) {
|
|
|
6460
6683
|
_registerComponent(new Component('heartbeat', container => new HeartbeatServiceImpl(container), "PRIVATE" /* ComponentType.PRIVATE */));
|
|
6461
6684
|
// Register `app` package.
|
|
6462
6685
|
registerVersion(name$q, version$1$1, variant);
|
|
6463
|
-
// BUILD_TARGET will be replaced by values like
|
|
6464
|
-
registerVersion(name$q, version$1$1, '
|
|
6686
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
6687
|
+
registerVersion(name$q, version$1$1, 'esm2020');
|
|
6465
6688
|
// Register platform SDK identifier (no version).
|
|
6466
6689
|
registerVersion('fire-js', '');
|
|
6467
6690
|
}
|
|
@@ -6475,7 +6698,7 @@ function registerCoreComponents(variant) {
|
|
|
6475
6698
|
registerCoreComponents('');
|
|
6476
6699
|
|
|
6477
6700
|
var name$4 = "firebase";
|
|
6478
|
-
var version$4 = "
|
|
6701
|
+
var version$4 = "12.5.0";
|
|
6479
6702
|
|
|
6480
6703
|
/**
|
|
6481
6704
|
* @license
|
|
@@ -6496,7 +6719,7 @@ var version$4 = "11.0.2";
|
|
|
6496
6719
|
registerVersion(name$4, version$4, 'app');
|
|
6497
6720
|
|
|
6498
6721
|
const name$3 = "@firebase/installations";
|
|
6499
|
-
const version$3 = "0.6.
|
|
6722
|
+
const version$3 = "0.6.19";
|
|
6500
6723
|
|
|
6501
6724
|
/**
|
|
6502
6725
|
* @license
|
|
@@ -6757,7 +6980,7 @@ function generateFid() {
|
|
|
6757
6980
|
const fid = encode(fidByteArray);
|
|
6758
6981
|
return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;
|
|
6759
6982
|
}
|
|
6760
|
-
catch
|
|
6983
|
+
catch {
|
|
6761
6984
|
// FID generation errored
|
|
6762
6985
|
return INVALID_FID;
|
|
6763
6986
|
}
|
|
@@ -7244,7 +7467,10 @@ function updateAuthTokenRequest(appConfig) {
|
|
|
7244
7467
|
}
|
|
7245
7468
|
const oldAuthToken = oldEntry.authToken;
|
|
7246
7469
|
if (hasAuthTokenRequestTimedOut(oldAuthToken)) {
|
|
7247
|
-
return
|
|
7470
|
+
return {
|
|
7471
|
+
...oldEntry,
|
|
7472
|
+
authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
|
|
7473
|
+
};
|
|
7248
7474
|
}
|
|
7249
7475
|
return oldEntry;
|
|
7250
7476
|
});
|
|
@@ -7252,7 +7478,10 @@ function updateAuthTokenRequest(appConfig) {
|
|
|
7252
7478
|
async function fetchAuthTokenFromServer(installations, installationEntry) {
|
|
7253
7479
|
try {
|
|
7254
7480
|
const authToken = await generateAuthTokenRequest(installations, installationEntry);
|
|
7255
|
-
const updatedInstallationEntry =
|
|
7481
|
+
const updatedInstallationEntry = {
|
|
7482
|
+
...installationEntry,
|
|
7483
|
+
authToken
|
|
7484
|
+
};
|
|
7256
7485
|
await set$1(installations.appConfig, updatedInstallationEntry);
|
|
7257
7486
|
return authToken;
|
|
7258
7487
|
}
|
|
@@ -7264,7 +7493,10 @@ async function fetchAuthTokenFromServer(installations, installationEntry) {
|
|
|
7264
7493
|
await remove$1(installations.appConfig);
|
|
7265
7494
|
}
|
|
7266
7495
|
else {
|
|
7267
|
-
const updatedInstallationEntry =
|
|
7496
|
+
const updatedInstallationEntry = {
|
|
7497
|
+
...installationEntry,
|
|
7498
|
+
authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ }
|
|
7499
|
+
};
|
|
7268
7500
|
await set$1(installations.appConfig, updatedInstallationEntry);
|
|
7269
7501
|
}
|
|
7270
7502
|
throw e;
|
|
@@ -7289,7 +7521,10 @@ function makeAuthTokenRequestInProgressEntry(oldEntry) {
|
|
|
7289
7521
|
requestStatus: 1 /* RequestStatus.IN_PROGRESS */,
|
|
7290
7522
|
requestTime: Date.now()
|
|
7291
7523
|
};
|
|
7292
|
-
return
|
|
7524
|
+
return {
|
|
7525
|
+
...oldEntry,
|
|
7526
|
+
authToken: inProgressAuthToken
|
|
7527
|
+
};
|
|
7293
7528
|
}
|
|
7294
7529
|
function hasAuthTokenRequestTimedOut(authToken) {
|
|
7295
7530
|
return (authToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */ &&
|
|
@@ -7474,8 +7709,8 @@ function registerInstallations() {
|
|
|
7474
7709
|
*/
|
|
7475
7710
|
registerInstallations();
|
|
7476
7711
|
registerVersion(name$3, version$3);
|
|
7477
|
-
// BUILD_TARGET will be replaced by values like
|
|
7478
|
-
registerVersion(name$3, version$3, '
|
|
7712
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
7713
|
+
registerVersion(name$3, version$3, 'esm2020');
|
|
7479
7714
|
|
|
7480
7715
|
/**
|
|
7481
7716
|
* @license
|
|
@@ -7635,7 +7870,7 @@ function insertScriptTag(dataLayerName, measurementId) {
|
|
|
7635
7870
|
// without fid. We will initialize ga-id using gtag (config) command together with fid.
|
|
7636
7871
|
const gtagScriptURL = `${GTAG_URL}?l=${dataLayerName}&id=${measurementId}`;
|
|
7637
7872
|
script.src = trustedTypesPolicy
|
|
7638
|
-
? trustedTypesPolicy
|
|
7873
|
+
? trustedTypesPolicy?.createScriptURL(gtagScriptURL)
|
|
7639
7874
|
: gtagScriptURL;
|
|
7640
7875
|
script.async = true;
|
|
7641
7876
|
document.head.appendChild(script);
|
|
@@ -7921,7 +8156,6 @@ function getHeaders(apiKey) {
|
|
|
7921
8156
|
* @param app Firebase app to fetch config for.
|
|
7922
8157
|
*/
|
|
7923
8158
|
async function fetchDynamicConfig(appFields) {
|
|
7924
|
-
var _a;
|
|
7925
8159
|
const { appId, apiKey } = appFields;
|
|
7926
8160
|
const request = {
|
|
7927
8161
|
method: 'GET',
|
|
@@ -7934,7 +8168,7 @@ async function fetchDynamicConfig(appFields) {
|
|
|
7934
8168
|
try {
|
|
7935
8169
|
// Try to get any error message text from server response.
|
|
7936
8170
|
const jsonResponse = (await response.json());
|
|
7937
|
-
if (
|
|
8171
|
+
if (jsonResponse.error?.message) {
|
|
7938
8172
|
errorMessage = jsonResponse.error.message;
|
|
7939
8173
|
}
|
|
7940
8174
|
}
|
|
@@ -7985,7 +8219,6 @@ retryData = defaultRetryData, timeoutMillis) {
|
|
|
7985
8219
|
*/
|
|
7986
8220
|
async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMillis, backoffCount }, signal, retryData = defaultRetryData // for testing
|
|
7987
8221
|
) {
|
|
7988
|
-
var _a;
|
|
7989
8222
|
const { appId, measurementId } = appFields;
|
|
7990
8223
|
// Starts with a (potentially zero) timeout to support resumption from stored state.
|
|
7991
8224
|
// Ensures the throttle end time is honored if the last attempt timed out.
|
|
@@ -7997,7 +8230,7 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
|
|
|
7997
8230
|
if (measurementId) {
|
|
7998
8231
|
logger$1.warn(`Timed out fetching this Firebase app's measurement ID from the server.` +
|
|
7999
8232
|
` Falling back to the measurement ID ${measurementId}` +
|
|
8000
|
-
` provided in the "measurementId" field in the local Firebase config. [${e
|
|
8233
|
+
` provided in the "measurementId" field in the local Firebase config. [${e?.message}]`);
|
|
8001
8234
|
return { appId, measurementId };
|
|
8002
8235
|
}
|
|
8003
8236
|
throw e;
|
|
@@ -8015,14 +8248,14 @@ async function attemptFetchDynamicConfigWithRetry(appFields, { throttleEndTimeMi
|
|
|
8015
8248
|
if (measurementId) {
|
|
8016
8249
|
logger$1.warn(`Failed to fetch this Firebase app's measurement ID from the server.` +
|
|
8017
8250
|
` Falling back to the measurement ID ${measurementId}` +
|
|
8018
|
-
` provided in the "measurementId" field in the local Firebase config. [${error
|
|
8251
|
+
` provided in the "measurementId" field in the local Firebase config. [${error?.message}]`);
|
|
8019
8252
|
return { appId, measurementId };
|
|
8020
8253
|
}
|
|
8021
8254
|
else {
|
|
8022
8255
|
throw e;
|
|
8023
8256
|
}
|
|
8024
8257
|
}
|
|
8025
|
-
const backoffMillis = Number(
|
|
8258
|
+
const backoffMillis = Number(error?.customData?.httpStatus) === 503
|
|
8026
8259
|
? calculateBackoffMillis(backoffCount, retryData.intervalMillis, LONG_RETRY_FACTOR)
|
|
8027
8260
|
: calculateBackoffMillis(backoffCount, retryData.intervalMillis);
|
|
8028
8261
|
// Increments backoff state.
|
|
@@ -8110,10 +8343,37 @@ async function logEvent$1(gtagFunction, initializationPromise, eventName, eventP
|
|
|
8110
8343
|
}
|
|
8111
8344
|
else {
|
|
8112
8345
|
const measurementId = await initializationPromise;
|
|
8113
|
-
const params =
|
|
8346
|
+
const params = {
|
|
8347
|
+
...eventParams,
|
|
8348
|
+
'send_to': measurementId
|
|
8349
|
+
};
|
|
8114
8350
|
gtagFunction("event" /* GtagCommand.EVENT */, eventName, params);
|
|
8115
8351
|
}
|
|
8116
8352
|
}
|
|
8353
|
+
/**
|
|
8354
|
+
* Set all other user properties other than user_id and screen_name.
|
|
8355
|
+
*
|
|
8356
|
+
* @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
|
|
8357
|
+
* @param properties Map of user properties to set
|
|
8358
|
+
*/
|
|
8359
|
+
async function setUserProperties$1(gtagFunction, initializationPromise, properties, options) {
|
|
8360
|
+
if (options && options.global) {
|
|
8361
|
+
const flatProperties = {};
|
|
8362
|
+
for (const key of Object.keys(properties)) {
|
|
8363
|
+
// use dot notation for merge behavior in gtag.js
|
|
8364
|
+
flatProperties[`user_properties.${key}`] = properties[key];
|
|
8365
|
+
}
|
|
8366
|
+
gtagFunction("set" /* GtagCommand.SET */, flatProperties);
|
|
8367
|
+
return Promise.resolve();
|
|
8368
|
+
}
|
|
8369
|
+
else {
|
|
8370
|
+
const measurementId = await initializationPromise;
|
|
8371
|
+
gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
|
|
8372
|
+
update: true,
|
|
8373
|
+
'user_properties': properties
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8376
|
+
}
|
|
8117
8377
|
|
|
8118
8378
|
/**
|
|
8119
8379
|
* @license
|
|
@@ -8144,7 +8404,7 @@ async function validateIndexedDB() {
|
|
|
8144
8404
|
}
|
|
8145
8405
|
catch (e) {
|
|
8146
8406
|
logger$1.warn(ERROR_FACTORY.create("indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */, {
|
|
8147
|
-
errorInfo: e
|
|
8407
|
+
errorInfo: e?.toString()
|
|
8148
8408
|
}).message);
|
|
8149
8409
|
return false;
|
|
8150
8410
|
}
|
|
@@ -8165,7 +8425,6 @@ async function validateIndexedDB() {
|
|
|
8165
8425
|
* @returns Measurement ID.
|
|
8166
8426
|
*/
|
|
8167
8427
|
async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementIdToAppId, installations, gtagCore, dataLayerName, options) {
|
|
8168
|
-
var _a;
|
|
8169
8428
|
const dynamicConfigPromise = fetchDynamicConfigWithRetry(app);
|
|
8170
8429
|
// Once fetched, map measurementIds to appId, for ease of lookup in wrapped gtag function.
|
|
8171
8430
|
dynamicConfigPromise
|
|
@@ -8207,7 +8466,7 @@ async function _initializeAnalytics(app, dynamicConfigPromisesList, measurementI
|
|
|
8207
8466
|
gtagCore('js', new Date());
|
|
8208
8467
|
// User config added first. We don't want users to accidentally overwrite
|
|
8209
8468
|
// base Firebase config properties.
|
|
8210
|
-
const configProperties =
|
|
8469
|
+
const configProperties = options?.config ?? {};
|
|
8211
8470
|
// guard against developers accidentally setting properties with prefix `firebase_`
|
|
8212
8471
|
configProperties[ORIGIN_KEY] = 'firebase';
|
|
8213
8472
|
configProperties.update = true;
|
|
@@ -8395,6 +8654,15 @@ function initializeAnalytics(app, options = {}) {
|
|
|
8395
8654
|
const analyticsInstance = analyticsProvider.initialize({ options });
|
|
8396
8655
|
return analyticsInstance;
|
|
8397
8656
|
}
|
|
8657
|
+
/**
|
|
8658
|
+
* Use gtag `config` command to set all params specified.
|
|
8659
|
+
*
|
|
8660
|
+
* @public
|
|
8661
|
+
*/
|
|
8662
|
+
function setUserProperties(analyticsInstance, properties, options) {
|
|
8663
|
+
analyticsInstance = getModularInstance(analyticsInstance);
|
|
8664
|
+
setUserProperties$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], properties, options).catch(e => logger$1.error(e));
|
|
8665
|
+
}
|
|
8398
8666
|
/**
|
|
8399
8667
|
* Sends a Google Analytics event with given `eventParams`. This method
|
|
8400
8668
|
* automatically associates this logged event with this Firebase web
|
|
@@ -8412,7 +8680,7 @@ function logEvent$2(analyticsInstance, eventName, eventParams, options) {
|
|
|
8412
8680
|
}
|
|
8413
8681
|
|
|
8414
8682
|
const name$2 = "@firebase/analytics";
|
|
8415
|
-
const version$2 = "0.10.
|
|
8683
|
+
const version$2 = "0.10.19";
|
|
8416
8684
|
|
|
8417
8685
|
/**
|
|
8418
8686
|
* The Firebase Analytics Web SDK.
|
|
@@ -8431,13 +8699,14 @@ function registerAnalytics() {
|
|
|
8431
8699
|
}, "PUBLIC" /* ComponentType.PUBLIC */));
|
|
8432
8700
|
_registerComponent(new Component('analytics-internal', internalFactory, "PRIVATE" /* ComponentType.PRIVATE */));
|
|
8433
8701
|
registerVersion(name$2, version$2);
|
|
8434
|
-
// BUILD_TARGET will be replaced by values like
|
|
8435
|
-
registerVersion(name$2, version$2, '
|
|
8702
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
8703
|
+
registerVersion(name$2, version$2, 'esm2020');
|
|
8436
8704
|
function internalFactory(container) {
|
|
8437
8705
|
try {
|
|
8438
8706
|
const analytics = container.getProvider(ANALYTICS_TYPE).getImmediate();
|
|
8439
8707
|
return {
|
|
8440
|
-
logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options)
|
|
8708
|
+
logEvent: (eventName, eventParams, options) => logEvent$2(analytics, eventName, eventParams, options),
|
|
8709
|
+
setUserProperties: (properties, options) => setUserProperties(analytics, properties, options)
|
|
8441
8710
|
};
|
|
8442
8711
|
}
|
|
8443
8712
|
catch (e) {
|
|
@@ -8449,40 +8718,6 @@ function registerAnalytics() {
|
|
|
8449
8718
|
}
|
|
8450
8719
|
registerAnalytics();
|
|
8451
8720
|
|
|
8452
|
-
/******************************************************************************
|
|
8453
|
-
Copyright (c) Microsoft Corporation.
|
|
8454
|
-
|
|
8455
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
8456
|
-
purpose with or without fee is hereby granted.
|
|
8457
|
-
|
|
8458
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8459
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
8460
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
8461
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
8462
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
8463
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
8464
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
8465
|
-
***************************************************************************** */
|
|
8466
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
function __rest(s, e) {
|
|
8470
|
-
var t = {};
|
|
8471
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
8472
|
-
t[p] = s[p];
|
|
8473
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
8474
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8475
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8476
|
-
t[p[i]] = s[p[i]];
|
|
8477
|
-
}
|
|
8478
|
-
return t;
|
|
8479
|
-
}
|
|
8480
|
-
|
|
8481
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
8482
|
-
var e = new Error(message);
|
|
8483
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
8484
|
-
};
|
|
8485
|
-
|
|
8486
8721
|
function _prodErrorMap() {
|
|
8487
8722
|
// We will include this one message in the prod error map since by the very
|
|
8488
8723
|
// nature of this error, developers will never be able to see the message
|
|
@@ -8554,7 +8789,10 @@ function _createError(authOrCode, ...rest) {
|
|
|
8554
8789
|
return createErrorInternal(authOrCode, ...rest);
|
|
8555
8790
|
}
|
|
8556
8791
|
function _errorWithCustomMessage(auth, code, message) {
|
|
8557
|
-
const errorMap =
|
|
8792
|
+
const errorMap = {
|
|
8793
|
+
...prodErrorMap(),
|
|
8794
|
+
[code]: message
|
|
8795
|
+
};
|
|
8558
8796
|
const factory = new ErrorFactory('auth', 'Firebase', errorMap);
|
|
8559
8797
|
return factory.create(code, {
|
|
8560
8798
|
appName: auth.name
|
|
@@ -8625,15 +8863,13 @@ function debugAssert(assertion, message) {
|
|
|
8625
8863
|
* limitations under the License.
|
|
8626
8864
|
*/
|
|
8627
8865
|
function _getCurrentUrl() {
|
|
8628
|
-
|
|
8629
|
-
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.href)) || '';
|
|
8866
|
+
return (typeof self !== 'undefined' && self.location?.href) || '';
|
|
8630
8867
|
}
|
|
8631
8868
|
function _isHttpOrHttps() {
|
|
8632
8869
|
return _getCurrentScheme() === 'http:' || _getCurrentScheme() === 'https:';
|
|
8633
8870
|
}
|
|
8634
8871
|
function _getCurrentScheme() {
|
|
8635
|
-
|
|
8636
|
-
return (typeof self !== 'undefined' && ((_a = self.location) === null || _a === void 0 ? void 0 : _a.protocol)) || null;
|
|
8872
|
+
return (typeof self !== 'undefined' && self.location?.protocol) || null;
|
|
8637
8873
|
}
|
|
8638
8874
|
|
|
8639
8875
|
/**
|
|
@@ -8936,10 +9172,21 @@ const SERVER_ERROR_MAP = {
|
|
|
8936
9172
|
* See the License for the specific language governing permissions and
|
|
8937
9173
|
* limitations under the License.
|
|
8938
9174
|
*/
|
|
9175
|
+
const CookieAuthProxiedEndpoints = [
|
|
9176
|
+
"/v1/accounts:signInWithCustomToken" /* Endpoint.SIGN_IN_WITH_CUSTOM_TOKEN */,
|
|
9177
|
+
"/v1/accounts:signInWithEmailLink" /* Endpoint.SIGN_IN_WITH_EMAIL_LINK */,
|
|
9178
|
+
"/v1/accounts:signInWithIdp" /* Endpoint.SIGN_IN_WITH_IDP */,
|
|
9179
|
+
"/v1/accounts:signInWithPassword" /* Endpoint.SIGN_IN_WITH_PASSWORD */,
|
|
9180
|
+
"/v1/accounts:signInWithPhoneNumber" /* Endpoint.SIGN_IN_WITH_PHONE_NUMBER */,
|
|
9181
|
+
"/v1/token" /* Endpoint.TOKEN */
|
|
9182
|
+
];
|
|
8939
9183
|
const DEFAULT_API_TIMEOUT_MS = new Delay(30000, 60000);
|
|
8940
9184
|
function _addTidIfNecessary(auth, request) {
|
|
8941
9185
|
if (auth.tenantId && !request.tenantId) {
|
|
8942
|
-
return
|
|
9186
|
+
return {
|
|
9187
|
+
...request,
|
|
9188
|
+
tenantId: auth.tenantId
|
|
9189
|
+
};
|
|
8943
9190
|
}
|
|
8944
9191
|
return request;
|
|
8945
9192
|
}
|
|
@@ -8957,14 +9204,20 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
|
|
|
8957
9204
|
};
|
|
8958
9205
|
}
|
|
8959
9206
|
}
|
|
8960
|
-
const query = querystring(
|
|
9207
|
+
const query = querystring({
|
|
9208
|
+
key: auth.config.apiKey,
|
|
9209
|
+
...params
|
|
9210
|
+
}).slice(1);
|
|
8961
9211
|
const headers = await auth._getAdditionalHeaders();
|
|
8962
9212
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/json';
|
|
8963
9213
|
if (auth.languageCode) {
|
|
8964
9214
|
headers["X-Firebase-Locale" /* HttpHeader.X_FIREBASE_LOCALE */] = auth.languageCode;
|
|
8965
9215
|
}
|
|
8966
|
-
const fetchArgs =
|
|
8967
|
-
|
|
9216
|
+
const fetchArgs = {
|
|
9217
|
+
method,
|
|
9218
|
+
headers,
|
|
9219
|
+
...body
|
|
9220
|
+
};
|
|
8968
9221
|
/* Security-conscious server-side frameworks tend to have built in mitigations for referrer
|
|
8969
9222
|
problems". See the Cloudflare GitHub issue #487: Error: The 'referrerPolicy' field on
|
|
8970
9223
|
'RequestInitializerDict' is not implemented."
|
|
@@ -8972,12 +9225,15 @@ async function _performApiRequest(auth, method, path, request, customErrorMap =
|
|
|
8972
9225
|
if (!isCloudflareWorker()) {
|
|
8973
9226
|
fetchArgs.referrerPolicy = 'no-referrer';
|
|
8974
9227
|
}
|
|
8975
|
-
|
|
9228
|
+
if (auth.emulatorConfig && isCloudWorkstation(auth.emulatorConfig.host)) {
|
|
9229
|
+
fetchArgs.credentials = 'include';
|
|
9230
|
+
}
|
|
9231
|
+
return FetchProvider.fetch()(await _getFinalTarget(auth, auth.config.apiHost, path, query), fetchArgs);
|
|
8976
9232
|
});
|
|
8977
9233
|
}
|
|
8978
9234
|
async function _performFetchWithErrorHandling(auth, customErrorMap, fetchFn) {
|
|
8979
9235
|
auth._canInitEmulator = false;
|
|
8980
|
-
const errorMap =
|
|
9236
|
+
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };
|
|
8981
9237
|
try {
|
|
8982
9238
|
const networkTimeout = new NetworkTimeout(auth);
|
|
8983
9239
|
const response = await Promise.race([
|
|
@@ -9037,12 +9293,25 @@ async function _performSignInRequest(auth, method, path, request, customErrorMap
|
|
|
9037
9293
|
}
|
|
9038
9294
|
return serverResponse;
|
|
9039
9295
|
}
|
|
9040
|
-
function _getFinalTarget(auth, host, path, query) {
|
|
9296
|
+
async function _getFinalTarget(auth, host, path, query) {
|
|
9041
9297
|
const base = `${host}${path}?${query}`;
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9298
|
+
const authInternal = auth;
|
|
9299
|
+
const finalTarget = authInternal.config.emulator
|
|
9300
|
+
? _emulatorUrl(auth.config, base)
|
|
9301
|
+
: `${auth.config.apiScheme}://${base}`;
|
|
9302
|
+
// Cookie auth works by MiTMing the signIn and token endpoints from the developer's backend,
|
|
9303
|
+
// saving the idToken and refreshToken into cookies, and then redacting the refreshToken
|
|
9304
|
+
// from the response
|
|
9305
|
+
if (CookieAuthProxiedEndpoints.includes(path)) {
|
|
9306
|
+
// Persistence manager is async, we need to await it. We can't just wait for auth initialized
|
|
9307
|
+
// here since auth initialization calls this function.
|
|
9308
|
+
await authInternal._persistenceManagerAvailable;
|
|
9309
|
+
if (authInternal._getPersistenceType() === "COOKIE" /* PersistenceType.COOKIE */) {
|
|
9310
|
+
const cookiePersistence = authInternal._getPersistence();
|
|
9311
|
+
return cookiePersistence._getFinalTarget(finalTarget).toString();
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
return finalTarget;
|
|
9046
9315
|
}
|
|
9047
9316
|
class NetworkTimeout {
|
|
9048
9317
|
clearNetworkTimeout() {
|
|
@@ -9152,7 +9421,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
|
|
|
9152
9421
|
const claims = _parseToken(token);
|
|
9153
9422
|
_assert(claims && claims.exp && claims.auth_time && claims.iat, userInternal.auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9154
9423
|
const firebase = typeof claims.firebase === 'object' ? claims.firebase : undefined;
|
|
9155
|
-
const signInProvider = firebase
|
|
9424
|
+
const signInProvider = firebase?.['sign_in_provider'];
|
|
9156
9425
|
return {
|
|
9157
9426
|
claims,
|
|
9158
9427
|
token,
|
|
@@ -9160,7 +9429,7 @@ async function getIdTokenResult(user, forceRefresh = false) {
|
|
|
9160
9429
|
issuedAtTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.iat)),
|
|
9161
9430
|
expirationTime: utcTimestampToDateString(secondsStringToMilliseconds(claims.exp)),
|
|
9162
9431
|
signInProvider: signInProvider || null,
|
|
9163
|
-
signInSecondFactor:
|
|
9432
|
+
signInSecondFactor: firebase?.['sign_in_second_factor'] || null
|
|
9164
9433
|
};
|
|
9165
9434
|
}
|
|
9166
9435
|
function secondsStringToMilliseconds(seconds) {
|
|
@@ -9183,7 +9452,7 @@ function _parseToken(token) {
|
|
|
9183
9452
|
return JSON.parse(decoded);
|
|
9184
9453
|
}
|
|
9185
9454
|
catch (e) {
|
|
9186
|
-
_logError('Caught error parsing JWT payload as JSON', e
|
|
9455
|
+
_logError('Caught error parsing JWT payload as JSON', e?.toString());
|
|
9187
9456
|
return null;
|
|
9188
9457
|
}
|
|
9189
9458
|
}
|
|
@@ -9279,7 +9548,6 @@ class ProactiveRefresh {
|
|
|
9279
9548
|
}
|
|
9280
9549
|
}
|
|
9281
9550
|
getInterval(wasError) {
|
|
9282
|
-
var _a;
|
|
9283
9551
|
if (wasError) {
|
|
9284
9552
|
const interval = this.errorBackoff;
|
|
9285
9553
|
this.errorBackoff = Math.min(this.errorBackoff * 2, 960000 /* Duration.RETRY_BACKOFF_MAX */);
|
|
@@ -9288,7 +9556,7 @@ class ProactiveRefresh {
|
|
|
9288
9556
|
else {
|
|
9289
9557
|
// Reset the error backoff
|
|
9290
9558
|
this.errorBackoff = 30000 /* Duration.RETRY_BACKOFF_MIN */;
|
|
9291
|
-
const expTime =
|
|
9559
|
+
const expTime = this.user.stsTokenManager.expirationTime ?? 0;
|
|
9292
9560
|
const interval = expTime - Date.now() - 300000 /* Duration.OFFSET */;
|
|
9293
9561
|
return Math.max(0, interval);
|
|
9294
9562
|
}
|
|
@@ -9309,7 +9577,7 @@ class ProactiveRefresh {
|
|
|
9309
9577
|
}
|
|
9310
9578
|
catch (e) {
|
|
9311
9579
|
// Only retry on network errors
|
|
9312
|
-
if (
|
|
9580
|
+
if (e?.code ===
|
|
9313
9581
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
9314
9582
|
this.schedule(/* wasError */ true);
|
|
9315
9583
|
}
|
|
@@ -9375,14 +9643,13 @@ class UserMetadata {
|
|
|
9375
9643
|
* limitations under the License.
|
|
9376
9644
|
*/
|
|
9377
9645
|
async function _reloadWithoutSaving(user) {
|
|
9378
|
-
var _a;
|
|
9379
9646
|
const auth = user.auth;
|
|
9380
9647
|
const idToken = await user.getIdToken();
|
|
9381
9648
|
const response = await _logoutIfInvalidated(user, getAccountInfo(auth, { idToken }));
|
|
9382
|
-
_assert(response
|
|
9649
|
+
_assert(response?.users.length, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9383
9650
|
const coreAccount = response.users[0];
|
|
9384
9651
|
user._notifyReloadListener(coreAccount);
|
|
9385
|
-
const newProviderData =
|
|
9652
|
+
const newProviderData = coreAccount.providerUserInfo?.length
|
|
9386
9653
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
9387
9654
|
: [];
|
|
9388
9655
|
const providerData = mergeProviderData(user.providerData, newProviderData);
|
|
@@ -9392,7 +9659,7 @@ async function _reloadWithoutSaving(user) {
|
|
|
9392
9659
|
// On the other hand, if it was not anonymous before, it should never be
|
|
9393
9660
|
// considered anonymous now.
|
|
9394
9661
|
const oldIsAnonymous = user.isAnonymous;
|
|
9395
|
-
const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !
|
|
9662
|
+
const newIsAnonymous = !(user.email && coreAccount.passwordHash) && !providerData?.length;
|
|
9396
9663
|
const isAnonymous = !oldIsAnonymous ? false : newIsAnonymous;
|
|
9397
9664
|
const updates = {
|
|
9398
9665
|
uid: coreAccount.localId,
|
|
@@ -9429,8 +9696,7 @@ function mergeProviderData(original, newData) {
|
|
|
9429
9696
|
return [...deduped, ...newData];
|
|
9430
9697
|
}
|
|
9431
9698
|
function extractProviderData(providers) {
|
|
9432
|
-
return providers.map((
|
|
9433
|
-
var { providerId } = _a, provider = __rest(_a, ["providerId"]);
|
|
9699
|
+
return providers.map(({ providerId, ...provider }) => {
|
|
9434
9700
|
return {
|
|
9435
9701
|
providerId,
|
|
9436
9702
|
uid: provider.rawId || '',
|
|
@@ -9465,14 +9731,19 @@ async function requestStsToken(auth, refreshToken) {
|
|
|
9465
9731
|
'refresh_token': refreshToken
|
|
9466
9732
|
}).slice(1);
|
|
9467
9733
|
const { tokenApiHost, apiKey } = auth.config;
|
|
9468
|
-
const url = _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
|
|
9734
|
+
const url = await _getFinalTarget(auth, tokenApiHost, "/v1/token" /* Endpoint.TOKEN */, `key=${apiKey}`);
|
|
9469
9735
|
const headers = await auth._getAdditionalHeaders();
|
|
9470
9736
|
headers["Content-Type" /* HttpHeader.CONTENT_TYPE */] = 'application/x-www-form-urlencoded';
|
|
9471
|
-
|
|
9737
|
+
const options = {
|
|
9472
9738
|
method: "POST" /* HttpMethod.POST */,
|
|
9473
9739
|
headers,
|
|
9474
9740
|
body
|
|
9475
|
-
}
|
|
9741
|
+
};
|
|
9742
|
+
if (auth.emulatorConfig &&
|
|
9743
|
+
isCloudWorkstation(auth.emulatorConfig.host)) {
|
|
9744
|
+
options.credentials = 'include';
|
|
9745
|
+
}
|
|
9746
|
+
return FetchProvider.fetch()(url, options);
|
|
9476
9747
|
});
|
|
9477
9748
|
// The response comes back in snake_case. Convert to camel:
|
|
9478
9749
|
return {
|
|
@@ -9617,8 +9888,7 @@ function assertStringOrUndefined(assertion, appName) {
|
|
|
9617
9888
|
_assert(typeof assertion === 'string' || typeof assertion === 'undefined', "internal-error" /* AuthErrorCode.INTERNAL_ERROR */, { appName });
|
|
9618
9889
|
}
|
|
9619
9890
|
class UserImpl {
|
|
9620
|
-
constructor(
|
|
9621
|
-
var { uid, auth, stsTokenManager } = _a, opt = __rest(_a, ["uid", "auth", "stsTokenManager"]);
|
|
9891
|
+
constructor({ uid, auth, stsTokenManager, ...opt }) {
|
|
9622
9892
|
// For the user object, provider is always Firebase.
|
|
9623
9893
|
this.providerId = "firebase" /* ProviderId.FIREBASE */;
|
|
9624
9894
|
this.proactiveRefresh = new ProactiveRefresh(this);
|
|
@@ -9666,12 +9936,16 @@ class UserImpl {
|
|
|
9666
9936
|
this.phoneNumber = user.phoneNumber;
|
|
9667
9937
|
this.isAnonymous = user.isAnonymous;
|
|
9668
9938
|
this.tenantId = user.tenantId;
|
|
9669
|
-
this.providerData = user.providerData.map(userInfo => (
|
|
9939
|
+
this.providerData = user.providerData.map(userInfo => ({ ...userInfo }));
|
|
9670
9940
|
this.metadata._copy(user.metadata);
|
|
9671
9941
|
this.stsTokenManager._assign(user.stsTokenManager);
|
|
9672
9942
|
}
|
|
9673
9943
|
_clone(auth) {
|
|
9674
|
-
const newUser = new UserImpl(
|
|
9944
|
+
const newUser = new UserImpl({
|
|
9945
|
+
...this,
|
|
9946
|
+
auth,
|
|
9947
|
+
stsTokenManager: this.stsTokenManager._clone()
|
|
9948
|
+
});
|
|
9675
9949
|
newUser.metadata._copy(this.metadata);
|
|
9676
9950
|
return newUser;
|
|
9677
9951
|
}
|
|
@@ -9726,26 +10000,40 @@ class UserImpl {
|
|
|
9726
10000
|
return this.auth.signOut();
|
|
9727
10001
|
}
|
|
9728
10002
|
toJSON() {
|
|
9729
|
-
return
|
|
10003
|
+
return {
|
|
10004
|
+
uid: this.uid,
|
|
10005
|
+
email: this.email || undefined,
|
|
10006
|
+
emailVerified: this.emailVerified,
|
|
10007
|
+
displayName: this.displayName || undefined,
|
|
10008
|
+
isAnonymous: this.isAnonymous,
|
|
10009
|
+
photoURL: this.photoURL || undefined,
|
|
10010
|
+
phoneNumber: this.phoneNumber || undefined,
|
|
10011
|
+
tenantId: this.tenantId || undefined,
|
|
10012
|
+
providerData: this.providerData.map(userInfo => ({ ...userInfo })),
|
|
10013
|
+
stsTokenManager: this.stsTokenManager.toJSON(),
|
|
9730
10014
|
// Redirect event ID must be maintained in case there is a pending
|
|
9731
10015
|
// redirect event.
|
|
9732
|
-
_redirectEventId: this._redirectEventId
|
|
10016
|
+
_redirectEventId: this._redirectEventId,
|
|
10017
|
+
...this.metadata.toJSON(),
|
|
9733
10018
|
// Required for compatibility with the legacy SDK (go/firebase-auth-sdk-persistence-parsing):
|
|
9734
|
-
apiKey: this.auth.config.apiKey,
|
|
10019
|
+
apiKey: this.auth.config.apiKey,
|
|
10020
|
+
appName: this.auth.name
|
|
10021
|
+
// Missing authDomain will be tolerated by the legacy SDK.
|
|
10022
|
+
// stsTokenManager.apiKey isn't actually required (despite the legacy SDK persisting it).
|
|
10023
|
+
};
|
|
9735
10024
|
}
|
|
9736
10025
|
get refreshToken() {
|
|
9737
10026
|
return this.stsTokenManager.refreshToken || '';
|
|
9738
10027
|
}
|
|
9739
10028
|
static _fromJSON(auth, object) {
|
|
9740
|
-
|
|
9741
|
-
const
|
|
9742
|
-
const
|
|
9743
|
-
const
|
|
9744
|
-
const
|
|
9745
|
-
const
|
|
9746
|
-
const
|
|
9747
|
-
const
|
|
9748
|
-
const lastLoginAt = (_h = object.lastLoginAt) !== null && _h !== void 0 ? _h : undefined;
|
|
10029
|
+
const displayName = object.displayName ?? undefined;
|
|
10030
|
+
const email = object.email ?? undefined;
|
|
10031
|
+
const phoneNumber = object.phoneNumber ?? undefined;
|
|
10032
|
+
const photoURL = object.photoURL ?? undefined;
|
|
10033
|
+
const tenantId = object.tenantId ?? undefined;
|
|
10034
|
+
const _redirectEventId = object._redirectEventId ?? undefined;
|
|
10035
|
+
const createdAt = object.createdAt ?? undefined;
|
|
10036
|
+
const lastLoginAt = object.lastLoginAt ?? undefined;
|
|
9749
10037
|
const { uid, emailVerified, isAnonymous, providerData, stsTokenManager: plainObjectTokenManager } = object;
|
|
9750
10038
|
_assert(uid && plainObjectTokenManager, auth, "internal-error" /* AuthErrorCode.INTERNAL_ERROR */);
|
|
9751
10039
|
const stsTokenManager = StsTokenManager.fromJSON(this.name, plainObjectTokenManager);
|
|
@@ -9775,7 +10063,7 @@ class UserImpl {
|
|
|
9775
10063
|
lastLoginAt
|
|
9776
10064
|
});
|
|
9777
10065
|
if (providerData && Array.isArray(providerData)) {
|
|
9778
|
-
user.providerData = providerData.map(userInfo => (
|
|
10066
|
+
user.providerData = providerData.map(userInfo => ({ ...userInfo }));
|
|
9779
10067
|
}
|
|
9780
10068
|
if (_redirectEventId) {
|
|
9781
10069
|
user._redirectEventId = _redirectEventId;
|
|
@@ -9812,7 +10100,7 @@ class UserImpl {
|
|
|
9812
10100
|
const providerData = coreAccount.providerUserInfo !== undefined
|
|
9813
10101
|
? extractProviderData(coreAccount.providerUserInfo)
|
|
9814
10102
|
: [];
|
|
9815
|
-
const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !
|
|
10103
|
+
const isAnonymous = !(coreAccount.email && coreAccount.passwordHash) && !providerData?.length;
|
|
9816
10104
|
const stsTokenManager = new StsTokenManager();
|
|
9817
10105
|
stsTokenManager.updateFromIdToken(idToken);
|
|
9818
10106
|
// Initialize the Firebase Auth user.
|
|
@@ -9834,7 +10122,7 @@ class UserImpl {
|
|
|
9834
10122
|
providerData,
|
|
9835
10123
|
metadata: new UserMetadata(coreAccount.createdAt, coreAccount.lastLoginAt),
|
|
9836
10124
|
isAnonymous: !(coreAccount.email && coreAccount.passwordHash) &&
|
|
9837
|
-
!
|
|
10125
|
+
!providerData?.length
|
|
9838
10126
|
};
|
|
9839
10127
|
Object.assign(user, updates);
|
|
9840
10128
|
return user;
|
|
@@ -9956,7 +10244,17 @@ class PersistenceUserManager {
|
|
|
9956
10244
|
}
|
|
9957
10245
|
async getCurrentUser() {
|
|
9958
10246
|
const blob = await this.persistence._get(this.fullUserKey);
|
|
9959
|
-
|
|
10247
|
+
if (!blob) {
|
|
10248
|
+
return null;
|
|
10249
|
+
}
|
|
10250
|
+
if (typeof blob === 'string') {
|
|
10251
|
+
const response = await getAccountInfo(this.auth, { idToken: blob }).catch(() => undefined);
|
|
10252
|
+
if (!response) {
|
|
10253
|
+
return null;
|
|
10254
|
+
}
|
|
10255
|
+
return UserImpl._fromGetAccountInfoResponse(this.auth, response, blob);
|
|
10256
|
+
}
|
|
10257
|
+
return UserImpl._fromJSON(this.auth, blob);
|
|
9960
10258
|
}
|
|
9961
10259
|
removeCurrentUser() {
|
|
9962
10260
|
return this.persistence._remove(this.fullUserKey);
|
|
@@ -10003,7 +10301,19 @@ class PersistenceUserManager {
|
|
|
10003
10301
|
try {
|
|
10004
10302
|
const blob = await persistence._get(key);
|
|
10005
10303
|
if (blob) {
|
|
10006
|
-
|
|
10304
|
+
let user;
|
|
10305
|
+
if (typeof blob === 'string') {
|
|
10306
|
+
const response = await getAccountInfo(auth, {
|
|
10307
|
+
idToken: blob
|
|
10308
|
+
}).catch(() => undefined);
|
|
10309
|
+
if (!response) {
|
|
10310
|
+
break;
|
|
10311
|
+
}
|
|
10312
|
+
user = await UserImpl._fromGetAccountInfoResponse(auth, response, blob);
|
|
10313
|
+
}
|
|
10314
|
+
else {
|
|
10315
|
+
user = UserImpl._fromJSON(auth, blob); // throws for unparsable blob (wrong format)
|
|
10316
|
+
}
|
|
10007
10317
|
if (persistence !== selectedPersistence) {
|
|
10008
10318
|
userToMigrate = user;
|
|
10009
10319
|
}
|
|
@@ -10011,7 +10321,7 @@ class PersistenceUserManager {
|
|
|
10011
10321
|
break;
|
|
10012
10322
|
}
|
|
10013
10323
|
}
|
|
10014
|
-
catch
|
|
10324
|
+
catch { }
|
|
10015
10325
|
}
|
|
10016
10326
|
// If we find the user in a persistence that does support migration, use
|
|
10017
10327
|
// that migration path (of only persistences that support migration)
|
|
@@ -10034,7 +10344,7 @@ class PersistenceUserManager {
|
|
|
10034
10344
|
try {
|
|
10035
10345
|
await persistence._remove(key);
|
|
10036
10346
|
}
|
|
10037
|
-
catch
|
|
10347
|
+
catch { }
|
|
10038
10348
|
}
|
|
10039
10349
|
}));
|
|
10040
10350
|
return new PersistenceUserManager(selectedPersistence, auth, userKey);
|
|
@@ -10104,7 +10414,7 @@ function _getBrowserName(userAgent) {
|
|
|
10104
10414
|
// Most modern browsers have name/version at end of user agent string.
|
|
10105
10415
|
const re = /([a-zA-Z\d\.]+)\/[a-zA-Z\d\.]*$/;
|
|
10106
10416
|
const matches = userAgent.match(re);
|
|
10107
|
-
if (
|
|
10417
|
+
if (matches?.length === 2) {
|
|
10108
10418
|
return matches[1];
|
|
10109
10419
|
}
|
|
10110
10420
|
}
|
|
@@ -10140,8 +10450,7 @@ function _isIOS(ua = getUA()) {
|
|
|
10140
10450
|
(/macintosh/i.test(ua) && /mobile/i.test(ua)));
|
|
10141
10451
|
}
|
|
10142
10452
|
function _isIOSStandalone(ua = getUA()) {
|
|
10143
|
-
|
|
10144
|
-
return _isIOS(ua) && !!((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.standalone);
|
|
10453
|
+
return _isIOS(ua) && !!window.navigator?.standalone;
|
|
10145
10454
|
}
|
|
10146
10455
|
function _isIE10() {
|
|
10147
10456
|
return isIE() && document.documentMode === 10;
|
|
@@ -10272,7 +10581,7 @@ class AuthMiddlewareQueue {
|
|
|
10272
10581
|
}
|
|
10273
10582
|
}
|
|
10274
10583
|
throw this.auth._errorFactory.create("login-blocked" /* AuthErrorCode.LOGIN_BLOCKED */, {
|
|
10275
|
-
originalMessage: e
|
|
10584
|
+
originalMessage: e?.message
|
|
10276
10585
|
});
|
|
10277
10586
|
}
|
|
10278
10587
|
}
|
|
@@ -10330,13 +10639,12 @@ const MINIMUM_MIN_PASSWORD_LENGTH = 6;
|
|
|
10330
10639
|
*/
|
|
10331
10640
|
class PasswordPolicyImpl {
|
|
10332
10641
|
constructor(response) {
|
|
10333
|
-
var _a, _b, _c, _d;
|
|
10334
10642
|
// Only include custom strength options defined in the response.
|
|
10335
10643
|
const responseOptions = response.customStrengthOptions;
|
|
10336
10644
|
this.customStrengthOptions = {};
|
|
10337
10645
|
// TODO: Remove once the backend is updated to include the minimum min password length instead of undefined when there is no minimum length set.
|
|
10338
10646
|
this.customStrengthOptions.minPasswordLength =
|
|
10339
|
-
|
|
10647
|
+
responseOptions.minPasswordLength ?? MINIMUM_MIN_PASSWORD_LENGTH;
|
|
10340
10648
|
if (responseOptions.maxPasswordLength) {
|
|
10341
10649
|
this.customStrengthOptions.maxPasswordLength =
|
|
10342
10650
|
responseOptions.maxPasswordLength;
|
|
@@ -10363,12 +10671,11 @@ class PasswordPolicyImpl {
|
|
|
10363
10671
|
}
|
|
10364
10672
|
// Use an empty string if no non-alphanumeric characters are specified in the response.
|
|
10365
10673
|
this.allowedNonAlphanumericCharacters =
|
|
10366
|
-
|
|
10367
|
-
this.forceUpgradeOnSignin =
|
|
10674
|
+
response.allowedNonAlphanumericCharacters?.join('') ?? '';
|
|
10675
|
+
this.forceUpgradeOnSignin = response.forceUpgradeOnSignin ?? false;
|
|
10368
10676
|
this.schemaVersion = response.schemaVersion;
|
|
10369
10677
|
}
|
|
10370
10678
|
validatePassword(password) {
|
|
10371
|
-
var _a, _b, _c, _d, _e, _f;
|
|
10372
10679
|
const status = {
|
|
10373
10680
|
isValid: true,
|
|
10374
10681
|
passwordPolicy: this
|
|
@@ -10377,12 +10684,12 @@ class PasswordPolicyImpl {
|
|
|
10377
10684
|
this.validatePasswordLengthOptions(password, status);
|
|
10378
10685
|
this.validatePasswordCharacterOptions(password, status);
|
|
10379
10686
|
// Combine the status into single isValid property.
|
|
10380
|
-
status.isValid && (status.isValid =
|
|
10381
|
-
status.isValid && (status.isValid =
|
|
10382
|
-
status.isValid && (status.isValid =
|
|
10383
|
-
status.isValid && (status.isValid =
|
|
10384
|
-
status.isValid && (status.isValid =
|
|
10385
|
-
status.isValid && (status.isValid =
|
|
10687
|
+
status.isValid && (status.isValid = status.meetsMinPasswordLength ?? true);
|
|
10688
|
+
status.isValid && (status.isValid = status.meetsMaxPasswordLength ?? true);
|
|
10689
|
+
status.isValid && (status.isValid = status.containsLowercaseLetter ?? true);
|
|
10690
|
+
status.isValid && (status.isValid = status.containsUppercaseLetter ?? true);
|
|
10691
|
+
status.isValid && (status.isValid = status.containsNumericCharacter ?? true);
|
|
10692
|
+
status.isValid && (status.isValid = status.containsNonAlphanumericCharacter ?? true);
|
|
10386
10693
|
return status;
|
|
10387
10694
|
}
|
|
10388
10695
|
/**
|
|
@@ -10497,6 +10804,7 @@ class AuthImpl {
|
|
|
10497
10804
|
this._tenantRecaptchaConfigs = {};
|
|
10498
10805
|
this._projectPasswordPolicy = null;
|
|
10499
10806
|
this._tenantPasswordPolicies = {};
|
|
10807
|
+
this._resolvePersistenceManagerAvailable = undefined;
|
|
10500
10808
|
// Tracks the last notified UID for state change listeners to prevent
|
|
10501
10809
|
// repeated calls to the callbacks. Undefined means it's never been
|
|
10502
10810
|
// called, whereas null means it's been called with a signed out user
|
|
@@ -10507,6 +10815,9 @@ class AuthImpl {
|
|
|
10507
10815
|
this.frameworks = [];
|
|
10508
10816
|
this.name = app.name;
|
|
10509
10817
|
this.clientVersion = config.sdkClientVersion;
|
|
10818
|
+
// TODO(jamesdaniels) explore less hacky way to do this, cookie authentication needs
|
|
10819
|
+
// persistenceMananger to be available. see _getFinalTarget for more context
|
|
10820
|
+
this._persistenceManagerAvailable = new Promise(resolve => (this._resolvePersistenceManagerAvailable = resolve));
|
|
10510
10821
|
}
|
|
10511
10822
|
_initializeWithPersistence(persistenceHierarchy, popupRedirectResolver) {
|
|
10512
10823
|
if (popupRedirectResolver) {
|
|
@@ -10515,17 +10826,17 @@ class AuthImpl {
|
|
|
10515
10826
|
// Have to check for app deletion throughout initialization (after each
|
|
10516
10827
|
// promise resolution)
|
|
10517
10828
|
this._initializationPromise = this.queue(async () => {
|
|
10518
|
-
var _a, _b;
|
|
10519
10829
|
if (this._deleted) {
|
|
10520
10830
|
return;
|
|
10521
10831
|
}
|
|
10522
10832
|
this.persistenceManager = await PersistenceUserManager.create(this, persistenceHierarchy);
|
|
10833
|
+
this._resolvePersistenceManagerAvailable?.();
|
|
10523
10834
|
if (this._deleted) {
|
|
10524
10835
|
return;
|
|
10525
10836
|
}
|
|
10526
10837
|
// Initialize the resolver early if necessary (only applicable to web:
|
|
10527
10838
|
// this will cause the iframe to load immediately in certain cases)
|
|
10528
|
-
if (
|
|
10839
|
+
if (this._popupRedirectResolver?._shouldInitProactively) {
|
|
10529
10840
|
// If this fails, don't halt auth loading
|
|
10530
10841
|
try {
|
|
10531
10842
|
await this._popupRedirectResolver._initialize(this);
|
|
@@ -10535,7 +10846,7 @@ class AuthImpl {
|
|
|
10535
10846
|
}
|
|
10536
10847
|
}
|
|
10537
10848
|
await this.initializeCurrentUser(popupRedirectResolver);
|
|
10538
|
-
this.lastNotifiedUid =
|
|
10849
|
+
this.lastNotifiedUid = this.currentUser?.uid || null;
|
|
10539
10850
|
if (this._deleted) {
|
|
10540
10851
|
return;
|
|
10541
10852
|
}
|
|
@@ -10580,7 +10891,6 @@ class AuthImpl {
|
|
|
10580
10891
|
}
|
|
10581
10892
|
}
|
|
10582
10893
|
async initializeCurrentUser(popupRedirectResolver) {
|
|
10583
|
-
var _a;
|
|
10584
10894
|
if (_isFirebaseServerApp(this.app)) {
|
|
10585
10895
|
const idToken = this.app.settings.authIdToken;
|
|
10586
10896
|
if (idToken) {
|
|
@@ -10600,15 +10910,15 @@ class AuthImpl {
|
|
|
10600
10910
|
let needsTocheckMiddleware = false;
|
|
10601
10911
|
if (popupRedirectResolver && this.config.authDomain) {
|
|
10602
10912
|
await this.getOrInitRedirectPersistenceManager();
|
|
10603
|
-
const redirectUserEventId =
|
|
10604
|
-
const storedUserEventId = futureCurrentUser
|
|
10913
|
+
const redirectUserEventId = this.redirectUser?._redirectEventId;
|
|
10914
|
+
const storedUserEventId = futureCurrentUser?._redirectEventId;
|
|
10605
10915
|
const result = await this.tryRedirectSignIn(popupRedirectResolver);
|
|
10606
10916
|
// If the stored user (i.e. the old "currentUser") has a redirectId that
|
|
10607
10917
|
// matches the redirect user, then we want to initially sign in with the
|
|
10608
10918
|
// new user object from result.
|
|
10609
10919
|
// TODO(samgho): More thoroughly test all of this
|
|
10610
10920
|
if ((!redirectUserEventId || redirectUserEventId === storedUserEventId) &&
|
|
10611
|
-
|
|
10921
|
+
result?.user) {
|
|
10612
10922
|
futureCurrentUser = result.user;
|
|
10613
10923
|
needsTocheckMiddleware = true;
|
|
10614
10924
|
}
|
|
@@ -10683,7 +10993,7 @@ class AuthImpl {
|
|
|
10683
10993
|
await _reloadWithoutSaving(user);
|
|
10684
10994
|
}
|
|
10685
10995
|
catch (e) {
|
|
10686
|
-
if (
|
|
10996
|
+
if (e?.code !==
|
|
10687
10997
|
`auth/${"network-request-failed" /* AuthErrorCode.NETWORK_REQUEST_FAILED */}`) {
|
|
10688
10998
|
// Something's wrong with the user's token. Log them out and remove
|
|
10689
10999
|
// them from storage
|
|
@@ -10789,9 +11099,12 @@ class AuthImpl {
|
|
|
10789
11099
|
this._tenantPasswordPolicies[this.tenantId] = passwordPolicy;
|
|
10790
11100
|
}
|
|
10791
11101
|
}
|
|
10792
|
-
|
|
11102
|
+
_getPersistenceType() {
|
|
10793
11103
|
return this.assertedPersistence.persistence.type;
|
|
10794
11104
|
}
|
|
11105
|
+
_getPersistence() {
|
|
11106
|
+
return this.assertedPersistence.persistence;
|
|
11107
|
+
}
|
|
10795
11108
|
_updateErrorMap(errorMap) {
|
|
10796
11109
|
this._errorFactory = new ErrorFactory('auth', 'Firebase', errorMap());
|
|
10797
11110
|
}
|
|
@@ -10837,12 +11150,11 @@ class AuthImpl {
|
|
|
10837
11150
|
}
|
|
10838
11151
|
}
|
|
10839
11152
|
toJSON() {
|
|
10840
|
-
var _a;
|
|
10841
11153
|
return {
|
|
10842
11154
|
apiKey: this.config.apiKey,
|
|
10843
11155
|
authDomain: this.config.authDomain,
|
|
10844
11156
|
appName: this.name,
|
|
10845
|
-
currentUser:
|
|
11157
|
+
currentUser: this._currentUser?.toJSON()
|
|
10846
11158
|
};
|
|
10847
11159
|
}
|
|
10848
11160
|
async _setRedirectUser(user, popupRedirectResolver) {
|
|
@@ -10863,16 +11175,15 @@ class AuthImpl {
|
|
|
10863
11175
|
return this.redirectPersistenceManager;
|
|
10864
11176
|
}
|
|
10865
11177
|
async _redirectUserForId(id) {
|
|
10866
|
-
var _a, _b;
|
|
10867
11178
|
// Make sure we've cleared any pending persistence actions if we're not in
|
|
10868
11179
|
// the initializer
|
|
10869
11180
|
if (this._isInitialized) {
|
|
10870
11181
|
await this.queue(async () => { });
|
|
10871
11182
|
}
|
|
10872
|
-
if (
|
|
11183
|
+
if (this._currentUser?._redirectEventId === id) {
|
|
10873
11184
|
return this._currentUser;
|
|
10874
11185
|
}
|
|
10875
|
-
if (
|
|
11186
|
+
if (this.redirectUser?._redirectEventId === id) {
|
|
10876
11187
|
return this.redirectUser;
|
|
10877
11188
|
}
|
|
10878
11189
|
return null;
|
|
@@ -10908,12 +11219,11 @@ class AuthImpl {
|
|
|
10908
11219
|
return this.currentUser;
|
|
10909
11220
|
}
|
|
10910
11221
|
notifyAuthListeners() {
|
|
10911
|
-
var _a, _b;
|
|
10912
11222
|
if (!this._isInitialized) {
|
|
10913
11223
|
return;
|
|
10914
11224
|
}
|
|
10915
11225
|
this.idTokenSubscription.next(this.currentUser);
|
|
10916
|
-
const currentUid =
|
|
11226
|
+
const currentUid = this.currentUser?.uid ?? null;
|
|
10917
11227
|
if (this.lastNotifiedUid !== currentUid) {
|
|
10918
11228
|
this.lastNotifiedUid = currentUid;
|
|
10919
11229
|
this.authStateSubscription.next(this.currentUser);
|
|
@@ -10998,7 +11308,6 @@ class AuthImpl {
|
|
|
10998
11308
|
return this.frameworks;
|
|
10999
11309
|
}
|
|
11000
11310
|
async _getAdditionalHeaders() {
|
|
11001
|
-
var _a;
|
|
11002
11311
|
// Additional headers on every request
|
|
11003
11312
|
const headers = {
|
|
11004
11313
|
["X-Client-Version" /* HttpHeader.X_CLIENT_VERSION */]: this.clientVersion
|
|
@@ -11007,10 +11316,11 @@ class AuthImpl {
|
|
|
11007
11316
|
headers["X-Firebase-gmpid" /* HttpHeader.X_FIREBASE_GMPID */] = this.app.options.appId;
|
|
11008
11317
|
}
|
|
11009
11318
|
// If the heartbeat service exists, add the heartbeat string
|
|
11010
|
-
const heartbeatsHeader = await
|
|
11319
|
+
const heartbeatsHeader = await this.heartbeatServiceProvider
|
|
11011
11320
|
.getImmediate({
|
|
11012
11321
|
optional: true
|
|
11013
|
-
})
|
|
11322
|
+
})
|
|
11323
|
+
?.getHeartbeatsHeader();
|
|
11014
11324
|
if (heartbeatsHeader) {
|
|
11015
11325
|
headers["X-Firebase-Client" /* HttpHeader.X_FIREBASE_CLIENT */] = heartbeatsHeader;
|
|
11016
11326
|
}
|
|
@@ -11022,17 +11332,20 @@ class AuthImpl {
|
|
|
11022
11332
|
return headers;
|
|
11023
11333
|
}
|
|
11024
11334
|
async _getAppCheckToken() {
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11335
|
+
if (_isFirebaseServerApp(this.app) && this.app.settings.appCheckToken) {
|
|
11336
|
+
return this.app.settings.appCheckToken;
|
|
11337
|
+
}
|
|
11338
|
+
const appCheckTokenResult = await this.appCheckServiceProvider
|
|
11339
|
+
.getImmediate({ optional: true })
|
|
11340
|
+
?.getToken();
|
|
11341
|
+
if (appCheckTokenResult?.error) {
|
|
11029
11342
|
// Context: appCheck.getToken() will never throw even if an error happened.
|
|
11030
11343
|
// In the error case, a dummy token will be returned along with an error field describing
|
|
11031
11344
|
// the error. In general, we shouldn't care about the error condition and just use
|
|
11032
11345
|
// the token (actual or dummy) to send requests.
|
|
11033
11346
|
_logWarn(`Error while retrieving App Check token: ${appCheckTokenResult.error}`);
|
|
11034
11347
|
}
|
|
11035
|
-
return appCheckTokenResult
|
|
11348
|
+
return appCheckTokenResult?.token;
|
|
11036
11349
|
}
|
|
11037
11350
|
}
|
|
11038
11351
|
/**
|
|
@@ -11140,7 +11453,7 @@ function initializeAuth(app, deps) {
|
|
|
11140
11453
|
if (provider.isInitialized()) {
|
|
11141
11454
|
const auth = provider.getImmediate();
|
|
11142
11455
|
const initialOptions = provider.getOptions();
|
|
11143
|
-
if (deepEqual(initialOptions, deps
|
|
11456
|
+
if (deepEqual(initialOptions, deps ?? {})) {
|
|
11144
11457
|
return auth;
|
|
11145
11458
|
}
|
|
11146
11459
|
else {
|
|
@@ -11151,15 +11464,15 @@ function initializeAuth(app, deps) {
|
|
|
11151
11464
|
return auth;
|
|
11152
11465
|
}
|
|
11153
11466
|
function _initializeAuthInstance(auth, deps) {
|
|
11154
|
-
const persistence =
|
|
11467
|
+
const persistence = deps?.persistence || [];
|
|
11155
11468
|
const hierarchy = (Array.isArray(persistence) ? persistence : [persistence]).map(_getInstance);
|
|
11156
|
-
if (deps
|
|
11469
|
+
if (deps?.errorMap) {
|
|
11157
11470
|
auth._updateErrorMap(deps.errorMap);
|
|
11158
11471
|
}
|
|
11159
11472
|
// This promise is intended to float; auth initialization happens in the
|
|
11160
11473
|
// background, meanwhile the auth object may be used by the app.
|
|
11161
11474
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
11162
|
-
auth._initializeWithPersistence(hierarchy, deps
|
|
11475
|
+
auth._initializeWithPersistence(hierarchy, deps?.popupRedirectResolver);
|
|
11163
11476
|
}
|
|
11164
11477
|
|
|
11165
11478
|
/**
|
|
@@ -11186,22 +11499,41 @@ function _initializeAuthInstance(auth, deps) {
|
|
|
11186
11499
|
*/
|
|
11187
11500
|
function connectAuthEmulator(auth, url, options) {
|
|
11188
11501
|
const authInternal = _castAuth(auth);
|
|
11189
|
-
_assert(authInternal._canInitEmulator, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11190
11502
|
_assert(/^https?:\/\//.test(url), authInternal, "invalid-emulator-scheme" /* AuthErrorCode.INVALID_EMULATOR_SCHEME */);
|
|
11191
|
-
const disableWarnings = !!
|
|
11503
|
+
const disableWarnings = !!options?.disableWarnings;
|
|
11192
11504
|
const protocol = extractProtocol(url);
|
|
11193
11505
|
const { host, port } = extractHostAndPort(url);
|
|
11194
11506
|
const portStr = port === null ? '' : `:${port}`;
|
|
11195
11507
|
// Always replace path with "/" (even if input url had no path at all, or had a different one).
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
authInternal.emulatorConfig = Object.freeze({
|
|
11508
|
+
const emulator = { url: `${protocol}//${host}${portStr}/` };
|
|
11509
|
+
const emulatorConfig = Object.freeze({
|
|
11199
11510
|
host,
|
|
11200
11511
|
port,
|
|
11201
11512
|
protocol: protocol.replace(':', ''),
|
|
11202
11513
|
options: Object.freeze({ disableWarnings })
|
|
11203
11514
|
});
|
|
11204
|
-
|
|
11515
|
+
// There are a few scenarios to guard against if the Auth instance has already started:
|
|
11516
|
+
if (!authInternal._canInitEmulator) {
|
|
11517
|
+
// Applications may not initialize the emulator for the first time if Auth has already started
|
|
11518
|
+
// to make network requests.
|
|
11519
|
+
_assert(authInternal.config.emulator && authInternal.emulatorConfig, authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11520
|
+
// Applications may not alter the configuration of the emulator (aka pass a different config)
|
|
11521
|
+
// once Auth has started to make network requests.
|
|
11522
|
+
_assert(deepEqual(emulator, authInternal.config.emulator) &&
|
|
11523
|
+
deepEqual(emulatorConfig, authInternal.emulatorConfig), authInternal, "emulator-config-failed" /* AuthErrorCode.EMULATOR_CONFIG_FAILED */);
|
|
11524
|
+
// It's valid, however, to invoke connectAuthEmulator() after Auth has started making
|
|
11525
|
+
// connections, so long as the config matches the existing config. This results in a no-op.
|
|
11526
|
+
return;
|
|
11527
|
+
}
|
|
11528
|
+
authInternal.config.emulator = emulator;
|
|
11529
|
+
authInternal.emulatorConfig = emulatorConfig;
|
|
11530
|
+
authInternal.settings.appVerificationDisabledForTesting = true;
|
|
11531
|
+
// Workaround to get cookies in Firebase Studio
|
|
11532
|
+
if (isCloudWorkstation(host)) {
|
|
11533
|
+
void pingServer(`${protocol}//${host}${portStr}`);
|
|
11534
|
+
updateEmulatorBanner('Auth', true);
|
|
11535
|
+
}
|
|
11536
|
+
else {
|
|
11205
11537
|
emitEmulatorWarning();
|
|
11206
11538
|
}
|
|
11207
11539
|
}
|
|
@@ -11440,7 +11772,7 @@ class OAuthCredential extends AuthCredential {
|
|
|
11440
11772
|
*/
|
|
11441
11773
|
static fromJSON(json) {
|
|
11442
11774
|
const obj = typeof json === 'string' ? JSON.parse(json) : json;
|
|
11443
|
-
const { providerId, signInMethod
|
|
11775
|
+
const { providerId, signInMethod, ...rest } = obj;
|
|
11444
11776
|
if (!providerId || !signInMethod) {
|
|
11445
11777
|
return null;
|
|
11446
11778
|
}
|
|
@@ -11717,7 +12049,7 @@ class FacebookAuthProvider extends BaseOAuthProvider {
|
|
|
11717
12049
|
try {
|
|
11718
12050
|
return FacebookAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
11719
12051
|
}
|
|
11720
|
-
catch
|
|
12052
|
+
catch {
|
|
11721
12053
|
return null;
|
|
11722
12054
|
}
|
|
11723
12055
|
}
|
|
@@ -11839,7 +12171,7 @@ class GoogleAuthProvider extends BaseOAuthProvider {
|
|
|
11839
12171
|
try {
|
|
11840
12172
|
return GoogleAuthProvider.credential(oauthIdToken, oauthAccessToken);
|
|
11841
12173
|
}
|
|
11842
|
-
catch
|
|
12174
|
+
catch {
|
|
11843
12175
|
return null;
|
|
11844
12176
|
}
|
|
11845
12177
|
}
|
|
@@ -11950,7 +12282,7 @@ class GithubAuthProvider extends BaseOAuthProvider {
|
|
|
11950
12282
|
try {
|
|
11951
12283
|
return GithubAuthProvider.credential(tokenResponse.oauthAccessToken);
|
|
11952
12284
|
}
|
|
11953
|
-
catch
|
|
12285
|
+
catch {
|
|
11954
12286
|
return null;
|
|
11955
12287
|
}
|
|
11956
12288
|
}
|
|
@@ -12061,7 +12393,7 @@ class TwitterAuthProvider extends BaseOAuthProvider {
|
|
|
12061
12393
|
try {
|
|
12062
12394
|
return TwitterAuthProvider.credential(oauthAccessToken, oauthTokenSecret);
|
|
12063
12395
|
}
|
|
12064
|
-
catch
|
|
12396
|
+
catch {
|
|
12065
12397
|
return null;
|
|
12066
12398
|
}
|
|
12067
12399
|
}
|
|
@@ -12144,7 +12476,6 @@ function providerIdForResponse(response) {
|
|
|
12144
12476
|
*/
|
|
12145
12477
|
class MultiFactorError extends FirebaseError {
|
|
12146
12478
|
constructor(auth, error, operationType, user) {
|
|
12147
|
-
var _a;
|
|
12148
12479
|
super(error.code, error.message);
|
|
12149
12480
|
this.operationType = operationType;
|
|
12150
12481
|
this.user = user;
|
|
@@ -12152,7 +12483,7 @@ class MultiFactorError extends FirebaseError {
|
|
|
12152
12483
|
Object.setPrototypeOf(this, MultiFactorError.prototype);
|
|
12153
12484
|
this.customData = {
|
|
12154
12485
|
appName: auth.name,
|
|
12155
|
-
tenantId:
|
|
12486
|
+
tenantId: auth.tenantId ?? undefined,
|
|
12156
12487
|
_serverResponse: error.customData._serverResponse,
|
|
12157
12488
|
operationType
|
|
12158
12489
|
};
|
|
@@ -12210,7 +12541,7 @@ async function _reauthenticate(user, credential, bypassAuthState = false) {
|
|
|
12210
12541
|
}
|
|
12211
12542
|
catch (e) {
|
|
12212
12543
|
// Convert user deleted error into user mismatch
|
|
12213
|
-
if (
|
|
12544
|
+
if (e?.code === `auth/${"user-not-found" /* AuthErrorCode.USER_DELETED */}`) {
|
|
12214
12545
|
_fail(auth, "user-mismatch" /* AuthErrorCode.USER_MISMATCH */);
|
|
12215
12546
|
}
|
|
12216
12547
|
throw e;
|
|
@@ -12400,7 +12731,7 @@ class BrowserPersistenceClass {
|
|
|
12400
12731
|
this.storage.removeItem(STORAGE_AVAILABLE_KEY);
|
|
12401
12732
|
return Promise.resolve(true);
|
|
12402
12733
|
}
|
|
12403
|
-
catch
|
|
12734
|
+
catch {
|
|
12404
12735
|
return Promise.resolve(false);
|
|
12405
12736
|
}
|
|
12406
12737
|
}
|
|
@@ -12741,7 +13072,7 @@ class Receiver {
|
|
|
12741
13072
|
const messageEvent = event;
|
|
12742
13073
|
const { eventId, eventType, data } = messageEvent.data;
|
|
12743
13074
|
const handlers = this.handlersMap[eventType];
|
|
12744
|
-
if (!
|
|
13075
|
+
if (!handlers?.size) {
|
|
12745
13076
|
return;
|
|
12746
13077
|
}
|
|
12747
13078
|
messageEvent.ports[0].postMessage({
|
|
@@ -12977,20 +13308,19 @@ function _isWorker() {
|
|
|
12977
13308
|
typeof _window()['importScripts'] === 'function');
|
|
12978
13309
|
}
|
|
12979
13310
|
async function _getActiveServiceWorker() {
|
|
12980
|
-
if (!
|
|
13311
|
+
if (!navigator?.serviceWorker) {
|
|
12981
13312
|
return null;
|
|
12982
13313
|
}
|
|
12983
13314
|
try {
|
|
12984
13315
|
const registration = await navigator.serviceWorker.ready;
|
|
12985
13316
|
return registration.active;
|
|
12986
13317
|
}
|
|
12987
|
-
catch
|
|
13318
|
+
catch {
|
|
12988
13319
|
return null;
|
|
12989
13320
|
}
|
|
12990
13321
|
}
|
|
12991
13322
|
function _getServiceWorkerController() {
|
|
12992
|
-
|
|
12993
|
-
return ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.serviceWorker) === null || _a === void 0 ? void 0 : _a.controller) || null;
|
|
13323
|
+
return navigator?.serviceWorker?.controller || null;
|
|
12994
13324
|
}
|
|
12995
13325
|
function _getWorkerGlobalScope() {
|
|
12996
13326
|
return _isWorker() ? self : null;
|
|
@@ -13173,7 +13503,6 @@ class IndexedDBLocalPersistence {
|
|
|
13173
13503
|
* may not resolve.
|
|
13174
13504
|
*/
|
|
13175
13505
|
async initializeSender() {
|
|
13176
|
-
var _a, _b;
|
|
13177
13506
|
// Check to see if there's an active service worker.
|
|
13178
13507
|
this.activeServiceWorker = await _getActiveServiceWorker();
|
|
13179
13508
|
if (!this.activeServiceWorker) {
|
|
@@ -13185,8 +13514,8 @@ class IndexedDBLocalPersistence {
|
|
|
13185
13514
|
if (!results) {
|
|
13186
13515
|
return;
|
|
13187
13516
|
}
|
|
13188
|
-
if (
|
|
13189
|
-
|
|
13517
|
+
if (results[0]?.fulfilled &&
|
|
13518
|
+
results[0]?.value.includes("keyChanged" /* _EventType.KEY_CHANGED */)) {
|
|
13190
13519
|
this.serviceWorkerReceiverAvailable = true;
|
|
13191
13520
|
}
|
|
13192
13521
|
}
|
|
@@ -13212,7 +13541,7 @@ class IndexedDBLocalPersistence {
|
|
|
13212
13541
|
? 800 /* _TimeoutDuration.LONG_ACK */
|
|
13213
13542
|
: 50 /* _TimeoutDuration.ACK */);
|
|
13214
13543
|
}
|
|
13215
|
-
catch
|
|
13544
|
+
catch {
|
|
13216
13545
|
// This is a best effort approach. Ignore errors.
|
|
13217
13546
|
}
|
|
13218
13547
|
}
|
|
@@ -13226,7 +13555,7 @@ class IndexedDBLocalPersistence {
|
|
|
13226
13555
|
await _deleteObject(db, STORAGE_AVAILABLE_KEY);
|
|
13227
13556
|
return true;
|
|
13228
13557
|
}
|
|
13229
|
-
catch
|
|
13558
|
+
catch { }
|
|
13230
13559
|
return false;
|
|
13231
13560
|
}
|
|
13232
13561
|
async _withPendingWrite(write) {
|
|
@@ -13596,8 +13925,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
|
|
|
13596
13925
|
this.pollUserCancellation();
|
|
13597
13926
|
}
|
|
13598
13927
|
get eventId() {
|
|
13599
|
-
|
|
13600
|
-
return ((_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.associatedEvent) || null;
|
|
13928
|
+
return this.authWindow?.associatedEvent || null;
|
|
13601
13929
|
}
|
|
13602
13930
|
cancel() {
|
|
13603
13931
|
this.reject(_createError(this.auth, "cancelled-popup-request" /* AuthErrorCode.EXPIRED_POPUP_REQUEST */));
|
|
@@ -13615,8 +13943,7 @@ class PopupOperation extends AbstractPopupRedirectOperation {
|
|
|
13615
13943
|
}
|
|
13616
13944
|
pollUserCancellation() {
|
|
13617
13945
|
const poll = () => {
|
|
13618
|
-
|
|
13619
|
-
if ((_b = (_a = this.authWindow) === null || _a === void 0 ? void 0 : _a.window) === null || _b === void 0 ? void 0 : _b.closed) {
|
|
13946
|
+
if (this.authWindow?.window?.closed) {
|
|
13620
13947
|
// Make sure that there is sufficient time for whatever action to
|
|
13621
13948
|
// complete. The window could have closed but the sign in network
|
|
13622
13949
|
// call could still be in flight. This is specifically true for
|
|
@@ -13816,9 +14143,8 @@ class AuthEventManager {
|
|
|
13816
14143
|
return handled;
|
|
13817
14144
|
}
|
|
13818
14145
|
sendToConsumer(event, consumer) {
|
|
13819
|
-
var _a;
|
|
13820
14146
|
if (event.error && !isNullRedirectEvent(event)) {
|
|
13821
|
-
const code =
|
|
14147
|
+
const code = event.error.code?.split('auth/')[1] ||
|
|
13822
14148
|
"internal-error" /* AuthErrorCode.INTERNAL_ERROR */;
|
|
13823
14149
|
consumer.onError(_createError(this.auth, code));
|
|
13824
14150
|
}
|
|
@@ -13848,7 +14174,7 @@ function eventUid(e) {
|
|
|
13848
14174
|
}
|
|
13849
14175
|
function isNullRedirectEvent({ type, error }) {
|
|
13850
14176
|
return (type === "unknown" /* AuthEventType.UNKNOWN */ &&
|
|
13851
|
-
|
|
14177
|
+
error?.code === `auth/${"no-auth-event" /* AuthErrorCode.NO_AUTH_EVENT */}`);
|
|
13852
14178
|
}
|
|
13853
14179
|
function isRedirectEvent(event) {
|
|
13854
14180
|
switch (event.type) {
|
|
@@ -13913,7 +14239,7 @@ async function _validateOrigin(auth) {
|
|
|
13913
14239
|
return;
|
|
13914
14240
|
}
|
|
13915
14241
|
}
|
|
13916
|
-
catch
|
|
14242
|
+
catch {
|
|
13917
14243
|
// Do nothing if there's a URL error; just continue searching
|
|
13918
14244
|
}
|
|
13919
14245
|
}
|
|
@@ -13976,7 +14302,7 @@ function resetUnloadedGapiModules() {
|
|
|
13976
14302
|
// Get gapix.beacon context.
|
|
13977
14303
|
const beacon = _window().___jsl;
|
|
13978
14304
|
// Get current hint.
|
|
13979
|
-
if (beacon
|
|
14305
|
+
if (beacon?.H) {
|
|
13980
14306
|
// Get gapi hint.
|
|
13981
14307
|
for (const hint of Object.keys(beacon.H)) {
|
|
13982
14308
|
// Requested modules.
|
|
@@ -13997,7 +14323,6 @@ function resetUnloadedGapiModules() {
|
|
|
13997
14323
|
}
|
|
13998
14324
|
function loadGapi(auth) {
|
|
13999
14325
|
return new Promise((resolve, reject) => {
|
|
14000
|
-
var _a, _b, _c;
|
|
14001
14326
|
// Function to run when gapi.load is ready.
|
|
14002
14327
|
function loadGapiIframe() {
|
|
14003
14328
|
// The developer may have tried to previously run gapi.load and failed.
|
|
@@ -14020,11 +14345,11 @@ function loadGapi(auth) {
|
|
|
14020
14345
|
timeout: NETWORK_TIMEOUT.get()
|
|
14021
14346
|
});
|
|
14022
14347
|
}
|
|
14023
|
-
if (
|
|
14348
|
+
if (_window().gapi?.iframes?.Iframe) {
|
|
14024
14349
|
// If gapi.iframes.Iframe available, resolve.
|
|
14025
14350
|
resolve(gapi.iframes.getContext());
|
|
14026
14351
|
}
|
|
14027
|
-
else if (!!
|
|
14352
|
+
else if (!!_window().gapi?.load) {
|
|
14028
14353
|
// Gapi loader ready, load gapi.iframes.
|
|
14029
14354
|
loadGapiIframe();
|
|
14030
14355
|
}
|
|
@@ -14197,8 +14522,13 @@ function _open(auth, url, name, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT)
|
|
|
14197
14522
|
const top = Math.max((window.screen.availHeight - height) / 2, 0).toString();
|
|
14198
14523
|
const left = Math.max((window.screen.availWidth - width) / 2, 0).toString();
|
|
14199
14524
|
let target = '';
|
|
14200
|
-
const options =
|
|
14201
|
-
|
|
14525
|
+
const options = {
|
|
14526
|
+
...BASE_POPUP_OPTIONS,
|
|
14527
|
+
width: width.toString(),
|
|
14528
|
+
height: height.toString(),
|
|
14529
|
+
top,
|
|
14530
|
+
left
|
|
14531
|
+
};
|
|
14202
14532
|
// Chrome iOS 7 and 8 is returning an undefined popup win when target is
|
|
14203
14533
|
// specified, even though the popup is not necessarily blocked.
|
|
14204
14534
|
const ua = getUA().toLowerCase();
|
|
@@ -14358,8 +14688,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14358
14688
|
// Wrapping in async even though we don't await anywhere in order
|
|
14359
14689
|
// to make sure errors are raised as promise rejections
|
|
14360
14690
|
async _openPopup(auth, provider, authType, eventId) {
|
|
14361
|
-
|
|
14362
|
-
debugAssert((_a = this.eventManagers[auth._key()]) === null || _a === void 0 ? void 0 : _a.manager, '_initialize() not called before _openPopup()');
|
|
14691
|
+
debugAssert(this.eventManagers[auth._key()]?.manager, '_initialize() not called before _openPopup()');
|
|
14363
14692
|
const url = await _getRedirectUrl(auth, provider, authType, _getCurrentUrl(), eventId);
|
|
14364
14693
|
return _open(auth, url, _generateEventId());
|
|
14365
14694
|
}
|
|
@@ -14394,7 +14723,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14394
14723
|
const iframe = await _openIframe(auth);
|
|
14395
14724
|
const manager = new AuthEventManager(auth);
|
|
14396
14725
|
iframe.register('authEvent', (iframeEvent) => {
|
|
14397
|
-
_assert(iframeEvent
|
|
14726
|
+
_assert(iframeEvent?.authEvent, auth, "invalid-auth-event" /* AuthErrorCode.INVALID_AUTH_EVENT */);
|
|
14398
14727
|
// TODO: Consider splitting redirect and popup events earlier on
|
|
14399
14728
|
const handled = manager.onEvent(iframeEvent.authEvent);
|
|
14400
14729
|
return { status: handled ? "ACK" /* GapiOutcome.ACK */ : "ERROR" /* GapiOutcome.ERROR */ };
|
|
@@ -14406,8 +14735,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14406
14735
|
_isIframeWebStorageSupported(auth, cb) {
|
|
14407
14736
|
const iframe = this.iframes[auth._key()];
|
|
14408
14737
|
iframe.send(WEB_STORAGE_SUPPORT_KEY, { type: WEB_STORAGE_SUPPORT_KEY }, result => {
|
|
14409
|
-
|
|
14410
|
-
const isSupported = (_a = result === null || result === void 0 ? void 0 : result[0]) === null || _a === void 0 ? void 0 : _a[WEB_STORAGE_SUPPORT_KEY];
|
|
14738
|
+
const isSupported = result?.[0]?.[WEB_STORAGE_SUPPORT_KEY];
|
|
14411
14739
|
if (isSupported !== undefined) {
|
|
14412
14740
|
cb(!!isSupported);
|
|
14413
14741
|
}
|
|
@@ -14438,7 +14766,7 @@ class BrowserPopupRedirectResolver {
|
|
|
14438
14766
|
const browserPopupRedirectResolver = BrowserPopupRedirectResolver;
|
|
14439
14767
|
|
|
14440
14768
|
var name$1 = "@firebase/auth";
|
|
14441
|
-
var version$1 = "1.
|
|
14769
|
+
var version$1 = "1.11.1";
|
|
14442
14770
|
|
|
14443
14771
|
/**
|
|
14444
14772
|
* @license
|
|
@@ -14462,9 +14790,8 @@ class AuthInterop {
|
|
|
14462
14790
|
this.internalListeners = new Map();
|
|
14463
14791
|
}
|
|
14464
14792
|
getUid() {
|
|
14465
|
-
var _a;
|
|
14466
14793
|
this.assertAuthConfigured();
|
|
14467
|
-
return
|
|
14794
|
+
return this.auth.currentUser?.uid || null;
|
|
14468
14795
|
}
|
|
14469
14796
|
async getToken(forceRefresh) {
|
|
14470
14797
|
this.assertAuthConfigured();
|
|
@@ -14481,7 +14808,7 @@ class AuthInterop {
|
|
|
14481
14808
|
return;
|
|
14482
14809
|
}
|
|
14483
14810
|
const unsubscribe = this.auth.onIdTokenChanged(user => {
|
|
14484
|
-
listener(
|
|
14811
|
+
listener(user?.stsTokenManager.accessToken || null);
|
|
14485
14812
|
});
|
|
14486
14813
|
this.internalListeners.set(listener, unsubscribe);
|
|
14487
14814
|
this.updateProactiveRefresh();
|
|
@@ -14580,8 +14907,8 @@ function registerAuth(clientPlatform) {
|
|
|
14580
14907
|
return (auth => new AuthInterop(auth))(auth);
|
|
14581
14908
|
}, "PRIVATE" /* ComponentType.PRIVATE */).setInstantiationMode("EXPLICIT" /* InstantiationMode.EXPLICIT */));
|
|
14582
14909
|
registerVersion(name$1, version$1, getVersionForPlatform(clientPlatform));
|
|
14583
|
-
// BUILD_TARGET will be replaced by values like
|
|
14584
|
-
registerVersion(name$1, version$1, '
|
|
14910
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
14911
|
+
registerVersion(name$1, version$1, 'esm2020');
|
|
14585
14912
|
}
|
|
14586
14913
|
|
|
14587
14914
|
/**
|
|
@@ -14611,7 +14938,7 @@ const mintCookieFactory = (url) => async (user) => {
|
|
|
14611
14938
|
return;
|
|
14612
14939
|
}
|
|
14613
14940
|
// Specifically trip null => undefined when logged out, to delete any existing cookie
|
|
14614
|
-
const idToken = idTokenResult
|
|
14941
|
+
const idToken = idTokenResult?.token;
|
|
14615
14942
|
if (lastPostedIdToken === idToken) {
|
|
14616
14943
|
return;
|
|
14617
14944
|
}
|
|
@@ -14666,8 +14993,7 @@ function getAuth(app = getApp()) {
|
|
|
14666
14993
|
return auth;
|
|
14667
14994
|
}
|
|
14668
14995
|
function getScriptParentElement() {
|
|
14669
|
-
|
|
14670
|
-
return (_b = (_a = document.getElementsByTagName('head')) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : document;
|
|
14996
|
+
return document.getElementsByTagName('head')?.[0] ?? document;
|
|
14671
14997
|
}
|
|
14672
14998
|
_setExternalJSProvider({
|
|
14673
14999
|
loadJS(url) {
|
|
@@ -14693,7 +15019,7 @@ _setExternalJSProvider({
|
|
|
14693
15019
|
registerAuth("Browser" /* ClientPlatform.BROWSER */);
|
|
14694
15020
|
|
|
14695
15021
|
const name = "@firebase/database";
|
|
14696
|
-
const version = "1.0
|
|
15022
|
+
const version = "1.1.0";
|
|
14697
15023
|
|
|
14698
15024
|
/**
|
|
14699
15025
|
* @license
|
|
@@ -15390,15 +15716,24 @@ const setTimeoutNonBlocking = function (fn, time) {
|
|
|
15390
15716
|
* Abstraction around AppCheck's token fetching capabilities.
|
|
15391
15717
|
*/
|
|
15392
15718
|
class AppCheckTokenProvider {
|
|
15393
|
-
constructor(
|
|
15394
|
-
this.appName_ = appName_;
|
|
15719
|
+
constructor(app, appCheckProvider) {
|
|
15395
15720
|
this.appCheckProvider = appCheckProvider;
|
|
15396
|
-
this.
|
|
15721
|
+
this.appName = app.name;
|
|
15722
|
+
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
|
|
15723
|
+
this.serverAppAppCheckToken = app.settings.appCheckToken;
|
|
15724
|
+
}
|
|
15725
|
+
this.appCheck = appCheckProvider?.getImmediate({ optional: true });
|
|
15397
15726
|
if (!this.appCheck) {
|
|
15398
|
-
appCheckProvider
|
|
15727
|
+
appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck));
|
|
15399
15728
|
}
|
|
15400
15729
|
}
|
|
15401
15730
|
getToken(forceRefresh) {
|
|
15731
|
+
if (this.serverAppAppCheckToken) {
|
|
15732
|
+
if (forceRefresh) {
|
|
15733
|
+
throw new Error('Attempted reuse of `FirebaseServerApp.appCheckToken` after previous usage failed.');
|
|
15734
|
+
}
|
|
15735
|
+
return Promise.resolve({ token: this.serverAppAppCheckToken });
|
|
15736
|
+
}
|
|
15402
15737
|
if (!this.appCheck) {
|
|
15403
15738
|
return new Promise((resolve, reject) => {
|
|
15404
15739
|
// Support delayed initialization of FirebaseAppCheck. This allows our
|
|
@@ -15418,11 +15753,12 @@ class AppCheckTokenProvider {
|
|
|
15418
15753
|
return this.appCheck.getToken(forceRefresh);
|
|
15419
15754
|
}
|
|
15420
15755
|
addTokenChangeListener(listener) {
|
|
15421
|
-
|
|
15422
|
-
|
|
15756
|
+
this.appCheckProvider
|
|
15757
|
+
?.get()
|
|
15758
|
+
.then(appCheck => appCheck.addTokenListener(listener));
|
|
15423
15759
|
}
|
|
15424
15760
|
notifyForInvalidToken() {
|
|
15425
|
-
warn(`Provided AppCheck credentials for the app named "${this.
|
|
15761
|
+
warn(`Provided AppCheck credentials for the app named "${this.appName}" ` +
|
|
15426
15762
|
'are invalid. This usually indicates your app was not initialized correctly.');
|
|
15427
15763
|
}
|
|
15428
15764
|
}
|
|
@@ -15608,7 +15944,7 @@ class RepoInfo {
|
|
|
15608
15944
|
* @param nodeAdmin - Whether this instance uses Admin SDK credentials
|
|
15609
15945
|
* @param persistenceKey - Override the default session persistence storage key
|
|
15610
15946
|
*/
|
|
15611
|
-
constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false) {
|
|
15947
|
+
constructor(host, secure, namespace, webSocketOnly, nodeAdmin = false, persistenceKey = '', includeNamespaceInQueryParams = false, isUsingEmulator = false, emulatorOptions = null) {
|
|
15612
15948
|
this.secure = secure;
|
|
15613
15949
|
this.namespace = namespace;
|
|
15614
15950
|
this.webSocketOnly = webSocketOnly;
|
|
@@ -15616,6 +15952,7 @@ class RepoInfo {
|
|
|
15616
15952
|
this.persistenceKey = persistenceKey;
|
|
15617
15953
|
this.includeNamespaceInQueryParams = includeNamespaceInQueryParams;
|
|
15618
15954
|
this.isUsingEmulator = isUsingEmulator;
|
|
15955
|
+
this.emulatorOptions = emulatorOptions;
|
|
15619
15956
|
this._host = host.toLowerCase();
|
|
15620
15957
|
this._domain = this._host.substr(this._host.indexOf('.') + 1);
|
|
15621
15958
|
this.internalHost =
|
|
@@ -17102,7 +17439,9 @@ class Connection {
|
|
|
17102
17439
|
if (MESSAGE_DATA in controlData) {
|
|
17103
17440
|
const payload = controlData[MESSAGE_DATA];
|
|
17104
17441
|
if (cmd === SERVER_HELLO) {
|
|
17105
|
-
const handshakePayload =
|
|
17442
|
+
const handshakePayload = {
|
|
17443
|
+
...payload
|
|
17444
|
+
};
|
|
17106
17445
|
if (this.repoInfo_.isUsingEmulator) {
|
|
17107
17446
|
// 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.
|
|
17108
17447
|
handshakePayload.h = this.repoInfo_.host;
|
|
@@ -18368,7 +18707,7 @@ class PersistentConnection extends ServerActions {
|
|
|
18368
18707
|
}
|
|
18369
18708
|
this.lastConnectionEstablishedTime_ = null;
|
|
18370
18709
|
}
|
|
18371
|
-
const timeSinceLastConnectAttempt = new Date().getTime() - this.lastConnectionAttemptTime_;
|
|
18710
|
+
const timeSinceLastConnectAttempt = Math.max(0, new Date().getTime() - this.lastConnectionAttemptTime_);
|
|
18372
18711
|
let reconnectDelay = Math.max(0, this.reconnectDelay_ - timeSinceLastConnectAttempt);
|
|
18373
18712
|
reconnectDelay = Math.random() * reconnectDelay;
|
|
18374
18713
|
this.log_('Trying to reconnect in ' + reconnectDelay + 'ms');
|
|
@@ -19910,9 +20249,9 @@ class IndexMap {
|
|
|
19910
20249
|
newIndex = fallbackObject;
|
|
19911
20250
|
}
|
|
19912
20251
|
const indexName = indexDefinition.toString();
|
|
19913
|
-
const newIndexSet =
|
|
20252
|
+
const newIndexSet = { ...this.indexSet_ };
|
|
19914
20253
|
newIndexSet[indexName] = indexDefinition;
|
|
19915
|
-
const newIndexes =
|
|
20254
|
+
const newIndexes = { ...this.indexes_ };
|
|
19916
20255
|
newIndexes[indexName] = newIndex;
|
|
19917
20256
|
return new IndexMap(newIndexes, newIndexSet);
|
|
19918
20257
|
}
|
|
@@ -21641,7 +21980,7 @@ class StatsListener {
|
|
|
21641
21980
|
}
|
|
21642
21981
|
get() {
|
|
21643
21982
|
const newStats = this.collection_.get();
|
|
21644
|
-
const delta =
|
|
21983
|
+
const delta = { ...newStats };
|
|
21645
21984
|
if (this.last_) {
|
|
21646
21985
|
each(this.last_, (stat, value) => {
|
|
21647
21986
|
delta[stat] = delta[stat] - value;
|
|
@@ -27125,10 +27464,13 @@ let useRestClient = false;
|
|
|
27125
27464
|
/**
|
|
27126
27465
|
* Update an existing `Repo` in place to point to a new host/port.
|
|
27127
27466
|
*/
|
|
27128
|
-
function repoManagerApplyEmulatorSettings(repo,
|
|
27129
|
-
|
|
27130
|
-
|
|
27131
|
-
|
|
27467
|
+
function repoManagerApplyEmulatorSettings(repo, hostAndPort, emulatorOptions, tokenProvider) {
|
|
27468
|
+
const portIndex = hostAndPort.lastIndexOf(':');
|
|
27469
|
+
const host = hostAndPort.substring(0, portIndex);
|
|
27470
|
+
const useSsl = isCloudWorkstation(host);
|
|
27471
|
+
repo.repoInfo_ = new RepoInfo(hostAndPort,
|
|
27472
|
+
/* secure= */ useSsl, repo.repoInfo_.namespace, repo.repoInfo_.webSocketOnly, repo.repoInfo_.nodeAdmin, repo.repoInfo_.persistenceKey, repo.repoInfo_.includeNamespaceInQueryParams,
|
|
27473
|
+
/*isUsingEmulator=*/ true, emulatorOptions);
|
|
27132
27474
|
if (tokenProvider) {
|
|
27133
27475
|
repo.authTokenProvider_ = tokenProvider;
|
|
27134
27476
|
}
|
|
@@ -27167,7 +27509,7 @@ function repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url, no
|
|
|
27167
27509
|
fatal('Database URL must point to the root of a Firebase Database ' +
|
|
27168
27510
|
'(not including a child path).');
|
|
27169
27511
|
}
|
|
27170
|
-
const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app
|
|
27512
|
+
const repo = repoManagerCreateRepo(repoInfo, app, authTokenProvider, new AppCheckTokenProvider(app, appCheckProvider));
|
|
27171
27513
|
return new Database(repo, app);
|
|
27172
27514
|
}
|
|
27173
27515
|
/**
|
|
@@ -27283,10 +27625,17 @@ function getDatabase(app = getApp(), url) {
|
|
|
27283
27625
|
function connectDatabaseEmulator(db, host, port, options = {}) {
|
|
27284
27626
|
db = getModularInstance(db);
|
|
27285
27627
|
db._checkNotDeleted('useEmulator');
|
|
27628
|
+
const hostAndPort = `${host}:${port}`;
|
|
27629
|
+
const repo = db._repoInternal;
|
|
27286
27630
|
if (db._instanceStarted) {
|
|
27287
|
-
|
|
27631
|
+
// If the instance has already been started, then silenty fail if this function is called again
|
|
27632
|
+
// with the same parameters. If the parameters differ then assert.
|
|
27633
|
+
if (hostAndPort === db._repoInternal.repoInfo_.host &&
|
|
27634
|
+
deepEqual(options, repo.repoInfo_.emulatorOptions)) {
|
|
27635
|
+
return;
|
|
27636
|
+
}
|
|
27637
|
+
fatal('connectDatabaseEmulator() cannot initialize or alter the emulator configuration after the database instance has started.');
|
|
27288
27638
|
}
|
|
27289
|
-
const repo = db._repoInternal;
|
|
27290
27639
|
let tokenProvider = undefined;
|
|
27291
27640
|
if (repo.repoInfo_.nodeAdmin) {
|
|
27292
27641
|
if (options.mockUserToken) {
|
|
@@ -27300,8 +27649,13 @@ function connectDatabaseEmulator(db, host, port, options = {}) {
|
|
|
27300
27649
|
: createMockUserToken(options.mockUserToken, db.app.options.projectId);
|
|
27301
27650
|
tokenProvider = new EmulatorTokenProvider(token);
|
|
27302
27651
|
}
|
|
27652
|
+
// Workaround to get cookies in Firebase Studio
|
|
27653
|
+
if (isCloudWorkstation(host)) {
|
|
27654
|
+
void pingServer(host);
|
|
27655
|
+
updateEmulatorBanner('Database', true);
|
|
27656
|
+
}
|
|
27303
27657
|
// Modify the repo to apply emulator settings
|
|
27304
|
-
repoManagerApplyEmulatorSettings(repo,
|
|
27658
|
+
repoManagerApplyEmulatorSettings(repo, hostAndPort, options, tokenProvider);
|
|
27305
27659
|
}
|
|
27306
27660
|
|
|
27307
27661
|
/**
|
|
@@ -27329,8 +27683,8 @@ function registerDatabase(variant) {
|
|
|
27329
27683
|
return repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url);
|
|
27330
27684
|
}, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
|
27331
27685
|
registerVersion(name, version, variant);
|
|
27332
|
-
// BUILD_TARGET will be replaced by values like
|
|
27333
|
-
registerVersion(name, version, '
|
|
27686
|
+
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
|
27687
|
+
registerVersion(name, version, 'esm2020');
|
|
27334
27688
|
}
|
|
27335
27689
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27336
27690
|
PersistentConnection.prototype.simpleListen = function (pathString, onComplete) {
|
|
@@ -27372,12 +27726,11 @@ let databaseReadyQueue = [];
|
|
|
27372
27726
|
* Execute all queued callbacks now that database is ready
|
|
27373
27727
|
*/
|
|
27374
27728
|
function flushDatabaseReadyQueue(state) {
|
|
27375
|
-
var _a;
|
|
27376
27729
|
const queue = [...databaseReadyQueue];
|
|
27377
27730
|
databaseReadyQueue = []; // Clear queue before executing to avoid re-queuing
|
|
27378
27731
|
queue.forEach(callback => callback());
|
|
27379
27732
|
// Also notify the global callback if set
|
|
27380
|
-
|
|
27733
|
+
state.onFirebaseReady?.();
|
|
27381
27734
|
}
|
|
27382
27735
|
function initFirebase(state) {
|
|
27383
27736
|
try {
|
|
@@ -27441,7 +27794,7 @@ function isFirebaseAuthenticated(state) {
|
|
|
27441
27794
|
async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
27442
27795
|
if (!app && !initFirebase(state))
|
|
27443
27796
|
return;
|
|
27444
|
-
const {
|
|
27797
|
+
const { netskraflUserId, locale, projectId, loginMethod } = state;
|
|
27445
27798
|
auth = getAuth(app);
|
|
27446
27799
|
if (!auth) {
|
|
27447
27800
|
console.error("Failed to initialize Firebase Auth");
|
|
@@ -27456,14 +27809,14 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
|
27456
27809
|
logEvent("sign_up", {
|
|
27457
27810
|
locale,
|
|
27458
27811
|
method: loginMethod,
|
|
27459
|
-
userid:
|
|
27812
|
+
userid: netskraflUserId
|
|
27460
27813
|
});
|
|
27461
27814
|
}
|
|
27462
27815
|
// And always log a login event
|
|
27463
27816
|
logEvent("login", {
|
|
27464
27817
|
locale,
|
|
27465
27818
|
method: loginMethod,
|
|
27466
|
-
userid:
|
|
27819
|
+
userid: netskraflUserId
|
|
27467
27820
|
});
|
|
27468
27821
|
}
|
|
27469
27822
|
});
|
|
@@ -27482,7 +27835,7 @@ async function loginFirebase(state, firebaseToken, onLoginFunc) {
|
|
|
27482
27835
|
if (!analytics) {
|
|
27483
27836
|
console.error("Failed to initialize Firebase Analytics");
|
|
27484
27837
|
}
|
|
27485
|
-
initPresence(projectId,
|
|
27838
|
+
initPresence(projectId, netskraflUserId, locale);
|
|
27486
27839
|
}
|
|
27487
27840
|
function initPresence(projectId, userId, locale) {
|
|
27488
27841
|
// Ensure that this user connection is recorded in Firebase
|
|
@@ -27645,7 +27998,6 @@ const checkCircuitBreakerReset = () => {
|
|
|
27645
27998
|
* - Request deduplication: Only one login attempt at a time
|
|
27646
27999
|
*/
|
|
27647
28000
|
const ensureAuthenticated = async (state) => {
|
|
27648
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
27649
28001
|
// Check and potentially reset circuit breaker
|
|
27650
28002
|
checkCircuitBreakerReset();
|
|
27651
28003
|
// Circuit breaker check - fail fast if too many recent failures
|
|
@@ -27710,22 +28062,22 @@ const ensureAuthenticated = async (state) => {
|
|
|
27710
28062
|
// Success! Update global state and persist authentication
|
|
27711
28063
|
// ========================================================================
|
|
27712
28064
|
// Update the user's ID to the internal one used by the backend and Firebase
|
|
27713
|
-
state.
|
|
27714
|
-
state.account = result.account || state.
|
|
28065
|
+
state.netskraflUserId = result.user_id || state.netskraflUserId;
|
|
28066
|
+
state.account = result.account || state.netskraflUserId;
|
|
27715
28067
|
state.userNick = result.nickname || state.userNick;
|
|
27716
28068
|
state.firebaseApiKey = result.firebase_api_key || state.firebaseApiKey;
|
|
27717
28069
|
// Load state flags and preferences
|
|
27718
|
-
state.beginner =
|
|
27719
|
-
state.fairPlay =
|
|
27720
|
-
state.ready =
|
|
27721
|
-
state.readyTimed =
|
|
27722
|
-
state.audio =
|
|
27723
|
-
state.fanfare =
|
|
27724
|
-
state.hasPaid =
|
|
28070
|
+
state.beginner = result.prefs?.beginner ?? true;
|
|
28071
|
+
state.fairPlay = result.prefs?.fairplay ?? false;
|
|
28072
|
+
state.ready = result.prefs?.ready ?? true;
|
|
28073
|
+
state.readyTimed = result.prefs?.ready_timed ?? true;
|
|
28074
|
+
state.audio = result.prefs?.audio ?? false;
|
|
28075
|
+
state.fanfare = result.prefs?.fanfare ?? false;
|
|
28076
|
+
state.hasPaid = result.prefs?.haspaid ?? false;
|
|
27725
28077
|
// Save authentication settings to sessionStorage
|
|
27726
28078
|
saveAuthSettings({
|
|
27727
28079
|
userEmail: state.userEmail,
|
|
27728
|
-
|
|
28080
|
+
netskraflUserId: state.netskraflUserId,
|
|
27729
28081
|
userNick: state.userNick,
|
|
27730
28082
|
firebaseApiKey: state.firebaseApiKey,
|
|
27731
28083
|
beginner: state.beginner,
|
|
@@ -27823,7 +28175,7 @@ const requestMoves = (state, options) => {
|
|
|
27823
28175
|
const headers = {
|
|
27824
28176
|
"Content-Type": "application/json; charset=UTF-8",
|
|
27825
28177
|
Authorization: `Bearer ${state.movesAccessKey}`,
|
|
27826
|
-
...options
|
|
28178
|
+
...options?.headers,
|
|
27827
28179
|
};
|
|
27828
28180
|
return m.request({
|
|
27829
28181
|
withCredentials: false,
|
|
@@ -28059,10 +28411,9 @@ const OnlinePresence = {
|
|
|
28059
28411
|
const UserId = {
|
|
28060
28412
|
// User identifier at top right, opens user preferences
|
|
28061
28413
|
view: (vnode) => {
|
|
28062
|
-
var _a;
|
|
28063
28414
|
const view = vnode.attrs.view;
|
|
28064
28415
|
const model = view.model;
|
|
28065
|
-
if (!
|
|
28416
|
+
if (!model.state?.netskraflUserId)
|
|
28066
28417
|
// Don't show the button if there is no logged-in user
|
|
28067
28418
|
return "";
|
|
28068
28419
|
return m(".userid", {
|
|
@@ -28075,13 +28426,25 @@ const UserId = {
|
|
|
28075
28426
|
}, [glyph("address-book"), nbsp(), model.state.userNick]);
|
|
28076
28427
|
}
|
|
28077
28428
|
};
|
|
28429
|
+
const ProModeIndicator = {
|
|
28430
|
+
// Display a Pro Mode (Keppnishamur) indicator using a lightbulb glyph
|
|
28431
|
+
// If header is true, always show grayed with title
|
|
28432
|
+
// Otherwise, the lightbulb is grayed if manual mode is not enabled
|
|
28433
|
+
view: (vnode) => {
|
|
28434
|
+
const { manual, header } = vnode.attrs;
|
|
28435
|
+
const title = ts("Keppnishamur");
|
|
28436
|
+
// Regular case: show grayed if not manual mode
|
|
28437
|
+
const highlight = (manual === true) && !header;
|
|
28438
|
+
const glyphAttrs = (highlight || header) ? { title } : {};
|
|
28439
|
+
return m("span.list-manual", glyphAttrs, glyph("lightbulb", undefined, !highlight));
|
|
28440
|
+
}
|
|
28441
|
+
};
|
|
28078
28442
|
const MultiSelection = (initialVnode) => {
|
|
28079
28443
|
// A multiple-selection div where users can click on child nodes
|
|
28080
28444
|
// to select them, giving them an addional selection class,
|
|
28081
28445
|
// typically .selected
|
|
28082
|
-
|
|
28083
|
-
|
|
28084
|
-
const defaultClass = (_b = initialVnode.attrs.defaultClass) !== null && _b !== void 0 ? _b : "";
|
|
28446
|
+
let sel = initialVnode.attrs.initialSelection ?? 0;
|
|
28447
|
+
const defaultClass = initialVnode.attrs.defaultClass ?? "";
|
|
28085
28448
|
const selectedClass = initialVnode.attrs.selectedClass || "selected";
|
|
28086
28449
|
return {
|
|
28087
28450
|
view: (vnode) => {
|
|
@@ -28128,10 +28491,9 @@ const HeaderLogo = {
|
|
|
28128
28491
|
};
|
|
28129
28492
|
const Toggler = () => {
|
|
28130
28493
|
function doToggle(togglerId, funcToggle) {
|
|
28131
|
-
var _a, _b;
|
|
28132
28494
|
// Perform the toggling, on a mouse click or keyboard input (space bar)
|
|
28133
|
-
const cls1 =
|
|
28134
|
-
const cls2 =
|
|
28495
|
+
const cls1 = document.querySelector("#" + togglerId + " #opt1")?.classList;
|
|
28496
|
+
const cls2 = document.querySelector("#" + togglerId + " #opt2")?.classList;
|
|
28135
28497
|
cls1 && cls1.toggle("selected");
|
|
28136
28498
|
cls2 && cls2.toggle("selected");
|
|
28137
28499
|
if (funcToggle !== undefined && cls2)
|
|
@@ -28185,19 +28547,16 @@ const TogglerReady = (initialVnode) => {
|
|
|
28185
28547
|
}
|
|
28186
28548
|
}
|
|
28187
28549
|
return {
|
|
28188
|
-
view: () => {
|
|
28189
|
-
|
|
28190
|
-
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
|
|
28197
|
-
|
|
28198
|
-
title: ts("Tek við áskorunum!"),
|
|
28199
|
-
});
|
|
28200
|
-
}
|
|
28550
|
+
view: () => m(Toggler, {
|
|
28551
|
+
id: "ready",
|
|
28552
|
+
state: model.state?.ready ?? false,
|
|
28553
|
+
tabindex: 2,
|
|
28554
|
+
opt1: nbsp(),
|
|
28555
|
+
opt2: glyph("thumbs-up"),
|
|
28556
|
+
funcToggle: toggleFunc,
|
|
28557
|
+
small: true,
|
|
28558
|
+
title: ts("Tek við áskorunum!"),
|
|
28559
|
+
})
|
|
28201
28560
|
};
|
|
28202
28561
|
};
|
|
28203
28562
|
const TogglerReadyTimed = (initialVnode) => {
|
|
@@ -28212,19 +28571,16 @@ const TogglerReadyTimed = (initialVnode) => {
|
|
|
28212
28571
|
}
|
|
28213
28572
|
}
|
|
28214
28573
|
return {
|
|
28215
|
-
view: () => {
|
|
28216
|
-
|
|
28217
|
-
|
|
28218
|
-
|
|
28219
|
-
|
|
28220
|
-
|
|
28221
|
-
|
|
28222
|
-
|
|
28223
|
-
|
|
28224
|
-
|
|
28225
|
-
title: ts("Til í viðureign með klukku!"),
|
|
28226
|
-
});
|
|
28227
|
-
}
|
|
28574
|
+
view: () => m(Toggler, {
|
|
28575
|
+
id: "timed",
|
|
28576
|
+
state: model.state?.readyTimed ?? false,
|
|
28577
|
+
tabindex: 3,
|
|
28578
|
+
opt1: nbsp(),
|
|
28579
|
+
opt2: glyph("time"),
|
|
28580
|
+
funcToggle: toggleFunc,
|
|
28581
|
+
small: true,
|
|
28582
|
+
title: ts("Til í viðureign með klukku!"),
|
|
28583
|
+
})
|
|
28228
28584
|
};
|
|
28229
28585
|
};
|
|
28230
28586
|
const TogglerAudio = (initialVnode) => {
|
|
@@ -28319,7 +28675,6 @@ const WaitDialog = {
|
|
|
28319
28675
|
state.firebasePath = "";
|
|
28320
28676
|
},
|
|
28321
28677
|
oncreate: (vnode) => {
|
|
28322
|
-
var _a, _b;
|
|
28323
28678
|
// Note: we must use a two-step initialization here,
|
|
28324
28679
|
// calling oninit() first and then oncreate(), since calls to
|
|
28325
28680
|
// m.redraw() - explicit or implicit via m.request() - from within
|
|
@@ -28329,7 +28684,7 @@ const WaitDialog = {
|
|
|
28329
28684
|
const { view, oppId, challengeKey: key } = attrs;
|
|
28330
28685
|
const { model } = view;
|
|
28331
28686
|
const globalState = model.state;
|
|
28332
|
-
const userId =
|
|
28687
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
28333
28688
|
if (!userId || !oppId)
|
|
28334
28689
|
return; // Should not happen
|
|
28335
28690
|
state.firebasePath = `user/${userId}/wait/${oppId}`;
|
|
@@ -28368,12 +28723,11 @@ const WaitDialog = {
|
|
|
28368
28723
|
});
|
|
28369
28724
|
},
|
|
28370
28725
|
view: (vnode) => {
|
|
28371
|
-
var _a, _b;
|
|
28372
28726
|
const { attrs, state } = vnode;
|
|
28373
28727
|
const { view, oppId, duration, challengeKey: key, oppNick, oppName } = attrs;
|
|
28374
28728
|
const { model } = view;
|
|
28375
28729
|
const globalState = model.state;
|
|
28376
|
-
const userId =
|
|
28730
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
28377
28731
|
if (!globalState)
|
|
28378
28732
|
return null;
|
|
28379
28733
|
const cancelWait = async () => {
|
|
@@ -28455,7 +28809,7 @@ const AcceptDialog = {
|
|
|
28455
28809
|
url: "/waitcheck",
|
|
28456
28810
|
body: { user: oppId, key }
|
|
28457
28811
|
});
|
|
28458
|
-
if (json
|
|
28812
|
+
if (json?.waiting) {
|
|
28459
28813
|
// Both players are now ready: Start the timed game.
|
|
28460
28814
|
// The newGame() call switches to a new route (/game),
|
|
28461
28815
|
// and all open dialogs are thereby closed automatically.
|
|
@@ -28518,12 +28872,11 @@ class WordChecker {
|
|
|
28518
28872
|
this.wordCheckCache = {};
|
|
28519
28873
|
}
|
|
28520
28874
|
ingestTwoLetterWords(locale, twoLetterWords) {
|
|
28521
|
-
var _a;
|
|
28522
28875
|
// Initialize the cache with a list of known two-letter words
|
|
28523
28876
|
// The two-letter word list contains a list of entries where each
|
|
28524
28877
|
// entry is an initial letter followed by a list of two-letter words
|
|
28525
28878
|
// starting with that letter.
|
|
28526
|
-
const cache =
|
|
28879
|
+
const cache = this.wordCheckCache[locale] ?? (this.wordCheckCache[locale] = {});
|
|
28527
28880
|
for (const [/* firstLetter */ , wordList] of twoLetterWords) {
|
|
28528
28881
|
for (const word of wordList) {
|
|
28529
28882
|
cache[word] = true;
|
|
@@ -28566,7 +28919,7 @@ class WordChecker {
|
|
|
28566
28919
|
words,
|
|
28567
28920
|
}
|
|
28568
28921
|
});
|
|
28569
|
-
if (
|
|
28922
|
+
if (response?.word === words[0] && response?.valid) {
|
|
28570
28923
|
// Looks like a valid response
|
|
28571
28924
|
if (!cache) {
|
|
28572
28925
|
// We didn't already have a cache for this locale
|
|
@@ -29209,7 +29562,6 @@ class BaseGame {
|
|
|
29209
29562
|
return c ? this.tiles[c] || null : null;
|
|
29210
29563
|
}
|
|
29211
29564
|
saveTiles() {
|
|
29212
|
-
var _a;
|
|
29213
29565
|
// Save the current unglued tile configuration to local storage
|
|
29214
29566
|
let tp = [];
|
|
29215
29567
|
const tilesPlaced = this.tilesPlaced();
|
|
@@ -29224,7 +29576,7 @@ class BaseGame {
|
|
|
29224
29576
|
if (sq in this.tiles)
|
|
29225
29577
|
tp.push({ sq, tile: this.tiles[sq].tile });
|
|
29226
29578
|
}
|
|
29227
|
-
|
|
29579
|
+
this.localStorage?.saveTiles(tp);
|
|
29228
29580
|
}
|
|
29229
29581
|
;
|
|
29230
29582
|
restoreTiles(savedTiles) {
|
|
@@ -29328,7 +29680,6 @@ const DEBUG_OVERTIME = 1 * 60.0;
|
|
|
29328
29680
|
const GAME_OVER = 99; // Error code corresponding to the Error class in skraflmechanics.py
|
|
29329
29681
|
class Game extends BaseGame {
|
|
29330
29682
|
constructor(uuid, srvGame, moveListener, state, maxOvertime) {
|
|
29331
|
-
var _a;
|
|
29332
29683
|
// Call parent constructor
|
|
29333
29684
|
super(uuid, state); // Default board_type: "standard"
|
|
29334
29685
|
// A class that represents a Netskrafl game instance on the client
|
|
@@ -29391,7 +29742,7 @@ class Game extends BaseGame {
|
|
|
29391
29742
|
return;
|
|
29392
29743
|
}
|
|
29393
29744
|
// Load previously saved tile positions from local storage, if any
|
|
29394
|
-
let savedTiles =
|
|
29745
|
+
let savedTiles = this.localStorage?.loadTiles() || [];
|
|
29395
29746
|
this.init(srvGame);
|
|
29396
29747
|
// Put tiles in the same position as they were when the player left the game
|
|
29397
29748
|
this.restoreTiles(savedTiles);
|
|
@@ -29597,7 +29948,7 @@ class Game extends BaseGame {
|
|
|
29597
29948
|
url: "/gamestate",
|
|
29598
29949
|
body: { game: this.uuid } // !!! FIXME: Add delete_zombie parameter
|
|
29599
29950
|
});
|
|
29600
|
-
if (!
|
|
29951
|
+
if (!result?.ok) {
|
|
29601
29952
|
// console.log("Game " + uuid + " could not be loaded");
|
|
29602
29953
|
}
|
|
29603
29954
|
else {
|
|
@@ -29941,8 +30292,7 @@ class Game extends BaseGame {
|
|
|
29941
30292
|
this.rack = rack;
|
|
29942
30293
|
}
|
|
29943
30294
|
rackAtMove(moveIndex) {
|
|
29944
|
-
|
|
29945
|
-
const numRacks = ((_a = this.racks) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
30295
|
+
const numRacks = this.racks?.length || 0;
|
|
29946
30296
|
if (!numRacks)
|
|
29947
30297
|
return "";
|
|
29948
30298
|
return this.racks[moveIndex >= numRacks ? numRacks - 1 : moveIndex];
|
|
@@ -30425,8 +30775,8 @@ class Riddle extends BaseGame {
|
|
|
30425
30775
|
// Initialize word checker
|
|
30426
30776
|
wordChecker.ingestTwoLetterWords(this.locale, this.two_letter_words[0]);
|
|
30427
30777
|
// Load persisted player moves from localStorage
|
|
30428
|
-
if (state.
|
|
30429
|
-
const persistedMoves = RiddlePersistence.loadLocalMoves(state.
|
|
30778
|
+
if (state.netskraflUserId) {
|
|
30779
|
+
const persistedMoves = RiddlePersistence.loadLocalMoves(state.netskraflUserId, date);
|
|
30430
30780
|
if (persistedMoves.length > 0) {
|
|
30431
30781
|
// Convert from IPlayerMove to RiddleWord format, preserving timestamps
|
|
30432
30782
|
this.playerMoves = persistedMoves.map(move => ({
|
|
@@ -30449,7 +30799,7 @@ class Riddle extends BaseGame {
|
|
|
30449
30799
|
}
|
|
30450
30800
|
async submitRiddleWord(move) {
|
|
30451
30801
|
const { state } = this;
|
|
30452
|
-
if (!state || !state.
|
|
30802
|
+
if (!state || !state.netskraflUserId)
|
|
30453
30803
|
return;
|
|
30454
30804
|
try {
|
|
30455
30805
|
await request(state, {
|
|
@@ -30458,9 +30808,9 @@ class Riddle extends BaseGame {
|
|
|
30458
30808
|
body: {
|
|
30459
30809
|
date: this.date,
|
|
30460
30810
|
locale: this.locale,
|
|
30461
|
-
userId: state.
|
|
30811
|
+
userId: state.netskraflUserId,
|
|
30462
30812
|
groupId: state.userGroupId || null,
|
|
30463
|
-
userDisplayName: state.userFullname || state.userNick ||
|
|
30813
|
+
userDisplayName: state.userFullname || state.userNick || "",
|
|
30464
30814
|
move,
|
|
30465
30815
|
}
|
|
30466
30816
|
});
|
|
@@ -30510,7 +30860,7 @@ class Riddle extends BaseGame {
|
|
|
30510
30860
|
if (!move)
|
|
30511
30861
|
return;
|
|
30512
30862
|
const { state } = this;
|
|
30513
|
-
if (!state || !state.
|
|
30863
|
+
if (!state || !state.netskraflUserId)
|
|
30514
30864
|
return;
|
|
30515
30865
|
// Save all moves to localStorage (local backup/cache)
|
|
30516
30866
|
// Convert RiddleWord[] to IPlayerMove[] for persistence
|
|
@@ -30520,7 +30870,7 @@ class Riddle extends BaseGame {
|
|
|
30520
30870
|
coord: m.coord,
|
|
30521
30871
|
timestamp: m.timestamp
|
|
30522
30872
|
}));
|
|
30523
|
-
RiddlePersistence.saveLocalMoves(state.
|
|
30873
|
+
RiddlePersistence.saveLocalMoves(state.netskraflUserId, this.date, movesToSave);
|
|
30524
30874
|
// If the move does not improve the personal best, we're done
|
|
30525
30875
|
if (move.score <= this.personalBestScore)
|
|
30526
30876
|
return;
|
|
@@ -30850,7 +31200,6 @@ class Model {
|
|
|
30850
31200
|
});
|
|
30851
31201
|
}
|
|
30852
31202
|
async loadGame(uuid, funcComplete, deleteZombie = false) {
|
|
30853
|
-
var _a;
|
|
30854
31203
|
// Fetch a Netskrafl game state from the server, given the game's UUID.
|
|
30855
31204
|
// If deleteZombie is true, we are loading a zombie game for
|
|
30856
31205
|
// inspection, so we tell the server to remove the zombie marker.
|
|
@@ -30870,12 +31219,12 @@ class Model {
|
|
|
30870
31219
|
game: uuid,
|
|
30871
31220
|
delete_zombie: deleteZombie
|
|
30872
31221
|
});
|
|
30873
|
-
if (!
|
|
31222
|
+
if (!result?.ok) {
|
|
30874
31223
|
// console.log("Game " + uuid + " could not be loaded");
|
|
30875
31224
|
}
|
|
30876
31225
|
else {
|
|
30877
31226
|
// Create a new game instance and load the state into it
|
|
30878
|
-
this.game = new Game(uuid, result.game, this, this.state,
|
|
31227
|
+
this.game = new Game(uuid, result.game, this, this.state, this.state?.runningLocal ? DEBUG_OVERTIME : MAX_OVERTIME);
|
|
30879
31228
|
// Successfully loaded: call the completion function, if given
|
|
30880
31229
|
// (this usually attaches the Firebase event listener)
|
|
30881
31230
|
funcComplete && funcComplete();
|
|
@@ -31182,13 +31531,12 @@ class Model {
|
|
|
31182
31531
|
}
|
|
31183
31532
|
}
|
|
31184
31533
|
async loadHelp() {
|
|
31185
|
-
var _a;
|
|
31186
31534
|
// Load the help screen HTML from the server
|
|
31187
31535
|
// (this is done the first time the help is displayed)
|
|
31188
31536
|
if (this.helpHTML !== null)
|
|
31189
31537
|
return; // Already loaded
|
|
31190
31538
|
try {
|
|
31191
|
-
const locale =
|
|
31539
|
+
const locale = this.state?.locale || "is_IS";
|
|
31192
31540
|
const result = await this.getText("/rawhelp?version=malstadur&locale=" + locale);
|
|
31193
31541
|
this.helpHTML = result;
|
|
31194
31542
|
}
|
|
@@ -31233,7 +31581,7 @@ class Model {
|
|
|
31233
31581
|
return;
|
|
31234
31582
|
try {
|
|
31235
31583
|
const result = await this.post("/saveuserprefs", user);
|
|
31236
|
-
if (result
|
|
31584
|
+
if (result?.ok) {
|
|
31237
31585
|
// User preferences modified successfully on the server:
|
|
31238
31586
|
// update the state variables that we're caching
|
|
31239
31587
|
const state = this.state;
|
|
@@ -31265,10 +31613,9 @@ class Model {
|
|
|
31265
31613
|
}
|
|
31266
31614
|
}
|
|
31267
31615
|
addChatMessage(game, from_userid, msg, ts) {
|
|
31268
|
-
var _a, _b;
|
|
31269
31616
|
// Add a chat message to the game's chat message list
|
|
31270
31617
|
if (this.game && this.game.uuid == game) {
|
|
31271
|
-
const userId =
|
|
31618
|
+
const userId = this.state?.netskraflUserId ?? "";
|
|
31272
31619
|
this.game.addChatMessage(from_userid, msg, ts, from_userid == userId);
|
|
31273
31620
|
// Returning true triggers a redraw
|
|
31274
31621
|
return true;
|
|
@@ -31328,10 +31675,9 @@ class Model {
|
|
|
31328
31675
|
}
|
|
31329
31676
|
}
|
|
31330
31677
|
notifyGameWon() {
|
|
31331
|
-
var _a;
|
|
31332
31678
|
// The user just won a game:
|
|
31333
31679
|
// play the "you-win" audio if fanfare is enabled
|
|
31334
|
-
if (
|
|
31680
|
+
if (this.user?.fanfare) {
|
|
31335
31681
|
playAudio(this.state, "you-win");
|
|
31336
31682
|
}
|
|
31337
31683
|
}
|
|
@@ -31422,7 +31768,7 @@ const FriendPromoteDialog = (initialVnode) => {
|
|
|
31422
31768
|
onclick: (ev) => {
|
|
31423
31769
|
ev.preventDefault();
|
|
31424
31770
|
logEvent("click_friend", {
|
|
31425
|
-
userid: state.
|
|
31771
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
31426
31772
|
});
|
|
31427
31773
|
// Navigate to the container-supplied subscription URL
|
|
31428
31774
|
window.location.href = state.subscriptionUrl;
|
|
@@ -31490,7 +31836,7 @@ const ChallengeDialog = () => {
|
|
|
31490
31836
|
m("div", { id: 'chall-15', tabindex: 3 }, [glyph("time"), t("2 x 15 mínútur")]),
|
|
31491
31837
|
m("div", { id: 'chall-20', tabindex: 4 }, [glyph("time"), t("2 x 20 mínútur")]),
|
|
31492
31838
|
m("div", { id: 'chall-25', tabindex: 5 }, [glyph("time"), t("2 x 25 mínútur")]),
|
|
31493
|
-
|
|
31839
|
+
state?.runningLocal ? // !!! TODO Debugging aid
|
|
31494
31840
|
m("div", { id: 'chall-3', tabindex: 6 }, [glyph("time"), t("2 x 3 mínútur")])
|
|
31495
31841
|
:
|
|
31496
31842
|
m("div", { id: 'chall-30', tabindex: 6 }, [glyph("time"), t("2 x 30 mínútur")])
|
|
@@ -31536,9 +31882,8 @@ const ChallengeDialog = () => {
|
|
|
31536
31882
|
title: ts("Skora á"),
|
|
31537
31883
|
tabindex: 9,
|
|
31538
31884
|
onclick: (ev) => {
|
|
31539
|
-
var _a, _b;
|
|
31540
31885
|
// Issue a new challenge
|
|
31541
|
-
let duration =
|
|
31886
|
+
let duration = document.querySelector("div.chall-time.selected")?.id.slice(6) ?? "0";
|
|
31542
31887
|
if (duration === "none")
|
|
31543
31888
|
duration = 0;
|
|
31544
31889
|
else
|
|
@@ -31581,7 +31926,7 @@ const RecentList = () => {
|
|
|
31581
31926
|
function durationDescription() {
|
|
31582
31927
|
// Format the game duration
|
|
31583
31928
|
let duration = "";
|
|
31584
|
-
if (!item.duration) {
|
|
31929
|
+
if (!item.prefs?.duration) {
|
|
31585
31930
|
// Regular (non-timed) game
|
|
31586
31931
|
if (item.days || item.hours || item.minutes) {
|
|
31587
31932
|
if (item.days > 1)
|
|
@@ -31610,7 +31955,7 @@ const RecentList = () => {
|
|
|
31610
31955
|
// This was a timed game
|
|
31611
31956
|
duration = [
|
|
31612
31957
|
m("span.timed-btn", { title: ts('Viðureign með klukku') }),
|
|
31613
|
-
" 2 x " + item.duration + ts(" mínútur")
|
|
31958
|
+
" 2 x " + item.prefs.duration + ts(" mínútur")
|
|
31614
31959
|
];
|
|
31615
31960
|
}
|
|
31616
31961
|
return duration;
|
|
@@ -31660,7 +32005,7 @@ const RecentList = () => {
|
|
|
31660
32005
|
m("div.list-elo-adj", eloAdjHuman),
|
|
31661
32006
|
m("div.list-elo-adj", eloAdj),
|
|
31662
32007
|
m("span.list-duration", durationDescription()),
|
|
31663
|
-
m(
|
|
32008
|
+
m(ProModeIndicator, { manual: item.prefs?.manual })
|
|
31664
32009
|
]));
|
|
31665
32010
|
}
|
|
31666
32011
|
return {
|
|
@@ -31671,6 +32016,295 @@ const RecentList = () => {
|
|
|
31671
32016
|
};
|
|
31672
32017
|
};
|
|
31673
32018
|
|
|
32019
|
+
/*
|
|
32020
|
+
|
|
32021
|
+
Gamelist.ts
|
|
32022
|
+
|
|
32023
|
+
List of games in progress display component
|
|
32024
|
+
|
|
32025
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32026
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32027
|
+
|
|
32028
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32029
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32030
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32031
|
+
|
|
32032
|
+
*/
|
|
32033
|
+
const Hint = {
|
|
32034
|
+
view: (vnode) => {
|
|
32035
|
+
const { loadingGameList, loadingAllLists, gameList } = vnode.attrs;
|
|
32036
|
+
// Show some help if the user has no games in progress
|
|
32037
|
+
if (loadingGameList || loadingAllLists || (gameList !== null && gameList.length > 0))
|
|
32038
|
+
// Either we have games in progress or the game list is being loaded
|
|
32039
|
+
return "";
|
|
32040
|
+
return m(".hint", { style: { display: "block" } }, [
|
|
32041
|
+
m("p", [
|
|
32042
|
+
"Ef þig vantar einhvern til að skrafla við, veldu flipann ",
|
|
32043
|
+
m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
|
|
32044
|
+
" og skoraðu á tölvuþjarka - ",
|
|
32045
|
+
glyph("cog"), nbsp(), m("b", "Amlóða"),
|
|
32046
|
+
", ",
|
|
32047
|
+
glyph("cog"), nbsp(), m("b", "Miðlung"),
|
|
32048
|
+
" eða ",
|
|
32049
|
+
glyph("cog"), nbsp(), m("b", "Fullsterkan"),
|
|
32050
|
+
" - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
|
|
32051
|
+
" sem þar er að finna til að skora á."
|
|
32052
|
+
]),
|
|
32053
|
+
m("p", [
|
|
32054
|
+
"Þú stofnar áskorun með því að smella á bendi-teiknið ",
|
|
32055
|
+
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
32056
|
+
" vinstra megin við nafn andstæðingsins."
|
|
32057
|
+
]),
|
|
32058
|
+
m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
|
|
32059
|
+
" hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
|
|
32060
|
+
m("p.no-mobile-block", [
|
|
32061
|
+
m(m.route.Link, { href: "/help" }, "Hjálp"),
|
|
32062
|
+
" má fá með því að smella á bláa ",
|
|
32063
|
+
glyph("info-sign"), nbsp(), "-", nbsp(),
|
|
32064
|
+
"teiknið hér til vinstri."
|
|
32065
|
+
]),
|
|
32066
|
+
m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
|
|
32067
|
+
"örvarmerkið efst vinstra megin við skraflborðið.")
|
|
32068
|
+
]);
|
|
32069
|
+
}
|
|
32070
|
+
};
|
|
32071
|
+
const GameListEntry = () => {
|
|
32072
|
+
// Generate a list item about a game in progress (or recently finished)
|
|
32073
|
+
function vwOpp(item) {
|
|
32074
|
+
const arg = item.oppid === null ? [glyph("cog"), nbsp(), item.opp] : item.opp;
|
|
32075
|
+
return m("span.list-opp", { title: item.fullname }, arg);
|
|
32076
|
+
}
|
|
32077
|
+
function vwTurn(item) {
|
|
32078
|
+
let turnText = "";
|
|
32079
|
+
let flagClass = "";
|
|
32080
|
+
if (item.my_turn) {
|
|
32081
|
+
turnText = ts("Þú átt leik");
|
|
32082
|
+
}
|
|
32083
|
+
else if (item.zombie) {
|
|
32084
|
+
turnText = ts("Viðureign lokið");
|
|
32085
|
+
flagClass = ".zombie";
|
|
32086
|
+
}
|
|
32087
|
+
else {
|
|
32088
|
+
// {opponent}'s move
|
|
32089
|
+
turnText = ts("opp_move", { opponent: item.opp });
|
|
32090
|
+
flagClass = ".grayed";
|
|
32091
|
+
}
|
|
32092
|
+
return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
|
|
32093
|
+
}
|
|
32094
|
+
function vwOverdue(item) {
|
|
32095
|
+
if (item.overdue)
|
|
32096
|
+
return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
|
|
32097
|
+
return glyphGrayed("hourglass");
|
|
32098
|
+
}
|
|
32099
|
+
function vwTileCount(item) {
|
|
32100
|
+
const winLose = item.sc0 < item.sc1 ? ".losing" : "";
|
|
32101
|
+
return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
|
|
32102
|
+
}
|
|
32103
|
+
return {
|
|
32104
|
+
view: (vnode) => {
|
|
32105
|
+
const { view, item, i } = vnode.attrs;
|
|
32106
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32107
|
+
m(m.route.Link, { href: gameUrl(item.url) }, [
|
|
32108
|
+
vwTurn(item),
|
|
32109
|
+
m("span.list-overdue", vwOverdue(item)),
|
|
32110
|
+
m("span.list-ts-short", item.ts),
|
|
32111
|
+
vwOpp(item)
|
|
32112
|
+
]),
|
|
32113
|
+
m(UserInfoButton, {
|
|
32114
|
+
view,
|
|
32115
|
+
item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
|
|
32116
|
+
}),
|
|
32117
|
+
m("span.list-s0", item.sc0),
|
|
32118
|
+
m("span.list-colon", ":"),
|
|
32119
|
+
m("span.list-s1", item.sc1),
|
|
32120
|
+
m("span.list-tc", vwTileCount(item)),
|
|
32121
|
+
m(ProModeIndicator, { manual: item.prefs?.manual })
|
|
32122
|
+
]);
|
|
32123
|
+
}
|
|
32124
|
+
};
|
|
32125
|
+
};
|
|
32126
|
+
const GameList = () => {
|
|
32127
|
+
// Shows a list of games in progress, obtained from view.model.gameList
|
|
32128
|
+
return {
|
|
32129
|
+
view: (vnode) => {
|
|
32130
|
+
const { view, id } = vnode.attrs;
|
|
32131
|
+
const { model } = view;
|
|
32132
|
+
const { gameList, loadingGameList, loadingAllLists } = model;
|
|
32133
|
+
// Trigger loading of game list if needed
|
|
32134
|
+
if (gameList === null)
|
|
32135
|
+
model.loadGameList();
|
|
32136
|
+
return [
|
|
32137
|
+
m(".listitem.listheader", [
|
|
32138
|
+
m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
|
|
32139
|
+
m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
|
|
32140
|
+
mt("span.list-ts-short", "Síðasti leikur"),
|
|
32141
|
+
mt("span.list-opp", "Andstæðingur"),
|
|
32142
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32143
|
+
mt("span.list-scorehdr", "Staða"),
|
|
32144
|
+
mt("span.list-tc", "Framvinda"),
|
|
32145
|
+
m(ProModeIndicator, { header: true }),
|
|
32146
|
+
]),
|
|
32147
|
+
m("div", { id }, !gameList ? "" : gameList.map((item, i) => m(GameListEntry, { view, item, i }))),
|
|
32148
|
+
m(Hint, { loadingGameList, loadingAllLists, gameList }),
|
|
32149
|
+
];
|
|
32150
|
+
}
|
|
32151
|
+
};
|
|
32152
|
+
};
|
|
32153
|
+
|
|
32154
|
+
/*
|
|
32155
|
+
|
|
32156
|
+
Challengelist.ts
|
|
32157
|
+
|
|
32158
|
+
List of challenges (received and sent) display component
|
|
32159
|
+
|
|
32160
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32161
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32162
|
+
|
|
32163
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32164
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32165
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32166
|
+
|
|
32167
|
+
*/
|
|
32168
|
+
const ChallengeListEntry = (initialVnode) => {
|
|
32169
|
+
// Generate a list item about a pending challenge (issued or received)
|
|
32170
|
+
const { view, item, i } = initialVnode.attrs;
|
|
32171
|
+
const { model } = view;
|
|
32172
|
+
function challengeDescription(json) {
|
|
32173
|
+
/* Return a human-readable string describing a challenge
|
|
32174
|
+
according to the enclosed preferences */
|
|
32175
|
+
if (!json || json.duration === undefined || json.duration === 0)
|
|
32176
|
+
/* Normal unbounded (untimed) game */
|
|
32177
|
+
return t("Venjuleg ótímabundin viðureign");
|
|
32178
|
+
return t("with_clock", { duration: json.duration.toString() });
|
|
32179
|
+
}
|
|
32180
|
+
function markChallenge(ev) {
|
|
32181
|
+
// Clicked the icon at the beginning of the line,
|
|
32182
|
+
// to decline a received challenge or retract an issued challenge
|
|
32183
|
+
ev.preventDefault();
|
|
32184
|
+
if (item.received) {
|
|
32185
|
+
view.actions.declineChallenge(item.userid, item.key);
|
|
32186
|
+
}
|
|
32187
|
+
else {
|
|
32188
|
+
view.actions.retractChallenge(item.userid, item.key);
|
|
32189
|
+
}
|
|
32190
|
+
}
|
|
32191
|
+
function clickChallenge(ev) {
|
|
32192
|
+
// Clicked the hotspot area to accept a received challenge
|
|
32193
|
+
ev.preventDefault();
|
|
32194
|
+
if (!model.moreGamesAllowed()) {
|
|
32195
|
+
// User must be a friend to be able to accept more challenges
|
|
32196
|
+
const state = model.state;
|
|
32197
|
+
if (state) {
|
|
32198
|
+
logEvent("hit_game_limit", {
|
|
32199
|
+
userid: state.netskraflUserId,
|
|
32200
|
+
locale: state.locale,
|
|
32201
|
+
limit: model.maxFreeGames
|
|
32202
|
+
});
|
|
32203
|
+
}
|
|
32204
|
+
// Promote a subscription to Netskrafl/Explo
|
|
32205
|
+
view.showFriendPromo();
|
|
32206
|
+
return;
|
|
32207
|
+
}
|
|
32208
|
+
if (item.received) {
|
|
32209
|
+
if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
|
|
32210
|
+
// Timed game: display a modal wait dialog
|
|
32211
|
+
view.pushDialog("wait", {
|
|
32212
|
+
oppId: item.userid,
|
|
32213
|
+
oppNick: item.opp,
|
|
32214
|
+
oppName: item.fullname,
|
|
32215
|
+
duration: item.prefs.duration,
|
|
32216
|
+
challengeKey: item.key,
|
|
32217
|
+
});
|
|
32218
|
+
else
|
|
32219
|
+
// Ask the server to create a new game and route to it
|
|
32220
|
+
view.actions.startNewGame(item.userid, false);
|
|
32221
|
+
}
|
|
32222
|
+
else {
|
|
32223
|
+
// Clicking on a sent challenge, i.e. a timed game
|
|
32224
|
+
// where the opponent is waiting and ready to start
|
|
32225
|
+
view.showAcceptDialog(item.userid, item.opp, item.key);
|
|
32226
|
+
}
|
|
32227
|
+
}
|
|
32228
|
+
return {
|
|
32229
|
+
view: () => {
|
|
32230
|
+
const oppReady = !item.received && item.opp_ready &&
|
|
32231
|
+
item.prefs && item.prefs.duration !== undefined &&
|
|
32232
|
+
item.prefs.duration > 0;
|
|
32233
|
+
const clickable = item.received || oppReady;
|
|
32234
|
+
const descr = challengeDescription(item.prefs);
|
|
32235
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32236
|
+
m("span.list-ch", { onclick: markChallenge }, item.received ?
|
|
32237
|
+
glyph("thumbs-down", { title: ts("Hafna") })
|
|
32238
|
+
:
|
|
32239
|
+
glyph("hand-right", { title: ts("Afturkalla") })),
|
|
32240
|
+
m(clickable ? "a.flex" : "span.flex", clickable ? {
|
|
32241
|
+
href: "#",
|
|
32242
|
+
onclick: clickChallenge,
|
|
32243
|
+
class: oppReady ? "opp-ready" : ""
|
|
32244
|
+
} : {}, [
|
|
32245
|
+
m("span.list-ts", item.ts),
|
|
32246
|
+
m("span.list-nick", { title: item.fullname }, item.opp),
|
|
32247
|
+
m("span.list-chall", [
|
|
32248
|
+
item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
|
|
32249
|
+
item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
|
|
32250
|
+
descr
|
|
32251
|
+
])
|
|
32252
|
+
]),
|
|
32253
|
+
m(UserInfoButton, {
|
|
32254
|
+
view,
|
|
32255
|
+
item: {
|
|
32256
|
+
userid: item.userid,
|
|
32257
|
+
nick: item.opp,
|
|
32258
|
+
fullname: item.fullname,
|
|
32259
|
+
}
|
|
32260
|
+
}),
|
|
32261
|
+
]);
|
|
32262
|
+
}
|
|
32263
|
+
};
|
|
32264
|
+
};
|
|
32265
|
+
const ChallengeList = {
|
|
32266
|
+
// Shows a list of challenges (received or sent), obtained from view.model.challengeList
|
|
32267
|
+
view: (vnode) => {
|
|
32268
|
+
const { view, showReceived } = vnode.attrs;
|
|
32269
|
+
const { model } = view;
|
|
32270
|
+
if (!model.state)
|
|
32271
|
+
return [];
|
|
32272
|
+
// Trigger loading of challenge list if needed
|
|
32273
|
+
if (model.challengeList === null)
|
|
32274
|
+
model.loadChallengeList();
|
|
32275
|
+
let cList = [];
|
|
32276
|
+
if (model.challengeList)
|
|
32277
|
+
cList = showReceived ?
|
|
32278
|
+
model.challengeList.filter((item) => item.received) :
|
|
32279
|
+
model.challengeList.filter((item) => !item.received);
|
|
32280
|
+
const listContent = m("div", { id: showReceived ? 'chall-received' : 'chall-sent' }, cList.map((item, i) => m(ChallengeListEntry, { view, item, i })));
|
|
32281
|
+
if (showReceived)
|
|
32282
|
+
// Challenges received
|
|
32283
|
+
return [
|
|
32284
|
+
m(".listitem.listheader", [
|
|
32285
|
+
m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
|
|
32286
|
+
mt("span.list-ts", "Hvenær"),
|
|
32287
|
+
mt("span.list-nick", "Áskorandi"),
|
|
32288
|
+
mt("span.list-chall", "Hvernig"),
|
|
32289
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32290
|
+
]),
|
|
32291
|
+
listContent
|
|
32292
|
+
];
|
|
32293
|
+
else
|
|
32294
|
+
// Challenges sent
|
|
32295
|
+
return [
|
|
32296
|
+
m(".listitem.listheader", [
|
|
32297
|
+
m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
|
|
32298
|
+
mt("span.list-ts", "Hvenær"),
|
|
32299
|
+
mt("span.list-nick", "Andstæðingur"),
|
|
32300
|
+
mt("span.list-chall", "Hvernig"),
|
|
32301
|
+
mt("span.list-info-hdr", "Ferill"),
|
|
32302
|
+
]),
|
|
32303
|
+
listContent
|
|
32304
|
+
];
|
|
32305
|
+
}
|
|
32306
|
+
};
|
|
32307
|
+
|
|
31674
32308
|
/*
|
|
31675
32309
|
|
|
31676
32310
|
EloPage.ts
|
|
@@ -31766,7 +32400,6 @@ const EloList = (initialVnode) => {
|
|
|
31766
32400
|
view: (vnode) => {
|
|
31767
32401
|
function itemize(item, i) {
|
|
31768
32402
|
// Generate a list item about a user in an Elo ranking table
|
|
31769
|
-
var _a;
|
|
31770
32403
|
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
31771
32404
|
function rankStr(rank, ref) {
|
|
31772
32405
|
// Return a rank string or dash if no rank or not meaningful
|
|
@@ -31777,7 +32410,7 @@ const EloList = (initialVnode) => {
|
|
|
31777
32410
|
}
|
|
31778
32411
|
let nick = item.nick;
|
|
31779
32412
|
let ch = m("span.list-ch", nbsp());
|
|
31780
|
-
const userId =
|
|
32413
|
+
const userId = state?.netskraflUserId ?? "";
|
|
31781
32414
|
if (item.userid != userId && !item.inactive) {
|
|
31782
32415
|
ch = m(ChallengeButton, { view: outerView, item });
|
|
31783
32416
|
}
|
|
@@ -31811,6 +32444,135 @@ const EloList = (initialVnode) => {
|
|
|
31811
32444
|
};
|
|
31812
32445
|
};
|
|
31813
32446
|
|
|
32447
|
+
/*
|
|
32448
|
+
|
|
32449
|
+
Userlist.ts
|
|
32450
|
+
|
|
32451
|
+
User list display component
|
|
32452
|
+
|
|
32453
|
+
Copyright (C) 2025 Miðeind ehf.
|
|
32454
|
+
Author: Vilhjalmur Thorsteinsson
|
|
32455
|
+
|
|
32456
|
+
The Creative Commons Attribution-NonCommercial 4.0
|
|
32457
|
+
International Public License (CC-BY-NC 4.0) applies to this software.
|
|
32458
|
+
For further information, see https://github.com/mideind/Netskrafl
|
|
32459
|
+
|
|
32460
|
+
*/
|
|
32461
|
+
const UserList = () => {
|
|
32462
|
+
// Shows a list of users, obtained from view.model.userList
|
|
32463
|
+
function itemize(view, item, i) {
|
|
32464
|
+
// Generate a list item about a user
|
|
32465
|
+
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
32466
|
+
let fullname = [];
|
|
32467
|
+
// Online and accepting challenges
|
|
32468
|
+
if (item.ready && !isRobot) {
|
|
32469
|
+
fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
|
|
32470
|
+
fullname.push(nbsp());
|
|
32471
|
+
}
|
|
32472
|
+
// Willing to accept challenges for timed games
|
|
32473
|
+
if (item.ready_timed) {
|
|
32474
|
+
fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
|
|
32475
|
+
fullname.push(nbsp());
|
|
32476
|
+
}
|
|
32477
|
+
// Fair play commitment
|
|
32478
|
+
if (item.fairplay) {
|
|
32479
|
+
fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
|
|
32480
|
+
fullname.push(nbsp());
|
|
32481
|
+
}
|
|
32482
|
+
fullname.push(item.fullname);
|
|
32483
|
+
function fav() {
|
|
32484
|
+
if (isRobot)
|
|
32485
|
+
return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
|
|
32486
|
+
return m("span.list-fav", {
|
|
32487
|
+
title: ts("Uppáhald"),
|
|
32488
|
+
onclick: (ev) => {
|
|
32489
|
+
view.actions.toggleFavorite(item.userid, item.fav);
|
|
32490
|
+
item.fav = !item.fav;
|
|
32491
|
+
ev.preventDefault();
|
|
32492
|
+
}
|
|
32493
|
+
}, glyph(item.fav ? "star" : "star-empty"));
|
|
32494
|
+
}
|
|
32495
|
+
function userLink() {
|
|
32496
|
+
if (isRobot)
|
|
32497
|
+
return m("a", {
|
|
32498
|
+
href: "",
|
|
32499
|
+
onclick: (ev) => {
|
|
32500
|
+
// Start a new game against the robot
|
|
32501
|
+
view.actions.startRobotGame(item.userid);
|
|
32502
|
+
ev.preventDefault();
|
|
32503
|
+
}
|
|
32504
|
+
}, [
|
|
32505
|
+
m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
|
|
32506
|
+
m("span.list-fullname-robot", fullname)
|
|
32507
|
+
]);
|
|
32508
|
+
else
|
|
32509
|
+
return [
|
|
32510
|
+
m("span.list-nick", item.nick),
|
|
32511
|
+
m("span.list-fullname", fullname),
|
|
32512
|
+
m("span.list-human-elo", item.human_elo)
|
|
32513
|
+
];
|
|
32514
|
+
}
|
|
32515
|
+
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32516
|
+
m(ChallengeButton, { view, item }),
|
|
32517
|
+
fav(),
|
|
32518
|
+
userLink(),
|
|
32519
|
+
m(UserInfoButton, { view, item }),
|
|
32520
|
+
]);
|
|
32521
|
+
}
|
|
32522
|
+
return {
|
|
32523
|
+
view: (vnode) => {
|
|
32524
|
+
const { view, id } = vnode.attrs;
|
|
32525
|
+
const { model } = view;
|
|
32526
|
+
// The type of list to show; by default it's 'robots'
|
|
32527
|
+
const query = model.userListCriteria?.query ?? "";
|
|
32528
|
+
const spec = model.userListCriteria?.spec ?? "";
|
|
32529
|
+
const loadingElo = model.eloRatingSpec === undefined;
|
|
32530
|
+
const listType = query || "robots";
|
|
32531
|
+
if (listType === "elo" || loadingElo)
|
|
32532
|
+
// Show Elo list
|
|
32533
|
+
return [
|
|
32534
|
+
m(EloPage, {
|
|
32535
|
+
id: "elolist",
|
|
32536
|
+
view: view,
|
|
32537
|
+
spec: model.eloRatingSpec ?? null,
|
|
32538
|
+
key: "elopage",
|
|
32539
|
+
})
|
|
32540
|
+
];
|
|
32541
|
+
// Show normal user list
|
|
32542
|
+
let list = [];
|
|
32543
|
+
if (model.userList === undefined) ;
|
|
32544
|
+
else if (model.userList === null || query !== listType) {
|
|
32545
|
+
view.actions.loadUsersByType(listType, true);
|
|
32546
|
+
}
|
|
32547
|
+
else {
|
|
32548
|
+
list = model.userList;
|
|
32549
|
+
}
|
|
32550
|
+
const nothingFound = list.length === 0 && listType === "search" && spec !== "";
|
|
32551
|
+
const robotList = listType === "robots";
|
|
32552
|
+
return [
|
|
32553
|
+
m(".listitem.listheader", [
|
|
32554
|
+
m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
|
|
32555
|
+
m("span.list-fav", glyph("star-empty", { title: ts('Uppáhald') })),
|
|
32556
|
+
mt("span.list-nick", "Einkenni"),
|
|
32557
|
+
mt("span.list-fullname", "Nafn og merki"),
|
|
32558
|
+
robotList ? "" : mt("span.list-human-elo[id='usr-list-elo']", "Elo"),
|
|
32559
|
+
robotList ? "" : mt("span.list-info-hdr[id='usr-list-info']", "Ferill"),
|
|
32560
|
+
]),
|
|
32561
|
+
m("div", { id }, list.map((item, i) => itemize(view, item, i))),
|
|
32562
|
+
// Show indicator if search didn't find any users matching the criteria
|
|
32563
|
+
nothingFound ?
|
|
32564
|
+
m("div", { id: "user-no-match", style: { display: "block" } }, [
|
|
32565
|
+
glyph("search"),
|
|
32566
|
+
" ",
|
|
32567
|
+
m("span", { id: "search-prefix" }, spec),
|
|
32568
|
+
t(" finnst ekki")
|
|
32569
|
+
])
|
|
32570
|
+
: undefined
|
|
32571
|
+
];
|
|
32572
|
+
}
|
|
32573
|
+
};
|
|
32574
|
+
};
|
|
32575
|
+
|
|
31814
32576
|
/*
|
|
31815
32577
|
|
|
31816
32578
|
Statsdisplay.ts
|
|
@@ -31837,7 +32599,6 @@ const StatsDisplay = () => {
|
|
|
31837
32599
|
}
|
|
31838
32600
|
return {
|
|
31839
32601
|
view: (vnode) => {
|
|
31840
|
-
var _a, _b;
|
|
31841
32602
|
// Display statistics about this user
|
|
31842
32603
|
var s = vnode.attrs.ownStats;
|
|
31843
32604
|
var winRatio = 0, winRatioHuman = 0;
|
|
@@ -31862,13 +32623,13 @@ const StatsDisplay = () => {
|
|
|
31862
32623
|
m(".option.small" + (sel === 2 ? ".selected" : ""), { id: 'opt2' }, glyph("cog"))
|
|
31863
32624
|
]),
|
|
31864
32625
|
sel === 1 ? m("div", { id: 'own-stats-human', className: 'stats-box', style: { display: "inline-block" } }, [
|
|
31865
|
-
m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat(
|
|
32626
|
+
m(".stats-fig", { title: ts('Elo-stig') }, s ? vwStat(s.locale_elo?.human_elo, "crown") : ""),
|
|
31866
32627
|
m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.human_games, "th") : ""),
|
|
31867
32628
|
m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatioHuman, "bookmark", "%")),
|
|
31868
32629
|
m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScoreHuman, "dashboard"))
|
|
31869
32630
|
]) : "",
|
|
31870
32631
|
sel === 2 ? m("div", { id: 'own-stats-all', className: 'stats-box', style: { display: "inline-block" } }, [
|
|
31871
|
-
m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat(
|
|
32632
|
+
m(".stats-fig", { title: ts("Elo-stig") }, s ? vwStat(s.locale_elo?.elo, "crown") : ""),
|
|
31872
32633
|
m(".stats-fig.stats-games", { title: ts('Fjöldi viðureigna') }, s ? vwStat(s.games, "th") : ""),
|
|
31873
32634
|
m(".stats-fig.stats-win-ratio", { title: ts('Vinningshlutfall') }, vwStat(winRatio, "bookmark", "%")),
|
|
31874
32635
|
m(".stats-fig.stats-avg-score", { title: ts('Meðalstigafjöldi') }, vwStat(avgScore, "dashboard"))
|
|
@@ -31900,7 +32661,6 @@ const SearchButton = (initialVnode) => {
|
|
|
31900
32661
|
let spec = ""; // The current search pattern
|
|
31901
32662
|
let promise = undefined;
|
|
31902
32663
|
function newSearch() {
|
|
31903
|
-
var _a, _b;
|
|
31904
32664
|
// There may have been a change of search parameters: react
|
|
31905
32665
|
if (promise !== undefined) {
|
|
31906
32666
|
// There was a previous promise, now obsolete: make it
|
|
@@ -31908,13 +32668,13 @@ const SearchButton = (initialVnode) => {
|
|
|
31908
32668
|
promise.result = false;
|
|
31909
32669
|
promise = undefined;
|
|
31910
32670
|
}
|
|
31911
|
-
let sel =
|
|
32671
|
+
let sel = model.userListCriteria?.query || "robots";
|
|
31912
32672
|
if (sel !== "search") {
|
|
31913
32673
|
// Not already in a search: load the user list immediately
|
|
31914
32674
|
view.actions.searchUsers(spec, true);
|
|
31915
32675
|
return;
|
|
31916
32676
|
}
|
|
31917
|
-
if (spec ===
|
|
32677
|
+
if (spec === model.userListCriteria?.spec)
|
|
31918
32678
|
// We're already looking at the same search spec: done
|
|
31919
32679
|
return;
|
|
31920
32680
|
// We're changing the search spec.
|
|
@@ -31946,18 +32706,16 @@ const SearchButton = (initialVnode) => {
|
|
|
31946
32706
|
}
|
|
31947
32707
|
return {
|
|
31948
32708
|
view: () => {
|
|
31949
|
-
|
|
31950
|
-
const sel = ((_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) || "robots";
|
|
32709
|
+
const sel = model.userListCriteria?.query || "robots";
|
|
31951
32710
|
return m(".user-cat[id='user-search']", [
|
|
31952
32711
|
glyph("search", {
|
|
31953
32712
|
id: 'search',
|
|
31954
32713
|
className: (sel == "search" ? "shown" : ""),
|
|
31955
32714
|
onclick: () => {
|
|
31956
|
-
var _a;
|
|
31957
32715
|
// Reset the search pattern when clicking the search icon
|
|
31958
32716
|
spec = "";
|
|
31959
32717
|
newSearch();
|
|
31960
|
-
|
|
32718
|
+
document.getElementById("search-id")?.focus();
|
|
31961
32719
|
}
|
|
31962
32720
|
}),
|
|
31963
32721
|
nbsp(),
|
|
@@ -32060,8 +32818,7 @@ function makeTabs(view, id, createFunc, wireHrefs, vnode) {
|
|
|
32060
32818
|
ev.preventDefault();
|
|
32061
32819
|
};
|
|
32062
32820
|
const clickUserPrefs = (ev) => {
|
|
32063
|
-
|
|
32064
|
-
if ((_a = model === null || model === void 0 ? void 0 : model.state) === null || _a === void 0 ? void 0 : _a.userId)
|
|
32821
|
+
if (model?.state?.netskraflUserId)
|
|
32065
32822
|
// Don't show the userprefs if no user logged in
|
|
32066
32823
|
view.pushDialog("userprefs");
|
|
32067
32824
|
ev.preventDefault();
|
|
@@ -32111,8 +32868,7 @@ function updateTabVisibility(vnode) {
|
|
|
32111
32868
|
const selected = vnode.state.selected;
|
|
32112
32869
|
const lis = vnode.state.lis;
|
|
32113
32870
|
vnode.state.ids.map((id, i) => {
|
|
32114
|
-
|
|
32115
|
-
(_a = document.getElementById(id)) === null || _a === void 0 ? void 0 : _a.setAttribute("style", "display: " +
|
|
32871
|
+
document.getElementById(id)?.setAttribute("style", "display: " +
|
|
32116
32872
|
(i == selected ? "block" : "none"));
|
|
32117
32873
|
lis[i].classList.toggle("ui-tabs-active", i === selected);
|
|
32118
32874
|
lis[i].classList.toggle("ui-state-active", i === selected);
|
|
@@ -32196,551 +32952,219 @@ const BestDisplay = () => {
|
|
|
32196
32952
|
For further information, see https://github.com/mideind/Netskrafl
|
|
32197
32953
|
|
|
32198
32954
|
*/
|
|
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
|
-
|
|
32246
|
-
|
|
32247
|
-
turnText = ts("Viðureign lokið");
|
|
32248
|
-
flagClass = ".zombie";
|
|
32249
|
-
}
|
|
32250
|
-
else {
|
|
32251
|
-
// {opponent}'s move
|
|
32252
|
-
turnText = ts("opp_move", { opponent: item.opp });
|
|
32253
|
-
flagClass = ".grayed";
|
|
32254
|
-
}
|
|
32255
|
-
return m("span.list-myturn", m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText }));
|
|
32256
|
-
}
|
|
32257
|
-
function vwOverdue() {
|
|
32258
|
-
if (item.overdue)
|
|
32259
|
-
return glyph("hourglass", { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" });
|
|
32260
|
-
return glyphGrayed("hourglass");
|
|
32261
|
-
}
|
|
32262
|
-
function vwTileCount() {
|
|
32263
|
-
const winLose = item.sc0 < item.sc1 ? ".losing" : "";
|
|
32264
|
-
return m(".tilecount", m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } }));
|
|
32265
|
-
}
|
|
32266
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32267
|
-
m(m.route.Link, { href: gameUrl(item.url) }, [
|
|
32268
|
-
vwTurn(),
|
|
32269
|
-
m("span.list-overdue", vwOverdue()),
|
|
32270
|
-
m("span.list-ts-short", item.ts),
|
|
32271
|
-
vwOpp()
|
|
32272
|
-
]),
|
|
32273
|
-
m(UserInfoButton, {
|
|
32274
|
-
view,
|
|
32275
|
-
item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
|
|
32276
|
-
}),
|
|
32277
|
-
m("span.list-s0", item.sc0),
|
|
32278
|
-
m("span.list-colon", ":"),
|
|
32279
|
-
m("span.list-s1", item.sc1),
|
|
32280
|
-
m("span.list-tc", vwTileCount()),
|
|
32281
|
-
m("span.list-manual", item.manual
|
|
32282
|
-
? glyph("lightbulb", { title: ts("Keppnishamur") })
|
|
32283
|
-
: glyphGrayed("lightbulb"))
|
|
32284
|
-
]);
|
|
32285
|
-
});
|
|
32286
|
-
}
|
|
32287
|
-
if (model.gameList === null)
|
|
32288
|
-
model.loadGameList();
|
|
32289
|
-
return m("div", { id: 'gamelist' }, viewGameList());
|
|
32290
|
-
}
|
|
32291
|
-
function vwHint() {
|
|
32292
|
-
// Show some help if the user has no games in progress
|
|
32293
|
-
if (model.loadingGameList
|
|
32294
|
-
|| model.loadingAllLists
|
|
32295
|
-
|| (model.gameList !== null && model.gameList.length > 0))
|
|
32296
|
-
// Either we have games in progress or the game list is being loaded
|
|
32297
|
-
return "";
|
|
32298
|
-
return m(".hint", { style: { display: "block" } }, [
|
|
32299
|
-
m("p", [
|
|
32300
|
-
"Ef þig vantar einhvern til að skrafla við, veldu flipann ",
|
|
32301
|
-
m(m.route.Link, { href: "/main?tab=2" }, [glyph("user"), nbsp(), "Andstæðingar"]),
|
|
32302
|
-
" og skoraðu á tölvuþjarka - ",
|
|
32303
|
-
glyph("cog"), nbsp(), m("b", "Amlóða"),
|
|
32304
|
-
", ",
|
|
32305
|
-
glyph("cog"), nbsp(), m("b", "Miðlung"),
|
|
32306
|
-
" eða ",
|
|
32307
|
-
glyph("cog"), nbsp(), m("b", "Fullsterkan"),
|
|
32308
|
-
" - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
|
|
32309
|
-
" sem þar er að finna til að skora á."
|
|
32310
|
-
]),
|
|
32311
|
-
m("p", [
|
|
32312
|
-
"Þú stofnar áskorun með því að smella á bendi-teiknið ",
|
|
32313
|
-
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
32314
|
-
" vinstra megin við nafn andstæðingsins."
|
|
32315
|
-
]),
|
|
32316
|
-
m("p", "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
|
|
32317
|
-
" hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."),
|
|
32318
|
-
m("p.no-mobile-block", [
|
|
32319
|
-
m(m.route.Link, { href: "/help" }, "Hjálp"),
|
|
32320
|
-
" má fá með því að smella á bláa ",
|
|
32321
|
-
glyph("info-sign"), nbsp(), "-", nbsp(),
|
|
32322
|
-
"teiknið hér til vinstri."
|
|
32323
|
-
]),
|
|
32324
|
-
m("p.no-mobile-block", "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
|
|
32325
|
-
"örvarmerkið efst vinstra megin við skraflborðið.")
|
|
32326
|
-
]);
|
|
32327
|
-
}
|
|
32328
|
-
return [
|
|
32329
|
-
m(".listitem.listheader", [
|
|
32330
|
-
m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
|
|
32331
|
-
m("span.list-overdue", glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })),
|
|
32332
|
-
mt("span.list-ts-short", "Síðasti leikur"),
|
|
32333
|
-
mt("span.list-opp", "Andstæðingur"),
|
|
32334
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32335
|
-
mt("span.list-scorehdr", "Staða"),
|
|
32336
|
-
mt("span.list-tc", "Framvinda"),
|
|
32337
|
-
m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
|
|
32338
|
-
]),
|
|
32339
|
-
vwList(),
|
|
32340
|
-
vwHint()
|
|
32341
|
-
];
|
|
32342
|
-
}
|
|
32343
|
-
function vwChallenges(showReceived) {
|
|
32344
|
-
function vwList() {
|
|
32345
|
-
if (!model.state)
|
|
32346
|
-
return undefined;
|
|
32347
|
-
const state = model.state;
|
|
32348
|
-
function itemize(item, i) {
|
|
32349
|
-
// Generate a list item about a pending challenge (issued or received)
|
|
32350
|
-
function challengeDescription(json) {
|
|
32351
|
-
/* Return a human-readable string describing a challenge
|
|
32352
|
-
according to the enclosed preferences */
|
|
32353
|
-
if (!json || json.duration === undefined || json.duration === 0)
|
|
32354
|
-
/* Normal unbounded (untimed) game */
|
|
32355
|
-
return t("Venjuleg ótímabundin viðureign");
|
|
32356
|
-
return t("with_clock", { duration: json.duration.toString() });
|
|
32357
|
-
}
|
|
32358
|
-
function markChallenge(ev) {
|
|
32359
|
-
// Clicked the icon at the beginning of the line,
|
|
32360
|
-
// to decline a received challenge or retract an issued challenge
|
|
32361
|
-
ev.preventDefault();
|
|
32362
|
-
if (item.received) {
|
|
32363
|
-
view.actions.declineChallenge(item.userid, item.key);
|
|
32364
|
-
}
|
|
32365
|
-
else {
|
|
32366
|
-
view.actions.retractChallenge(item.userid, item.key);
|
|
32367
|
-
}
|
|
32368
|
-
}
|
|
32369
|
-
function clickChallenge(ev) {
|
|
32370
|
-
// Clicked the hotspot area to accept a received challenge
|
|
32371
|
-
ev.preventDefault();
|
|
32372
|
-
if (!model.moreGamesAllowed()) {
|
|
32373
|
-
// User must be a friend to be able to accept more challenges
|
|
32374
|
-
logEvent("hit_game_limit", {
|
|
32375
|
-
userid: state.userId,
|
|
32376
|
-
locale: state.locale,
|
|
32377
|
-
limit: model.maxFreeGames
|
|
32378
|
-
});
|
|
32379
|
-
// Promote a subscription to Netskrafl/Explo
|
|
32380
|
-
view.showFriendPromo();
|
|
32381
|
-
return;
|
|
32382
|
-
}
|
|
32383
|
-
if (item.received) {
|
|
32384
|
-
if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
|
|
32385
|
-
// Timed game: display a modal wait dialog
|
|
32386
|
-
view.pushDialog("wait", {
|
|
32387
|
-
oppId: item.userid,
|
|
32388
|
-
oppNick: item.opp,
|
|
32389
|
-
oppName: item.fullname,
|
|
32390
|
-
duration: item.prefs.duration,
|
|
32391
|
-
challengeKey: item.key,
|
|
32392
|
-
});
|
|
32393
|
-
else
|
|
32394
|
-
// Ask the server to create a new game and route to it
|
|
32395
|
-
view.actions.startNewGame(item.userid, false);
|
|
32396
|
-
}
|
|
32397
|
-
else {
|
|
32398
|
-
// Clicking on a sent challenge, i.e. a timed game
|
|
32399
|
-
// where the opponent is waiting and ready to start
|
|
32400
|
-
view.showAcceptDialog(item.userid, item.opp, item.key);
|
|
32401
|
-
}
|
|
32402
|
-
}
|
|
32403
|
-
const oppReady = !item.received && item.opp_ready &&
|
|
32404
|
-
item.prefs && item.prefs.duration !== undefined &&
|
|
32405
|
-
item.prefs.duration > 0;
|
|
32406
|
-
const clickable = item.received || oppReady;
|
|
32407
|
-
const descr = challengeDescription(item.prefs);
|
|
32408
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32409
|
-
m("span.list-ch", { onclick: markChallenge }, item.received ?
|
|
32410
|
-
glyph("thumbs-down", { title: ts("Hafna") })
|
|
32411
|
-
:
|
|
32412
|
-
glyph("hand-right", { title: ts("Afturkalla") })),
|
|
32413
|
-
m(clickable ? "a.flex" : "span.flex", clickable ? {
|
|
32414
|
-
href: "#",
|
|
32415
|
-
onclick: clickChallenge,
|
|
32416
|
-
class: oppReady ? "opp-ready" : ""
|
|
32417
|
-
} : {}, [
|
|
32418
|
-
m("span.list-ts", item.ts),
|
|
32419
|
-
m("span.list-nick", { title: item.fullname }, item.opp),
|
|
32420
|
-
m("span.list-chall", [
|
|
32421
|
-
item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
|
|
32422
|
-
item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
|
|
32423
|
-
descr
|
|
32424
|
-
])
|
|
32425
|
-
]),
|
|
32426
|
-
m(UserInfoButton, {
|
|
32427
|
-
view,
|
|
32428
|
-
item: {
|
|
32429
|
-
userid: item.userid,
|
|
32430
|
-
nick: item.opp,
|
|
32431
|
-
fullname: item.fullname,
|
|
32432
|
-
}
|
|
32433
|
-
}),
|
|
32434
|
-
]);
|
|
32435
|
-
}
|
|
32436
|
-
let cList = [];
|
|
32437
|
-
if (model.challengeList)
|
|
32438
|
-
cList = showReceived ?
|
|
32439
|
-
model.challengeList.filter((item) => item.received) :
|
|
32440
|
-
model.challengeList.filter((item) => !item.received);
|
|
32441
|
-
return m("div", {
|
|
32442
|
-
id: showReceived ? 'chall-received' : 'chall-sent',
|
|
32443
|
-
oninit: () => {
|
|
32444
|
-
if (model.challengeList === null)
|
|
32445
|
-
model.loadChallengeList();
|
|
32446
|
-
}
|
|
32447
|
-
}, cList.map(itemize));
|
|
32448
|
-
}
|
|
32449
|
-
const n = vwList();
|
|
32450
|
-
if (!n)
|
|
32451
|
-
return [];
|
|
32452
|
-
if (showReceived)
|
|
32453
|
-
// Challenges received
|
|
32454
|
-
return [
|
|
32455
|
-
m(".listitem.listheader", [
|
|
32456
|
-
m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
|
|
32457
|
-
mt("span.list-ts", "Hvenær"),
|
|
32458
|
-
mt("span.list-nick", "Áskorandi"),
|
|
32459
|
-
mt("span.list-chall", "Hvernig"),
|
|
32460
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32461
|
-
]),
|
|
32462
|
-
n
|
|
32463
|
-
];
|
|
32464
|
-
else
|
|
32465
|
-
// Challenges sent
|
|
32466
|
-
return [
|
|
32467
|
-
m(".listitem.listheader", [
|
|
32468
|
-
m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
|
|
32469
|
-
mt("span.list-ts", "Hvenær"),
|
|
32470
|
-
mt("span.list-nick", "Andstæðingur"),
|
|
32471
|
-
mt("span.list-chall", "Hvernig"),
|
|
32472
|
-
mt("span.list-info-hdr", "Ferill"),
|
|
32473
|
-
]),
|
|
32474
|
-
n
|
|
32475
|
-
];
|
|
32476
|
-
}
|
|
32477
|
-
function vwRecentList() {
|
|
32478
|
-
function vwList() {
|
|
32479
|
-
if (model.recentList === null)
|
|
32480
|
-
model.loadRecentList();
|
|
32481
|
-
return m(RecentList, {
|
|
32482
|
-
view,
|
|
32483
|
-
id: "recentlist",
|
|
32484
|
-
recentList: model.recentList || [],
|
|
32485
|
-
});
|
|
32486
|
-
}
|
|
32487
|
-
return [
|
|
32488
|
-
m(".listitem.listheader", [
|
|
32489
|
-
m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
|
|
32490
|
-
mt("span.list-ts-short", "Viðureign lauk"),
|
|
32491
|
-
mt("span.list-nick", "Andstæðingur"),
|
|
32492
|
-
mt("span.list-scorehdr", "Úrslit"),
|
|
32493
|
-
m("span.list-elo-hdr", [
|
|
32494
|
-
m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
|
|
32495
|
-
t("Elo"),
|
|
32496
|
-
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32497
|
-
]),
|
|
32498
|
-
mt("span.list-duration", "Lengd"),
|
|
32499
|
-
m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
|
|
32955
|
+
// Local components for MainTabs
|
|
32956
|
+
const MainTabHeader = {
|
|
32957
|
+
// Tab navigation headers with game/challenge counts
|
|
32958
|
+
view: (vnode) => {
|
|
32959
|
+
const { view } = vnode.attrs;
|
|
32960
|
+
const { model } = view;
|
|
32961
|
+
const { numGames, numChallenges } = model;
|
|
32962
|
+
return [
|
|
32963
|
+
m(HeaderLogo, { hidden: true }),
|
|
32964
|
+
m("ul", [
|
|
32965
|
+
m("li", m("a[href='#tabs-1']", [
|
|
32966
|
+
glyph("th"), m("span.tab-legend", t("Viðureignir")),
|
|
32967
|
+
m("span", {
|
|
32968
|
+
id: 'numgames',
|
|
32969
|
+
style: numGames ? 'display: inline-block' : ''
|
|
32970
|
+
}, numGames)
|
|
32971
|
+
])),
|
|
32972
|
+
m("li", m("a[href='#tabs-2']", [
|
|
32973
|
+
glyph("hand-right"), m("span.tab-legend", t("Áskoranir")),
|
|
32974
|
+
// Blink if we have timed games where the opponent is ready
|
|
32975
|
+
m("span" + (model.oppReady ? ".opp-ready" : ""), {
|
|
32976
|
+
id: "numchallenges",
|
|
32977
|
+
style: numChallenges ? 'display: inline-block' : ''
|
|
32978
|
+
}, numChallenges)
|
|
32979
|
+
])),
|
|
32980
|
+
m("li", m("a[href='#tabs-3']", [glyph("user"), m("span.tab-legend", t("Andstæðingar"))])),
|
|
32981
|
+
m("li.no-mobile-list", m("a[href='#tabs-4']", [glyph("bookmark"), m("span.tab-legend", t("Ferill"))]))
|
|
32982
|
+
])
|
|
32983
|
+
];
|
|
32984
|
+
}
|
|
32985
|
+
};
|
|
32986
|
+
const RecentListWithHeader = {
|
|
32987
|
+
// Recent games list with header
|
|
32988
|
+
view: (vnode) => {
|
|
32989
|
+
const { view } = vnode.attrs;
|
|
32990
|
+
const { model } = view;
|
|
32991
|
+
if (model.recentList === null)
|
|
32992
|
+
model.loadRecentList();
|
|
32993
|
+
return [
|
|
32994
|
+
m(".listitem.listheader", [
|
|
32995
|
+
m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
|
|
32996
|
+
mt("span.list-ts-short", "Viðureign lauk"),
|
|
32997
|
+
mt("span.list-nick", "Andstæðingur"),
|
|
32998
|
+
mt("span.list-scorehdr", "Úrslit"),
|
|
32999
|
+
m("span.list-elo-hdr", [
|
|
33000
|
+
m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
|
|
33001
|
+
t("Elo"),
|
|
33002
|
+
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32500
33003
|
]),
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
|
|
32514
|
-
|
|
32515
|
-
|
|
32516
|
-
|
|
32517
|
-
|
|
32518
|
-
|
|
32519
|
-
|
|
32520
|
-
|
|
32521
|
-
|
|
32522
|
-
|
|
32523
|
-
}
|
|
32524
|
-
ev.preventDefault();
|
|
33004
|
+
mt("span.list-duration", "Lengd"),
|
|
33005
|
+
m(ProModeIndicator, { header: true })
|
|
33006
|
+
]),
|
|
33007
|
+
m(RecentList, { view, id: "recentlist", recentList: model.recentList || [] })
|
|
33008
|
+
];
|
|
33009
|
+
}
|
|
33010
|
+
};
|
|
33011
|
+
const UserButton = {
|
|
33012
|
+
// User list type button (robots, fav, alike, elo)
|
|
33013
|
+
view: (vnode) => {
|
|
33014
|
+
const { view, buttonId, icon, text } = vnode.attrs;
|
|
33015
|
+
const { model } = view;
|
|
33016
|
+
const sel = model.userListCriteria?.query || "robots";
|
|
33017
|
+
return m("span", {
|
|
33018
|
+
className: (buttonId === sel ? "shown" : ""),
|
|
33019
|
+
id: buttonId,
|
|
33020
|
+
onclick: (ev) => {
|
|
33021
|
+
if (buttonId === "elo") {
|
|
33022
|
+
// Load Elo rating list, defaulting to the human-only rating
|
|
33023
|
+
model.userListCriteria = { query: "elo", spec: "human" };
|
|
33024
|
+
model.userList = null;
|
|
33025
|
+
view.actions.withSpinner(() => model.loadEloRatingList("human"));
|
|
32525
33026
|
}
|
|
32526
|
-
|
|
32527
|
-
|
|
32528
|
-
|
|
32529
|
-
|
|
32530
|
-
|
|
32531
|
-
function itemize(item, i) {
|
|
32532
|
-
// Generate a list item about a user
|
|
32533
|
-
const isRobot = item.userid.indexOf("robot-") === 0;
|
|
32534
|
-
let fullname = [];
|
|
32535
|
-
// Online and accepting challenges
|
|
32536
|
-
if (item.ready && !isRobot) {
|
|
32537
|
-
fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
|
|
32538
|
-
fullname.push(nbsp());
|
|
32539
|
-
}
|
|
32540
|
-
// Willing to accept challenges for timed games
|
|
32541
|
-
if (item.ready_timed) {
|
|
32542
|
-
fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
|
|
32543
|
-
fullname.push(nbsp());
|
|
32544
|
-
}
|
|
32545
|
-
// Fair play commitment
|
|
32546
|
-
if (item.fairplay) {
|
|
32547
|
-
fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
|
|
32548
|
-
fullname.push(nbsp());
|
|
32549
|
-
}
|
|
32550
|
-
fullname.push(item.fullname);
|
|
32551
|
-
function fav() {
|
|
32552
|
-
if (isRobot)
|
|
32553
|
-
return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
|
|
32554
|
-
return m("span.list-fav", {
|
|
32555
|
-
title: ts("Uppáhald"),
|
|
32556
|
-
onclick: (ev) => {
|
|
32557
|
-
view.actions.toggleFavorite(item.userid, item.fav);
|
|
32558
|
-
item.fav = !item.fav;
|
|
32559
|
-
ev.preventDefault();
|
|
32560
|
-
}
|
|
32561
|
-
}, glyph(item.fav ? "star" : "star-empty"));
|
|
32562
|
-
}
|
|
32563
|
-
function userLink() {
|
|
32564
|
-
if (isRobot)
|
|
32565
|
-
return m("a", {
|
|
32566
|
-
href: "",
|
|
32567
|
-
onclick: (ev) => {
|
|
32568
|
-
// Start a new game against the robot
|
|
32569
|
-
view.actions.startRobotGame(item.userid);
|
|
32570
|
-
ev.preventDefault();
|
|
32571
|
-
}
|
|
32572
|
-
}, [
|
|
32573
|
-
m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
|
|
32574
|
-
m("span.list-fullname-robot", fullname)
|
|
32575
|
-
]);
|
|
32576
|
-
else
|
|
32577
|
-
return m.fragment({}, [
|
|
32578
|
-
m("span.list-nick", item.nick),
|
|
32579
|
-
m("span.list-fullname", fullname),
|
|
32580
|
-
m("span.list-human-elo", item.human_elo)
|
|
32581
|
-
]);
|
|
32582
|
-
}
|
|
32583
|
-
return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"), [
|
|
32584
|
-
m(ChallengeButton, { view, item }),
|
|
32585
|
-
fav(),
|
|
32586
|
-
userLink(),
|
|
32587
|
-
m(UserInfoButton, { view, item }),
|
|
32588
|
-
]);
|
|
33027
|
+
else {
|
|
33028
|
+
// Load user list
|
|
33029
|
+
view.actions.loadUsersByType(buttonId, true);
|
|
33030
|
+
model.eloRatingSpec = null;
|
|
33031
|
+
model.eloRatingList = null;
|
|
32589
33032
|
}
|
|
32590
|
-
|
|
32591
|
-
}
|
|
32592
|
-
// The type of list to show; by default it's 'robots'
|
|
32593
|
-
const query = (_b = (_a = model.userListCriteria) === null || _a === void 0 ? void 0 : _a.query) !== null && _b !== void 0 ? _b : "";
|
|
32594
|
-
const spec = (_d = (_c = model.userListCriteria) === null || _c === void 0 ? void 0 : _c.spec) !== null && _d !== void 0 ? _d : "";
|
|
32595
|
-
const loadingElo = model.eloRatingSpec === undefined;
|
|
32596
|
-
const listType = query || "robots";
|
|
32597
|
-
if (listType === "elo" || loadingElo)
|
|
32598
|
-
// Show Elo list
|
|
32599
|
-
return [
|
|
32600
|
-
m(EloPage, {
|
|
32601
|
-
id: "elolist",
|
|
32602
|
-
view: view,
|
|
32603
|
-
spec: (_e = model.eloRatingSpec) !== null && _e !== void 0 ? _e : null,
|
|
32604
|
-
key: "elopage",
|
|
32605
|
-
})
|
|
32606
|
-
];
|
|
32607
|
-
// Show normal user list
|
|
32608
|
-
let list = [];
|
|
32609
|
-
if (model.userList === undefined) ;
|
|
32610
|
-
else if (model.userList === null || query !== listType) {
|
|
32611
|
-
view.actions.loadUsersByType(listType, true);
|
|
32612
|
-
}
|
|
32613
|
-
else {
|
|
32614
|
-
list = model.userList;
|
|
33033
|
+
ev.preventDefault();
|
|
32615
33034
|
}
|
|
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
|
-
|
|
32694
|
-
" ",
|
|
32695
|
-
vwUserButton("alike", "resize-small", ts("Svipaðir")),
|
|
32696
|
-
" ",
|
|
32697
|
-
vwUserButton("elo", "crown", ts("Topp 100"))
|
|
32698
|
-
]),
|
|
32699
|
-
m(SearchButton, { view })
|
|
32700
|
-
]),
|
|
32701
|
-
vwUserList()
|
|
32702
|
-
]);
|
|
32703
|
-
}
|
|
32704
|
-
function tab4() {
|
|
32705
|
-
// Recent games and statistics
|
|
32706
|
-
return m("div", { id: 'tabs-4' }, [
|
|
32707
|
-
vwStats(),
|
|
32708
|
-
vwBest(),
|
|
32709
|
-
mt("p.no-mobile-block", [
|
|
32710
|
-
mt("strong", "Nýlegar viðureignir þínar"),
|
|
32711
|
-
"click_to_review" // "Click on a game to review it"
|
|
33035
|
+
}, [glyph(icon, { style: { padding: 0 } }), nbsp(), text]);
|
|
33036
|
+
}
|
|
33037
|
+
};
|
|
33038
|
+
const OwnStats = {
|
|
33039
|
+
// User's own statistics summary
|
|
33040
|
+
view: (vnode) => {
|
|
33041
|
+
const { view } = vnode.attrs;
|
|
33042
|
+
const { model } = view;
|
|
33043
|
+
const { ownStats } = model;
|
|
33044
|
+
if (ownStats === null)
|
|
33045
|
+
model.loadOwnStats();
|
|
33046
|
+
return ownStats ? m(StatsDisplay, { view, id: 'own-stats', ownStats }) : null;
|
|
33047
|
+
}
|
|
33048
|
+
};
|
|
33049
|
+
const OwnBest = {
|
|
33050
|
+
// User's own best game and word scores
|
|
33051
|
+
view: (vnode) => {
|
|
33052
|
+
const { view } = vnode.attrs;
|
|
33053
|
+
const { model } = view;
|
|
33054
|
+
const { ownStats } = model;
|
|
33055
|
+
if (ownStats === null)
|
|
33056
|
+
model.loadOwnStats();
|
|
33057
|
+
return ownStats ? m(BestDisplay, { id: 'own-best', ownStats, myself: true }) : null;
|
|
33058
|
+
}
|
|
33059
|
+
};
|
|
33060
|
+
const Tab1 = {
|
|
33061
|
+
// Ongoing games tab
|
|
33062
|
+
view: (vnode) => {
|
|
33063
|
+
const { view } = vnode.attrs;
|
|
33064
|
+
return m("div", { id: 'tabs-1' }, [
|
|
33065
|
+
mt("p.no-mobile-block", [
|
|
33066
|
+
mt("strong", "Viðureignir sem standa yfir"),
|
|
33067
|
+
"click_on_game", // "Click on a game to view it and make a move if"
|
|
33068
|
+
glyph("flag"),
|
|
33069
|
+
" þú átt leik"
|
|
33070
|
+
]),
|
|
33071
|
+
m(GameList, { view, id: 'gamelist' })
|
|
33072
|
+
]);
|
|
33073
|
+
}
|
|
33074
|
+
};
|
|
33075
|
+
const Tab2 = {
|
|
33076
|
+
// Challenges received and sent tab
|
|
33077
|
+
view: (vnode) => {
|
|
33078
|
+
const { view } = vnode.attrs;
|
|
33079
|
+
return m("div", { id: 'tabs-2' }, [
|
|
33080
|
+
mt("p.no-mobile-block", [
|
|
33081
|
+
mt("strong", "Skorað á þig"),
|
|
33082
|
+
"click_on_challenge", // "Click on a challenge to accept it and start a game, or on"
|
|
33083
|
+
glyph("thumbs-down", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
33084
|
+
" til að hafna henni"
|
|
33085
|
+
]),
|
|
33086
|
+
m(ChallengeList, { view, showReceived: true }),
|
|
33087
|
+
mt("p.no-mobile-block", [
|
|
33088
|
+
mt("strong", "Þú skorar á aðra"),
|
|
33089
|
+
" - smelltu á ",
|
|
33090
|
+
glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
|
|
33091
|
+
" til að afturkalla áskorun"
|
|
33092
|
+
]),
|
|
33093
|
+
m(ChallengeList, { view, showReceived: false })
|
|
33094
|
+
]);
|
|
33095
|
+
}
|
|
33096
|
+
};
|
|
33097
|
+
const Tab3 = {
|
|
33098
|
+
// Opponent lists tab
|
|
33099
|
+
view: (vnode) => {
|
|
33100
|
+
const { view } = vnode.attrs;
|
|
33101
|
+
return m("div", { id: 'tabs-3' }, [
|
|
33102
|
+
m("div", { id: 'initials' }, [
|
|
33103
|
+
m(".user-cat[id='user-headings']", [
|
|
33104
|
+
m(UserButton, { view, buttonId: "robots", icon: "cog", text: ts("Þjarkar") }),
|
|
33105
|
+
" ",
|
|
33106
|
+
m(UserButton, { view, buttonId: "fav", icon: "star", text: ts("Uppáhalds") }),
|
|
33107
|
+
" ",
|
|
33108
|
+
m(UserButton, { view, buttonId: "live", icon: "flash", text: ts("Álínis") }),
|
|
33109
|
+
" ",
|
|
33110
|
+
m(UserButton, { view, buttonId: "alike", icon: "resize-small", text: ts("Svipaðir") }),
|
|
33111
|
+
" ",
|
|
33112
|
+
m(UserButton, { view, buttonId: "elo", icon: "crown", text: ts("Topp 100") })
|
|
32712
33113
|
]),
|
|
32713
|
-
|
|
32714
|
-
])
|
|
32715
|
-
|
|
33114
|
+
m(SearchButton, { view })
|
|
33115
|
+
]),
|
|
33116
|
+
m(UserList, { view, id: 'userlist' })
|
|
33117
|
+
]);
|
|
33118
|
+
}
|
|
33119
|
+
};
|
|
33120
|
+
const Tab4 = {
|
|
33121
|
+
// Recent games and statistics tab
|
|
33122
|
+
view: (vnode) => {
|
|
33123
|
+
const { view } = vnode.attrs;
|
|
33124
|
+
return m("div", { id: 'tabs-4' }, [
|
|
33125
|
+
m(OwnStats, { view }),
|
|
33126
|
+
m(OwnBest, { view }),
|
|
33127
|
+
mt("p.no-mobile-block", [
|
|
33128
|
+
mt("strong", "Nýlegar viðureignir þínar"),
|
|
33129
|
+
"click_to_review" // "Click on a game to review it"
|
|
33130
|
+
]),
|
|
33131
|
+
m(RecentListWithHeader, { view })
|
|
33132
|
+
]);
|
|
33133
|
+
}
|
|
33134
|
+
};
|
|
33135
|
+
const MainTabs = {
|
|
33136
|
+
// Main tabs component containing all tabs and their content
|
|
33137
|
+
view: (vnode) => {
|
|
33138
|
+
const { view } = vnode.attrs;
|
|
32716
33139
|
return m(".tabbed-page", m("div", { id: 'main-tabs' }, [
|
|
32717
|
-
|
|
33140
|
+
m(MainTabHeader, { view }),
|
|
32718
33141
|
m("div.tab-scroll-area", [
|
|
32719
|
-
|
|
32720
|
-
|
|
32721
|
-
|
|
32722
|
-
|
|
33142
|
+
m(Tab1, { view }),
|
|
33143
|
+
m(Tab2, { view }),
|
|
33144
|
+
m(Tab4, { view }),
|
|
33145
|
+
m(Tab3, { view }),
|
|
32723
33146
|
]),
|
|
32724
33147
|
]));
|
|
32725
33148
|
}
|
|
32726
|
-
|
|
32727
|
-
|
|
32728
|
-
|
|
32729
|
-
|
|
32730
|
-
|
|
32731
|
-
|
|
32732
|
-
|
|
32733
|
-
|
|
32734
|
-
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
},
|
|
32741
|
-
|
|
32742
|
-
|
|
32743
|
-
|
|
33149
|
+
};
|
|
33150
|
+
const Main = {
|
|
33151
|
+
// Main screen with tabs
|
|
33152
|
+
view: (vnode) => {
|
|
33153
|
+
const { view } = vnode.attrs;
|
|
33154
|
+
return [
|
|
33155
|
+
m(LeftLogo, { hidden: true }), // No legend, scale up by 50%
|
|
33156
|
+
m(UserId, { view }),
|
|
33157
|
+
m(Info),
|
|
33158
|
+
m(TogglerReady, { view }),
|
|
33159
|
+
m(TogglerReadyTimed, { view }),
|
|
33160
|
+
m("div", {
|
|
33161
|
+
oncreate: (vnode) => {
|
|
33162
|
+
makeTabs(view, "main-tabs", undefined, false, vnode);
|
|
33163
|
+
},
|
|
33164
|
+
onupdate: updateSelection
|
|
33165
|
+
}, m(MainTabs, { view })),
|
|
33166
|
+
];
|
|
33167
|
+
}
|
|
32744
33168
|
};
|
|
32745
33169
|
|
|
32746
33170
|
/*
|
|
@@ -32773,13 +33197,12 @@ const _updateStats = (attrs, state) => {
|
|
|
32773
33197
|
});
|
|
32774
33198
|
};
|
|
32775
33199
|
const _updateRecentList = (attrs, state) => {
|
|
32776
|
-
var _a, _b;
|
|
32777
33200
|
// Fetch the recent game list of the given user
|
|
32778
33201
|
if (state.loadingRecentList)
|
|
32779
33202
|
return;
|
|
32780
33203
|
state.loadingRecentList = true;
|
|
32781
33204
|
const { model } = attrs.view;
|
|
32782
|
-
model.loadUserRecentList(attrs.userid, state.versusAll ? null :
|
|
33205
|
+
model.loadUserRecentList(attrs.userid, state.versusAll ? null : model.state?.netskraflUserId ?? "", (json) => {
|
|
32783
33206
|
if (json && json.result === 0)
|
|
32784
33207
|
state.recentList = json.recentlist;
|
|
32785
33208
|
else
|
|
@@ -32834,10 +33257,9 @@ const UserInfoDialog = {
|
|
|
32834
33257
|
m(".usr-info-fav", {
|
|
32835
33258
|
title: ts('Uppáhald'),
|
|
32836
33259
|
onclick: (ev) => {
|
|
32837
|
-
var _a;
|
|
32838
33260
|
// Toggle the favorite setting
|
|
32839
33261
|
ev.preventDefault();
|
|
32840
|
-
view.actions.toggleFavorite(vnode.attrs.userid,
|
|
33262
|
+
view.actions.toggleFavorite(vnode.attrs.userid, stats.favorite ?? false);
|
|
32841
33263
|
stats.favorite = !stats.favorite;
|
|
32842
33264
|
}
|
|
32843
33265
|
}, stats.favorite ? glyph("star") : glyph("star-empty"))
|
|
@@ -32867,7 +33289,7 @@ const UserInfoDialog = {
|
|
|
32867
33289
|
m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
|
|
32868
33290
|
]),
|
|
32869
33291
|
mt("span.list-duration", "Lengd"),
|
|
32870
|
-
m(
|
|
33292
|
+
m(ProModeIndicator, { header: true })
|
|
32871
33293
|
]),
|
|
32872
33294
|
m(RecentList, { view, id: 'usr-recent', recentList }), // Recent game list
|
|
32873
33295
|
m(StatsDisplay, { view, id: 'usr-stats', ownStats: stats }),
|
|
@@ -32906,8 +33328,7 @@ const UserPrefsDialog = (initialVnode) => {
|
|
|
32906
33328
|
m(".errinput", [glyph("arrow-up"), nbsp(), err[propname] || ""]) : "";
|
|
32907
33329
|
}
|
|
32908
33330
|
function getToggle(elemId) {
|
|
32909
|
-
|
|
32910
|
-
const cls2 = (_a = document.querySelector("#" + elemId + "-toggler #opt2")) === null || _a === void 0 ? void 0 : _a.classList;
|
|
33331
|
+
const cls2 = document.querySelector("#" + elemId + "-toggler #opt2")?.classList;
|
|
32911
33332
|
if (!cls2)
|
|
32912
33333
|
return false;
|
|
32913
33334
|
return cls2.contains("selected");
|
|
@@ -33018,7 +33439,7 @@ const UserPrefsDialog = (initialVnode) => {
|
|
|
33018
33439
|
// Open the container's subscription page
|
|
33019
33440
|
ev.preventDefault();
|
|
33020
33441
|
logEvent("click_friend", {
|
|
33021
|
-
userid: state.
|
|
33442
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
33022
33443
|
});
|
|
33023
33444
|
// Navigate to the container-supplied URL
|
|
33024
33445
|
window.location.href = state.subscriptionUrl;
|
|
@@ -33454,7 +33875,7 @@ const Buttons = {
|
|
|
33454
33875
|
sc && r.push(sc);
|
|
33455
33876
|
}
|
|
33456
33877
|
// Is the server processing a move?
|
|
33457
|
-
if (game
|
|
33878
|
+
if (game?.moveInProgress) {
|
|
33458
33879
|
r.push(m(".waitmove", m(".animated-waitmove", m(AnimatedNetskraflLogo, {
|
|
33459
33880
|
withCircle: false,
|
|
33460
33881
|
}))));
|
|
@@ -33496,7 +33917,6 @@ class DragManager {
|
|
|
33496
33917
|
return (p.x >= rect.left) && (p.x < rect.right) && (p.y >= rect.top) && (p.y < rect.bottom);
|
|
33497
33918
|
}
|
|
33498
33919
|
constructor(e, dropHandler) {
|
|
33499
|
-
var _a, _b;
|
|
33500
33920
|
this.parentElement = null;
|
|
33501
33921
|
this.parentRect = null;
|
|
33502
33922
|
this.offsetX = 0;
|
|
@@ -33517,7 +33937,7 @@ class DragManager {
|
|
|
33517
33937
|
this.draggedElement = dragged;
|
|
33518
33938
|
this.dropHandler = dropHandler;
|
|
33519
33939
|
this.parentElement = dragged.parentElement;
|
|
33520
|
-
this.parentRect =
|
|
33940
|
+
this.parentRect = this.parentElement?.getBoundingClientRect() ?? null;
|
|
33521
33941
|
// Find out the bounding rectangle of the element
|
|
33522
33942
|
// before starting to apply modifications
|
|
33523
33943
|
const rect = dragged.getBoundingClientRect();
|
|
@@ -33875,7 +34295,6 @@ const DropTargetSquare = {
|
|
|
33875
34295
|
const RackTiles = {
|
|
33876
34296
|
// A rack of 7 tiles (tiles only, no buttons)
|
|
33877
34297
|
view: (vnode) => {
|
|
33878
|
-
var _a;
|
|
33879
34298
|
const { view, game, review } = vnode.attrs;
|
|
33880
34299
|
// If review==true, this is a review rack
|
|
33881
34300
|
// that is not a drop target and whose color reflects the
|
|
@@ -33884,7 +34303,7 @@ const RackTiles = {
|
|
|
33884
34303
|
return undefined;
|
|
33885
34304
|
const { model } = view;
|
|
33886
34305
|
let r = [];
|
|
33887
|
-
const reviewMove =
|
|
34306
|
+
const reviewMove = model.reviewMove ?? 0;
|
|
33888
34307
|
// Avoid flicker when paging through game review screen,
|
|
33889
34308
|
// as model.reviewMove becomes undefined or 0 while we
|
|
33890
34309
|
// are fetching the next move to display
|
|
@@ -34078,8 +34497,7 @@ const PlayerName = (initialVnode) => {
|
|
|
34078
34497
|
const { view } = initialVnode.attrs;
|
|
34079
34498
|
const model = view.model;
|
|
34080
34499
|
function lookAtPlayer(game, ev, player, side) {
|
|
34081
|
-
|
|
34082
|
-
if (!((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen))
|
|
34500
|
+
if (!(model.state?.uiFullscreen))
|
|
34083
34501
|
// Don't do anything on mobile, and allow the click
|
|
34084
34502
|
// to propagate to the parent
|
|
34085
34503
|
return;
|
|
@@ -34294,14 +34712,13 @@ const Chat = (initialVnode) => {
|
|
|
34294
34712
|
return str;
|
|
34295
34713
|
}
|
|
34296
34714
|
function chatMessages() {
|
|
34297
|
-
var _a, _b;
|
|
34298
34715
|
let r = [];
|
|
34299
|
-
if (
|
|
34716
|
+
if (game?.chatLoading || !game?.messages)
|
|
34300
34717
|
return r;
|
|
34301
34718
|
var key = 0;
|
|
34302
|
-
const userId =
|
|
34719
|
+
const userId = model.state?.netskraflUserId ?? "";
|
|
34303
34720
|
for (const msg of game.messages) {
|
|
34304
|
-
let p = player
|
|
34721
|
+
let p = player ?? 0;
|
|
34305
34722
|
if (msg.from_userid != userId)
|
|
34306
34723
|
p = 1 - p;
|
|
34307
34724
|
const mTs = makeTimestamp(msg.ts, key);
|
|
@@ -34339,13 +34756,13 @@ const Chat = (initialVnode) => {
|
|
|
34339
34756
|
setInput("msg", "");
|
|
34340
34757
|
}
|
|
34341
34758
|
}
|
|
34342
|
-
const numMessages =
|
|
34759
|
+
const numMessages = game?.messages ? game.messages.length : 0;
|
|
34343
34760
|
return {
|
|
34344
34761
|
view: () => m(".chat-container", {
|
|
34345
34762
|
style: "z-index: 6" // Appear on top of board on mobile
|
|
34346
34763
|
// key: uuid
|
|
34347
34764
|
}, [
|
|
34348
|
-
m(".chat-area" + (
|
|
34765
|
+
m(".chat-area" + (game?.showClock() ? ".with-clock" : ""), {
|
|
34349
34766
|
id: 'chat-area',
|
|
34350
34767
|
// Make sure that we see the bottom-most chat message
|
|
34351
34768
|
oncreate: scrollChatToBottom,
|
|
@@ -34435,7 +34852,7 @@ const MoveListItem = (initialVnode) => {
|
|
|
34435
34852
|
else {
|
|
34436
34853
|
// Show a friend promotion dialog
|
|
34437
34854
|
logEvent("click_review", {
|
|
34438
|
-
userid: state.
|
|
34855
|
+
userid: state.netskraflUserId, locale: state.locale
|
|
34439
34856
|
});
|
|
34440
34857
|
view.showFriendPromo();
|
|
34441
34858
|
}
|
|
@@ -34619,7 +35036,7 @@ const Movelist = (initialVnode) => {
|
|
|
34619
35036
|
onupdate: () => { setTimeout(scrollMovelistToBottom); }
|
|
34620
35037
|
}, movelist()),
|
|
34621
35038
|
// Show the bag here on mobile
|
|
34622
|
-
|
|
35039
|
+
state?.uiFullscreen ? "" : m(Bag, { bag, newbag })
|
|
34623
35040
|
])
|
|
34624
35041
|
};
|
|
34625
35042
|
};
|
|
@@ -34790,7 +35207,7 @@ const RightColumn = (initialVnode) => {
|
|
|
34790
35207
|
break;
|
|
34791
35208
|
}
|
|
34792
35209
|
const tabgrp = m(TabGroup, { view });
|
|
34793
|
-
return m(".right-area" + (
|
|
35210
|
+
return m(".right-area" + (game?.showClock() ? ".with-clock" : ""), component ? [tabgrp, component] : [tabgrp]);
|
|
34794
35211
|
}
|
|
34795
35212
|
return {
|
|
34796
35213
|
view: () => m(".rightcol", [
|
|
@@ -34881,14 +35298,14 @@ const GameView = {
|
|
|
34881
35298
|
m(RightColumn, { view }),
|
|
34882
35299
|
m(BoardArea, { view, game }),
|
|
34883
35300
|
// The bag is visible in fullscreen
|
|
34884
|
-
|
|
35301
|
+
state?.uiFullscreen ? m(Bag, { bag: bag, newbag: newbag }) : "",
|
|
34885
35302
|
game.askingForBlank ? m(BlankDialog, { game }) : ""
|
|
34886
35303
|
]),
|
|
34887
35304
|
// The left margin stuff: back button, square color help, info/help button
|
|
34888
35305
|
// These elements appear after the game container, since we want
|
|
34889
35306
|
// them to be above it in the z-order
|
|
34890
35307
|
m(LeftLogo),
|
|
34891
|
-
|
|
35308
|
+
state?.beginner ? m(Beginner, { view, showClose: true }) : "",
|
|
34892
35309
|
m(Info),
|
|
34893
35310
|
]);
|
|
34894
35311
|
}
|
|
@@ -35099,7 +35516,6 @@ const vwBestMove = (view, moveIndex, bestMoveIndex, move, info) => {
|
|
|
35099
35516
|
}
|
|
35100
35517
|
};
|
|
35101
35518
|
const vwScoreReview = (view, moveIndex) => {
|
|
35102
|
-
var _a;
|
|
35103
35519
|
// Shows the score of the current move within a game review screen
|
|
35104
35520
|
const game = view.model.game;
|
|
35105
35521
|
if (!game)
|
|
@@ -35113,7 +35529,7 @@ const vwScoreReview = (view, moveIndex) => {
|
|
|
35113
35529
|
return undefined;
|
|
35114
35530
|
let sc = [".score"];
|
|
35115
35531
|
if (moveIndex > 0) {
|
|
35116
|
-
if (moveIndex % 2 === (
|
|
35532
|
+
if (moveIndex % 2 === (game.player ?? 0))
|
|
35117
35533
|
// Opponent's move
|
|
35118
35534
|
sc.push("opponent");
|
|
35119
35535
|
else
|
|
@@ -35261,12 +35677,11 @@ const vwStatsReview = (view) => {
|
|
|
35261
35677
|
]);
|
|
35262
35678
|
};
|
|
35263
35679
|
const vwButtonsReview = (view, moveIndex) => {
|
|
35264
|
-
var _a, _b;
|
|
35265
35680
|
// The navigation buttons below the board on the review screen
|
|
35266
35681
|
const model = view.model;
|
|
35267
35682
|
const game = model.game;
|
|
35268
|
-
const numMoves =
|
|
35269
|
-
const gameUuid =
|
|
35683
|
+
const numMoves = game?.moves.length ?? 0;
|
|
35684
|
+
const gameUuid = game?.uuid ?? "";
|
|
35270
35685
|
let r = [];
|
|
35271
35686
|
if (!gameUuid)
|
|
35272
35687
|
return r;
|
|
@@ -35339,13 +35754,12 @@ const Review = (initialVnode) => {
|
|
|
35339
35754
|
}
|
|
35340
35755
|
return {
|
|
35341
35756
|
view: () => {
|
|
35342
|
-
var _a;
|
|
35343
35757
|
let r = [];
|
|
35344
35758
|
const { model } = view;
|
|
35345
35759
|
const { game } = model;
|
|
35346
35760
|
if (game) {
|
|
35347
35761
|
// Create a list of major elements that we're showing
|
|
35348
|
-
const moveIndex =
|
|
35762
|
+
const moveIndex = model.reviewMove ?? 0;
|
|
35349
35763
|
const bestMoves = model.bestMoves || [];
|
|
35350
35764
|
r.push(vwRightColumn(game, moveIndex, bestMoves));
|
|
35351
35765
|
r.push(m(BoardReview, { view, moveIndex }));
|
|
@@ -35500,12 +35914,11 @@ class View {
|
|
|
35500
35914
|
this.actions.hideSpinner();
|
|
35501
35915
|
}
|
|
35502
35916
|
notifyMediaChange() {
|
|
35503
|
-
var _a, _b;
|
|
35504
35917
|
// The view is changing, between mobile and fullscreen
|
|
35505
35918
|
// and/or between portrait and landscape: ensure that
|
|
35506
35919
|
// we don't end up with a selected game tab that is not visible
|
|
35507
35920
|
const model = this.model;
|
|
35508
|
-
if (
|
|
35921
|
+
if (model.state?.uiFullscreen || model.state?.uiLandscape) {
|
|
35509
35922
|
// In this case, there is no board tab:
|
|
35510
35923
|
// show the movelist
|
|
35511
35924
|
if (this.setSelectedTab("movelist"))
|
|
@@ -35542,7 +35955,7 @@ class View {
|
|
|
35542
35955
|
}
|
|
35543
35956
|
this.boardScale = 1.0;
|
|
35544
35957
|
const boardParent = document.getElementById("board-parent");
|
|
35545
|
-
const board = boardParent
|
|
35958
|
+
const board = boardParent?.children[0];
|
|
35546
35959
|
if (board) {
|
|
35547
35960
|
board.style.transition = 'none';
|
|
35548
35961
|
board.style.transform = `translate(0px, 0px) scale(1)`;
|
|
@@ -35551,7 +35964,6 @@ class View {
|
|
|
35551
35964
|
boardParent.scrollTo(0, 0);
|
|
35552
35965
|
}
|
|
35553
35966
|
updateScale() {
|
|
35554
|
-
var _a;
|
|
35555
35967
|
const model = this.model;
|
|
35556
35968
|
// Use either the regular game or the riddle (Gáta Dagsins)
|
|
35557
35969
|
const game = model.game || model.riddle;
|
|
@@ -35562,7 +35974,7 @@ class View {
|
|
|
35562
35974
|
// taking clamping into account to ensure that the board always fills
|
|
35563
35975
|
// the viewport.
|
|
35564
35976
|
const boardParent = document.getElementById("board-parent");
|
|
35565
|
-
const board = boardParent
|
|
35977
|
+
const board = boardParent?.children[0];
|
|
35566
35978
|
if (!board || !boardParent)
|
|
35567
35979
|
return;
|
|
35568
35980
|
const offset = 3;
|
|
@@ -35572,7 +35984,7 @@ class View {
|
|
|
35572
35984
|
const c = coord(row, col);
|
|
35573
35985
|
// board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
35574
35986
|
const el = document.getElementById("sq_" + c);
|
|
35575
|
-
const elRect = el
|
|
35987
|
+
const elRect = el?.getBoundingClientRect();
|
|
35576
35988
|
const boardRect = boardParent.getBoundingClientRect();
|
|
35577
35989
|
if (elRect && boardRect) {
|
|
35578
35990
|
// Get the dimensions of the scrollable area
|
|
@@ -35614,7 +36026,7 @@ class View {
|
|
|
35614
36026
|
}, 350);
|
|
35615
36027
|
}
|
|
35616
36028
|
};
|
|
35617
|
-
if (!game ||
|
|
36029
|
+
if (!game || model.state?.uiFullscreen || game.moveInProgress) {
|
|
35618
36030
|
// No game or we're in full screen mode: always 100% scale
|
|
35619
36031
|
// Also, as soon as a move is being processed by the server, we zoom out
|
|
35620
36032
|
this.boardScale = 1.0; // Needs to be done before setTimeout() call
|
|
@@ -35671,12 +36083,10 @@ class View {
|
|
|
35671
36083
|
function wireQuestions(vnode) {
|
|
35672
36084
|
// Clicking on a question brings the corresponding answer into view
|
|
35673
36085
|
// This is achieved by wiring up all contained a[href="#faq-*"] links
|
|
35674
|
-
var _a;
|
|
35675
36086
|
function showAnswer(ev, href) {
|
|
35676
|
-
var _a;
|
|
35677
36087
|
// this points to the vnode
|
|
35678
36088
|
vnode.state.selected = 1; // FAQ tab
|
|
35679
|
-
|
|
36089
|
+
vnode.dom.querySelector(href)?.scrollIntoView();
|
|
35680
36090
|
ev.preventDefault();
|
|
35681
36091
|
}
|
|
35682
36092
|
const anchors = vnode.dom.querySelectorAll("a");
|
|
@@ -35690,7 +36100,7 @@ class View {
|
|
|
35690
36100
|
// Go to the FAQ tab and scroll the requested question into view
|
|
35691
36101
|
selectTab(vnode, 1);
|
|
35692
36102
|
vnode.state.selected = 1; // FAQ tab
|
|
35693
|
-
|
|
36103
|
+
vnode.dom.querySelector("#faq-" + faqNumber.toString())?.scrollIntoView();
|
|
35694
36104
|
}
|
|
35695
36105
|
}
|
|
35696
36106
|
// Output literal HTML obtained from rawhelp.html on the server
|
|
@@ -35768,14 +36178,13 @@ class Actions {
|
|
|
35768
36178
|
// when view is available
|
|
35769
36179
|
}
|
|
35770
36180
|
onNavigateTo(routeName, params, view) {
|
|
35771
|
-
var _a, _b;
|
|
35772
36181
|
// We have navigated to a new route
|
|
35773
36182
|
// If navigating to something other than help,
|
|
35774
36183
|
// we need to have a logged-in user
|
|
35775
36184
|
const model = this.model;
|
|
35776
36185
|
model.routeName = routeName;
|
|
35777
36186
|
model.params = params;
|
|
35778
|
-
const uuid =
|
|
36187
|
+
const uuid = params.uuid ?? "";
|
|
35779
36188
|
if (routeName == "game") {
|
|
35780
36189
|
// New game route: initiate loading of the game into the model
|
|
35781
36190
|
if (model.game !== null) {
|
|
@@ -35785,10 +36194,9 @@ class Actions {
|
|
|
35785
36194
|
const deleteZombie = params.zombie === "1";
|
|
35786
36195
|
// Load the game, and attach it to the Firebase listener once it's loaded
|
|
35787
36196
|
model.loadGame(uuid, () => {
|
|
35788
|
-
var _a;
|
|
35789
36197
|
this.attachListenerToGame(uuid, view);
|
|
35790
36198
|
setTimeout(scrollMovelistToBottom);
|
|
35791
|
-
if (!
|
|
36199
|
+
if (!model.state?.uiFullscreen)
|
|
35792
36200
|
// Mobile UI: show board tab
|
|
35793
36201
|
view.setSelectedTab("board");
|
|
35794
36202
|
}, deleteZombie);
|
|
@@ -35816,10 +36224,9 @@ class Actions {
|
|
|
35816
36224
|
// Different game than we had before: load it, and then
|
|
35817
36225
|
// fetch the best moves
|
|
35818
36226
|
model.loadGame(uuid, () => {
|
|
35819
|
-
var _a;
|
|
35820
36227
|
model.loadBestMoves(move);
|
|
35821
36228
|
setTimeout(scrollMovelistToBottom);
|
|
35822
|
-
if (!
|
|
36229
|
+
if (!model.state?.uiFullscreen)
|
|
35823
36230
|
// Mobile UI: show board tab
|
|
35824
36231
|
view.setSelectedTab("board");
|
|
35825
36232
|
});
|
|
@@ -35837,7 +36244,7 @@ class Actions {
|
|
|
35837
36244
|
model.game.cleanup();
|
|
35838
36245
|
model.game = null;
|
|
35839
36246
|
}
|
|
35840
|
-
const locale =
|
|
36247
|
+
const locale = model.state?.locale || "is_IS";
|
|
35841
36248
|
if (routeName == "help") {
|
|
35842
36249
|
// Make sure that the help HTML is loaded upon first use
|
|
35843
36250
|
model.loadHelp();
|
|
@@ -35869,7 +36276,6 @@ class Actions {
|
|
|
35869
36276
|
this.model.handleUserMoveMessage(json, firstAttach);
|
|
35870
36277
|
}
|
|
35871
36278
|
onChatMessage(json, firstAttach, view) {
|
|
35872
|
-
var _a;
|
|
35873
36279
|
// Handle an incoming chat message
|
|
35874
36280
|
if (firstAttach) ;
|
|
35875
36281
|
else {
|
|
@@ -35881,7 +36287,7 @@ class Actions {
|
|
|
35881
36287
|
// - User has audio enabled
|
|
35882
36288
|
// - Message is from opponent (not from current user)
|
|
35883
36289
|
const { state } = this.model;
|
|
35884
|
-
const userId =
|
|
36290
|
+
const userId = state.netskraflUserId ?? "";
|
|
35885
36291
|
if (state.audio && json.from_userid !== userId) {
|
|
35886
36292
|
playAudio(state, "new-msg");
|
|
35887
36293
|
}
|
|
@@ -35985,26 +36391,26 @@ class Actions {
|
|
|
35985
36391
|
}
|
|
35986
36392
|
attachListenerToUser() {
|
|
35987
36393
|
const state = this.model.state;
|
|
35988
|
-
// console.log(`attachListenerToUser():
|
|
35989
|
-
if (!state || !state.
|
|
36394
|
+
// console.log(`attachListenerToUser(): netskraflUserId=${state?.netskraflUserId}`);
|
|
36395
|
+
if (!state || !state.netskraflUserId)
|
|
35990
36396
|
return;
|
|
35991
36397
|
// Listen to challenge and move notifications separately
|
|
35992
|
-
attachFirebaseListener(`user/${state.
|
|
35993
|
-
attachFirebaseListener(`user/${state.
|
|
36398
|
+
attachFirebaseListener(`user/${state.netskraflUserId}/challenge`, (json, firstAttach) => this.onUserChallengeMessage(json, firstAttach));
|
|
36399
|
+
attachFirebaseListener(`user/${state.netskraflUserId}/move`, (json, firstAttach) => this.onUserMoveMessage(json, firstAttach));
|
|
35994
36400
|
}
|
|
35995
36401
|
detachListenerFromUser() {
|
|
35996
36402
|
// Stop listening to Firebase notifications for the current user
|
|
35997
36403
|
const state = this.model.state;
|
|
35998
|
-
// console.log(`detachListenerFromUser():
|
|
35999
|
-
if (state && state.
|
|
36000
|
-
detachFirebaseListener('user/' + state.
|
|
36404
|
+
// console.log(`detachListenerFromUser(): netskraflUserId=${state?.netskraflUserId}`);
|
|
36405
|
+
if (state && state.netskraflUserId)
|
|
36406
|
+
detachFirebaseListener('user/' + state.netskraflUserId);
|
|
36001
36407
|
}
|
|
36002
36408
|
attachListenerToGame(uuid, view) {
|
|
36003
36409
|
// Listen to Firebase events on the /game/[gameId]/[userId] path
|
|
36004
36410
|
const state = this.model.state;
|
|
36005
36411
|
if (!uuid || !state)
|
|
36006
36412
|
return;
|
|
36007
|
-
const basepath = `game/${uuid}/${state.
|
|
36413
|
+
const basepath = `game/${uuid}/${state.netskraflUserId}/`;
|
|
36008
36414
|
// New moves
|
|
36009
36415
|
attachFirebaseListener(basepath + "move", (json, firstAttach) => this.onMoveMessage(json, firstAttach));
|
|
36010
36416
|
// New chat messages
|
|
@@ -36015,7 +36421,7 @@ class Actions {
|
|
|
36015
36421
|
const state = this.model.state;
|
|
36016
36422
|
if (!uuid || !state)
|
|
36017
36423
|
return;
|
|
36018
|
-
const basepath = `game/${uuid}/${state.
|
|
36424
|
+
const basepath = `game/${uuid}/${state.netskraflUserId}/`;
|
|
36019
36425
|
detachFirebaseListener(basepath + "move");
|
|
36020
36426
|
detachFirebaseListener(basepath + "chat");
|
|
36021
36427
|
}
|
|
@@ -36040,7 +36446,6 @@ class Actions {
|
|
|
36040
36446
|
}
|
|
36041
36447
|
// Challenge Management Actions
|
|
36042
36448
|
async handleChallenge(parameters) {
|
|
36043
|
-
var _a;
|
|
36044
36449
|
// Reject or retract a challenge
|
|
36045
36450
|
if (!this.model.state)
|
|
36046
36451
|
return;
|
|
@@ -36050,9 +36455,9 @@ class Actions {
|
|
|
36050
36455
|
url: "/challenge",
|
|
36051
36456
|
body: parameters
|
|
36052
36457
|
});
|
|
36053
|
-
if (
|
|
36458
|
+
if (json?.result === 0) {
|
|
36054
36459
|
// Log the change of challenge status (issue/decline/retract/accept)
|
|
36055
|
-
const locale =
|
|
36460
|
+
const locale = this.model.state?.locale || "is_IS";
|
|
36056
36461
|
var p = { locale };
|
|
36057
36462
|
if (parameters.duration !== undefined)
|
|
36058
36463
|
p.duration = parameters.duration;
|
|
@@ -36098,7 +36503,6 @@ class Actions {
|
|
|
36098
36503
|
}
|
|
36099
36504
|
// Game Management Actions
|
|
36100
36505
|
async startNewGame(oppid, reverse = false) {
|
|
36101
|
-
var _a;
|
|
36102
36506
|
// Ask the server to initiate a new game against the given opponent
|
|
36103
36507
|
if (!this.model.state)
|
|
36104
36508
|
return;
|
|
@@ -36115,9 +36519,9 @@ class Actions {
|
|
|
36115
36519
|
body: rqBody
|
|
36116
36520
|
};
|
|
36117
36521
|
const json = await request(this.model.state, rq);
|
|
36118
|
-
if (json
|
|
36522
|
+
if (json?.ok) {
|
|
36119
36523
|
// Log the new game event
|
|
36120
|
-
const locale =
|
|
36524
|
+
const locale = this.model.state?.locale || "is_IS";
|
|
36121
36525
|
logEvent("new_game", {
|
|
36122
36526
|
uuid: json.uuid,
|
|
36123
36527
|
timed: reverse,
|
|
@@ -36207,14 +36611,14 @@ class Actions {
|
|
|
36207
36611
|
url: "/cancelplan",
|
|
36208
36612
|
body: {}
|
|
36209
36613
|
});
|
|
36210
|
-
if (json
|
|
36614
|
+
if (json?.ok) {
|
|
36211
36615
|
// Successfully cancelled: immediately update the friend and hasPaid state
|
|
36212
36616
|
user.friend = false;
|
|
36213
36617
|
state.hasPaid = false;
|
|
36214
36618
|
state.plan = "";
|
|
36215
36619
|
// Log a friendship cancellation event
|
|
36216
36620
|
logEvent("cancel_plan", {
|
|
36217
|
-
userid: state.
|
|
36621
|
+
userid: state.netskraflUserId,
|
|
36218
36622
|
locale: state.locale,
|
|
36219
36623
|
// Add plan identifiers here
|
|
36220
36624
|
plan: "friend"
|
|
@@ -36233,29 +36637,29 @@ class Actions {
|
|
|
36233
36637
|
// Listen to global best score
|
|
36234
36638
|
attachFirebaseListener(basePath + "best", (json, firstAttach) => this.onRiddleGlobalScoreUpdate(json, firstAttach));
|
|
36235
36639
|
// Listen to group scores (if user has a group)
|
|
36236
|
-
if (state
|
|
36640
|
+
if (state?.userGroupId) {
|
|
36237
36641
|
attachFirebaseListener(basePath + `group/${state.userGroupId}/best`, (json, firstAttach) => this.onRiddleGroupScoreUpdate(json, firstAttach));
|
|
36238
36642
|
}
|
|
36239
36643
|
// Listen to global leaderboard
|
|
36240
36644
|
attachFirebaseListener(basePath + "leaders", (json, firstAttach) => this.onLeaderboardUpdate(json, firstAttach));
|
|
36241
36645
|
// Listen to user stats (if user is logged in)
|
|
36242
|
-
if (state
|
|
36243
|
-
attachFirebaseListener(`gatadagsins/users/${locale}/${state.
|
|
36646
|
+
if (state?.netskraflUserId) {
|
|
36647
|
+
attachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`, (json, firstAttach) => this.onUserStatsUpdate(json, firstAttach));
|
|
36244
36648
|
// Listen to personal best move (entire achievement object)
|
|
36245
|
-
attachFirebaseListener(basePath + `achievements/${state.
|
|
36649
|
+
attachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`, (json, firstAttach) => this.onPersonalBestScoreUpdate(json, firstAttach));
|
|
36246
36650
|
}
|
|
36247
36651
|
}
|
|
36248
36652
|
detachListenerFromRiddle(date, locale) {
|
|
36249
36653
|
const { state } = this.model;
|
|
36250
36654
|
const basePath = `gatadagsins/${date}/${locale}/`;
|
|
36251
36655
|
detachFirebaseListener(basePath + "best");
|
|
36252
|
-
if (state
|
|
36656
|
+
if (state?.userGroupId) {
|
|
36253
36657
|
detachFirebaseListener(basePath + `group/${state.userGroupId}/best`);
|
|
36254
36658
|
}
|
|
36255
36659
|
detachFirebaseListener(basePath + "leaders");
|
|
36256
|
-
if (state
|
|
36257
|
-
detachFirebaseListener(`gatadagsins/users/${locale}/${state.
|
|
36258
|
-
detachFirebaseListener(basePath + `achievements/${state.
|
|
36660
|
+
if (state?.netskraflUserId) {
|
|
36661
|
+
detachFirebaseListener(`gatadagsins/users/${locale}/${state.netskraflUserId}/stats`);
|
|
36662
|
+
detachFirebaseListener(basePath + `achievements/${state.netskraflUserId}`);
|
|
36259
36663
|
}
|
|
36260
36664
|
}
|
|
36261
36665
|
onRiddleGlobalScoreUpdate(json, firstAttach) {
|
|
@@ -36463,7 +36867,6 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36463
36867
|
}, []);
|
|
36464
36868
|
*/
|
|
36465
36869
|
React.useEffect(() => {
|
|
36466
|
-
var _a;
|
|
36467
36870
|
// Load the Netskrafl (Mithril) UI for a new user
|
|
36468
36871
|
if (!userEmail)
|
|
36469
36872
|
return;
|
|
@@ -36473,7 +36876,7 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36473
36876
|
return;
|
|
36474
36877
|
}
|
|
36475
36878
|
const elemId = `netskrafl-user-${userEmail}`;
|
|
36476
|
-
if (
|
|
36879
|
+
if (container.firstElementChild?.id === elemId) {
|
|
36477
36880
|
// The Netskrafl UI is already correctly mounted
|
|
36478
36881
|
return;
|
|
36479
36882
|
}
|
|
@@ -36499,8 +36902,8 @@ const NetskraflImpl = ({ state, tokenExpired }) => {
|
|
|
36499
36902
|
// when the component is unmounted
|
|
36500
36903
|
// console.log("Dismounting Netskrafl UI for user", userEmail);
|
|
36501
36904
|
const container = document.getElementById("netskrafl-container");
|
|
36502
|
-
const div = container
|
|
36503
|
-
if (
|
|
36905
|
+
const div = container?.firstElementChild;
|
|
36906
|
+
if (div?.id === elemId) {
|
|
36504
36907
|
document.body.appendChild(div);
|
|
36505
36908
|
}
|
|
36506
36909
|
};
|
|
@@ -37141,7 +37544,7 @@ const RightSideTabs = () => {
|
|
|
37141
37544
|
return m(".gatadagsins-right-side-tabs", "");
|
|
37142
37545
|
}
|
|
37143
37546
|
// Check if current user is on the leaderboard
|
|
37144
|
-
const currentUserId =
|
|
37547
|
+
const currentUserId = state?.netskraflUserId || "";
|
|
37145
37548
|
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37146
37549
|
const tabs = [
|
|
37147
37550
|
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
@@ -37180,7 +37583,7 @@ const RightSideTabs = () => {
|
|
|
37180
37583
|
// Leaderboard tab
|
|
37181
37584
|
activeTab === "leaderboard" ? m(LeaderboardView, {
|
|
37182
37585
|
leaderboard: view.model.leaderboard || [],
|
|
37183
|
-
currentUserId:
|
|
37586
|
+
currentUserId: state?.netskraflUserId || "",
|
|
37184
37587
|
date: riddle.date,
|
|
37185
37588
|
loading: false
|
|
37186
37589
|
}) : null
|
|
@@ -37357,7 +37760,7 @@ const StatsModal = () => {
|
|
|
37357
37760
|
return null;
|
|
37358
37761
|
}
|
|
37359
37762
|
// Check if current user is on the leaderboard
|
|
37360
|
-
const currentUserId =
|
|
37763
|
+
const currentUserId = state?.netskraflUserId || "";
|
|
37361
37764
|
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37362
37765
|
const tabs = [
|
|
37363
37766
|
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
@@ -37403,7 +37806,7 @@ const StatsModal = () => {
|
|
|
37403
37806
|
}) : null,
|
|
37404
37807
|
activeTab === "leaderboard" ? m(LeaderboardView, {
|
|
37405
37808
|
leaderboard: view.model.leaderboard || [],
|
|
37406
|
-
currentUserId:
|
|
37809
|
+
currentUserId: state?.netskraflUserId || "",
|
|
37407
37810
|
date: riddle.date,
|
|
37408
37811
|
loading: false
|
|
37409
37812
|
}) : null
|
|
@@ -37431,8 +37834,7 @@ const StatsModal = () => {
|
|
|
37431
37834
|
*/
|
|
37432
37835
|
const MAX_MOVES_TO_DISPLAY = 10;
|
|
37433
37836
|
const currentMoveState = (riddle) => {
|
|
37434
|
-
|
|
37435
|
-
const thisPlayer = ((_a = riddle.model.state) === null || _a === void 0 ? void 0 : _a.userId) || "";
|
|
37837
|
+
const thisPlayer = riddle.model.state?.netskraflUserId || "";
|
|
37436
37838
|
const { bestPossibleScore, // The highest score achievable for this riddle
|
|
37437
37839
|
globalBestScore, // The best score achieved by any player
|
|
37438
37840
|
groupBestScore, // The best score achieved within the player's group
|
|
@@ -37453,7 +37855,7 @@ const currentMoveState = (riddle) => {
|
|
|
37453
37855
|
// Check whether we need to add or annotate the global best score
|
|
37454
37856
|
if (globalBestScore && globalBestScore.score > 0) {
|
|
37455
37857
|
const { score, word, coord } = globalBestScore;
|
|
37456
|
-
if ((
|
|
37858
|
+
if ((selectedMoves[0]?.score ?? 0) >= score) {
|
|
37457
37859
|
// This player has made a move that scores the same
|
|
37458
37860
|
// or better as the top score: mark the move
|
|
37459
37861
|
selectedMoves[0].isGlobalBestScore = true;
|
|
@@ -37495,7 +37897,6 @@ const GataDagsins$1 = () => {
|
|
|
37495
37897
|
showStatsModal = false;
|
|
37496
37898
|
},
|
|
37497
37899
|
view: (vnode) => {
|
|
37498
|
-
var _a;
|
|
37499
37900
|
const { view } = vnode.attrs;
|
|
37500
37901
|
const { model } = view;
|
|
37501
37902
|
const { riddle } = model;
|
|
@@ -37530,7 +37931,7 @@ const GataDagsins$1 = () => {
|
|
|
37530
37931
|
// These elements appear after the main container for proper z-order
|
|
37531
37932
|
// m(LeftLogo), // Currently no need for the logo for Gáta Dagsins
|
|
37532
37933
|
// Show the Beginner component if the user is a beginner
|
|
37533
|
-
|
|
37934
|
+
model.state?.beginner ? m(Beginner, { view, showClose: false }) : "",
|
|
37534
37935
|
// Custom Info button for GataDagsins that shows help dialog
|
|
37535
37936
|
m(".info", { title: ts("Upplýsingar og hjálp") }, m("a.iconlink", { href: "#", onclick: (e) => { e.preventDefault(); toggleHelp(); } }, glyph("info-sign"))),
|
|
37536
37937
|
// Help dialog and backdrop
|
|
@@ -37639,7 +38040,6 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37639
38040
|
const completeState = makeGlobalState({ ...state, tokenExpired });
|
|
37640
38041
|
const { userEmail } = completeState;
|
|
37641
38042
|
React.useEffect(() => {
|
|
37642
|
-
var _a;
|
|
37643
38043
|
// Load the Gáta Dagsins (Mithril) UI for a new user
|
|
37644
38044
|
if (!userEmail)
|
|
37645
38045
|
return;
|
|
@@ -37649,7 +38049,7 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37649
38049
|
return;
|
|
37650
38050
|
}
|
|
37651
38051
|
const elemId = `gatadagsins-user-${userEmail}`;
|
|
37652
|
-
if (
|
|
38052
|
+
if (container.firstElementChild?.id === elemId) {
|
|
37653
38053
|
// The Gáta Dagsins UI is already correctly mounted
|
|
37654
38054
|
return;
|
|
37655
38055
|
}
|
|
@@ -37675,8 +38075,8 @@ const GataDagsinsImpl = ({ state, tokenExpired }) => {
|
|
|
37675
38075
|
// when the component is unmounted
|
|
37676
38076
|
// console.log("Dismounting Gáta Dagsins UI for user", userEmail);
|
|
37677
38077
|
const container = document.getElementById("gatadagsins-container");
|
|
37678
|
-
const div = container
|
|
37679
|
-
if (
|
|
38078
|
+
const div = container?.firstElementChild;
|
|
38079
|
+
if (div?.id === elemId) {
|
|
37680
38080
|
document.body.appendChild(div);
|
|
37681
38081
|
}
|
|
37682
38082
|
};
|