Skip to content

Commit 8f6167e

Browse files
committed
WIP
1 parent d97337f commit 8f6167e

File tree

5 files changed

+117
-45
lines changed

5 files changed

+117
-45
lines changed

common/api-review/util.api.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ export type EmulatorMockTokenOptions = ({
146146
export interface EmulatorStatus {
147147
// (undocumented)
148148
isRunningEmulator: boolean;
149-
// (undocumented)
150-
name: string;
151149
}
152150

153151
// Warning: (ae-missing-release-tag) "ErrorData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -495,7 +493,7 @@ export type Unsubscribe = () => void;
495493
// Warning: (ae-missing-release-tag) "updateStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
496494
//
497495
// @public (undocumented)
498-
export function updateStatus(emulatorStatus: EmulatorStatus, isCloudWorkstation: boolean): void;
496+
export function updateStatus(name: string, isRunningEmulator: boolean): void;
499497

500498
// Warning: (ae-missing-release-tag) "validateArgCount" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
501499
//

packages/auth/src/api/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,8 @@ export async function _performFetchWithErrorHandling<V>(
201201
): Promise<V> {
202202
const authInternal = auth as AuthInternal;
203203
updateStatus(
204-
{
205-
name: 'Auth',
206-
isRunningEmulator: authInternal.emulatorConfig !== undefined
207-
},
208-
authInternal.emulatorConfig!! &&
209-
isCloudWorkstation(authInternal.emulatorConfig.host)
204+
'Auth',
205+
authInternal.emulatorConfig !== null
210206
);
211207
authInternal._canInitEmulator = false;
212208
const errorMap = { ...SERVER_ERROR_MAP, ...customErrorMap };

packages/auth/src/core/auth/emulator.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,8 @@ function parsePort(portStr: string): number | null {
150150

151151
function emitEmulatorWarning(isCloudWorkstation: boolean): void {
152152
updateStatus(
153-
{
154-
name: 'Auth',
155-
isRunningEmulator: true
156-
},
157-
isCloudWorkstation
153+
'Auth',
154+
true
158155
);
159156
if (typeof console !== 'undefined' && typeof console.info === 'function') {
160157
console.info(

packages/firestore/src/lite-api/database.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,8 @@ export class Firestore implements FirestoreService {
144144
_freezeSettings(): FirestoreSettingsImpl {
145145
this._settingsFrozen = true;
146146
updateStatus(
147-
{
148-
name: 'Firestore',
149-
isRunningEmulator: (this._settings as PrivateSettings).emulator!!
150-
},
151-
isCloudWorkstation(this._settings.host)
147+
'Firestore',
148+
(this._settings as PrivateSettings).emulator!!
152149
);
153150
return this._settings;
154151
}

packages/util/src/emulator.ts

Lines changed: 110 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -152,47 +152,117 @@ function getOrCreate(id: string): { created: boolean; element: HTMLElement } {
152152
return { created, element: parentDiv };
153153
}
154154

155+
interface EmulatorStatuses {
156+
[name: string]: boolean;
157+
}
158+
const emulatorStatus: EmulatorStatuses = {};
159+
155160
export interface EmulatorStatus {
156-
name: string;
157161
isRunningEmulator: boolean;
158162
}
159-
export function updateStatus(
160-
emulatorStatus: EmulatorStatus,
161-
isCloudWorkstation: boolean
162-
) {
163+
164+
function createPopover() {
165+
const popover = document.createElement('div');
166+
popover.setAttribute('id', 'firebase__popover');
167+
popover.innerText = "I'm a popover!";
168+
popover.style.padding = '1em';
169+
popover.style.display = 'none';
170+
popover.style.position = 'absolute';
171+
popover.style.top = '50px';
172+
return popover;
173+
}
174+
175+
function createFirebaseEl() {
176+
const firebaseIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
177+
firebaseIcon.setAttribute('width', '25');
178+
firebaseIcon.setAttribute('height', '25');
179+
firebaseIcon.setAttribute('viewBox', '0 0 600 600');
180+
firebaseIcon.setAttribute('fill', 'none');
181+
182+
firebaseIcon.setAttribute('xlmns', 'http://www.w3.org/2000/svg');
183+
firebaseIcon.innerHTML =
184+
'<path d="M518.293 327.327V327.152C518.29 327.087 518.29 327.021 518.293 326.955C513.042 292.744 499.418 261.23 481.346 232.883C479.073 229.299 476.709 225.776 474.292 222.275C473.12 220.578 471.93 218.896 470.723 217.229C447.484 184.98 374.082 97.3028 335.522 51.5891C320.935 34.2901 311.335 23 311.335 23C306.986 26.4856 302.735 30.0822 298.582 33.79L298.438 33.9188C268.366 62.0273 243.429 95.1671 224.749 131.848L224.689 131.961C221.092 139.034 217.733 146.232 214.611 153.557C207.388 170.525 201.518 188.038 197.054 205.931C194.347 216.746 192.154 227.751 190.477 238.945C181.248 237.435 171.926 236.559 162.578 236.323C160.809 236.278 159.041 236.255 157.273 236.255C141.997 236.244 126.767 237.947 111.87 241.332C102.752 256.057 95.4109 271.812 90.0022 288.266C82.691 310.534 78.9771 333.826 79.0001 357.264C79.0001 464.392 155.334 553.706 256.581 573.747C270.78 576.56 285.221 577.974 299.696 577.968C302.553 577.968 305.394 577.907 308.228 577.809C315.669 577.516 323.006 576.869 330.24 575.869C347.097 573.532 363.625 569.245 379.493 563.094C383.721 561.457 387.883 559.689 391.98 557.79C467.753 522.851 520.392 446.199 520.392 357.264C520.363 347.249 519.662 337.248 518.293 327.327ZM228.462 525.692C206.732 516.512 186.992 503.2 170.337 486.495C153.636 469.84 140.327 450.103 131.147 428.377C121.624 405.87 116.743 381.673 116.796 357.234C116.742 332.793 121.623 308.593 131.147 286.083C132.662 282.466 134.302 278.915 136.065 275.43C143.07 274.551 150.123 274.111 157.182 274.111C167.114 274.11 177.025 274.988 186.802 276.733C186.59 281.666 186.484 286.644 186.484 291.637C186.35 384.538 222.981 473.718 288.376 539.703C267.751 538.489 247.486 533.75 228.462 525.692ZM309.539 507.06C305.104 502.347 300.838 497.512 296.741 492.557C277.101 468.917 261.052 442.512 249.11 414.192C232.7 375.411 224.285 333.717 224.37 291.607C224.37 290.46 224.37 289.316 224.37 288.175C243.143 296.502 260.212 308.232 274.714 322.773C297.198 345.136 312.765 373.502 319.556 404.478C322.124 416.25 323.414 428.263 323.405 440.311C323.476 463.278 318.772 486.008 309.592 507.06H309.539ZM482.612 363.561C481.454 397.93 470.608 431.273 451.325 459.747C435.654 482.871 414.924 502.124 390.707 516.046L390.464 515.933C384.1 519.579 377.521 522.838 370.763 525.692C362.345 529.254 353.67 532.175 344.811 534.429C343.235 534.833 341.654 535.212 340.068 535.565C339.212 535.762 338.363 535.952 337.507 536.126C337.84 535.52 338.166 534.891 338.484 534.27C338.977 533.323 339.462 532.375 339.932 531.421C345.716 519.852 350.375 507.753 353.843 495.292C358.821 477.405 361.337 458.923 361.322 440.357C361.329 426.85 360.002 413.375 357.359 400.129C343.319 329.918 293.263 272.725 227.28 248.583C230.32 226.251 235.774 204.316 243.548 183.161C245.291 178.428 247.148 173.727 249.118 169.06C252.088 162.028 255.301 155.133 258.756 148.374C258.777 148.34 258.794 148.305 258.809 148.268C260.226 145.32 261.688 142.418 263.204 139.524C275.098 116.875 290.091 95.9959 307.751 77.4882L308.046 77.8445C323.701 96.4088 344.652 121.384 365.52 146.548C376.196 159.429 386.85 172.363 396.761 184.525C415.348 207.34 431.321 227.435 439.952 239.369C441.187 241.082 442.392 242.787 443.574 244.514C455.97 262.51 465.548 280.704 472.041 298.7C476.17 310.046 479.147 321.779 480.93 333.722V333.873C482.388 343.698 482.956 353.634 482.627 363.561H482.612Z" fill="#1F1F1F"/>';
185+
return firebaseIcon;
186+
}
187+
export function updateStatus(name: string, isRunningEmulator: boolean) {
188+
if (emulatorStatus[name] === isRunningEmulator) {
189+
// No rerendering required
190+
return;
191+
}
192+
emulatorStatus[name] = isRunningEmulator;
193+
194+
function setDarkMode(el: HTMLElement) {
195+
el.setAttribute(
196+
'style',
197+
'position: fixed; bottom: 0px; border: solid 1px; width: 100%; border-radius: 10px; padding: .5em; text-align: center; background: black; color: white;'
198+
);
199+
}
200+
function setLightMode(el: HTMLElement) {
201+
el.setAttribute(
202+
'style',
203+
'position: fixed; bottom: 0px; border: solid 1px; width: 100%; border-radius: 10px; padding: .5em; text-align: center; background: #e9f1fe; color: black;'
204+
);
205+
}
163206
function setupDom() {
164207
const parentDivId = `__firebase_status`;
165208

166209
let { element: parentDiv, created } = getOrCreate(parentDivId);
167210

168211
if (created) {
169-
parentDiv.style.position = 'fixed';
170-
parentDiv.style.bottom = '0px';
171-
parentDiv.style.border = 'solid 1px';
172-
parentDiv.style.width = '100%';
173-
parentDiv.style.borderRadius = '10px';
174-
parentDiv.style.padding = '.5em';
175-
parentDiv.style.textAlign = 'center';
176212
parentDiv.classList.add('firebase-emulator-warning');
177213
document.body.appendChild(parentDiv);
178214
}
215+
window
216+
.matchMedia('(prefers-color-scheme: dark)')
217+
.addEventListener('change', event => {
218+
event.matches ? setDarkMode(parentDiv) : setLightMode(parentDiv);
219+
});
179220

180-
const { name, isRunningEmulator } = emulatorStatus;
181-
const { element, created: productDivCreated } = getOrCreate(
182-
`${parentDivId}_${name}`
183-
);
184-
// If in prod, and not using a cloud workstation, we should remove the node, as the banner can be distracting.
185-
if (!isRunningEmulator && !isCloudWorkstation) {
186-
element.remove();
187-
return;
221+
const paragraph = document.createElement('p');
222+
const anchor = document.createElement('a');
223+
if (isRunningEmulator) {
224+
paragraph.innerHTML = 'Running in local emulator';
225+
const firstDashIdx = window.location.host.indexOf('-');
226+
const emulatorHostPort = '4000';
227+
anchor.href = `${emulatorHostPort}-${window.location.host.substring(
228+
firstDashIdx
229+
)}`;
230+
} else {
231+
paragraph.innerHTML = 'Emulator disconnected';
232+
anchor.innerHTML = 'Learn more';
188233
}
189-
if (productDivCreated) {
190-
parentDiv.appendChild(element);
234+
const banner = getOrCreate('firebase__banner');
235+
if (banner.created) {
236+
// update styles
237+
const bannerEl = banner.element;
238+
bannerEl.style.display = 'flex';
239+
bannerEl.style.background = '#7faaf0';
240+
bannerEl.style.position = 'absolute';
241+
bannerEl.style.bottom = '0';
242+
const popOver = createPopover();
243+
// TODO(mtewani): Light vs dark mode
244+
const firebaseIcon = createFirebaseEl();
245+
const firebaseText = document.createElement('span');
246+
firebaseText.setAttribute('style', 'align-content: center');
247+
firebaseText.innerText = 'Firebase: ';
248+
const statusEl = document.createElement('span');
249+
updateStatusCount(statusEl);
250+
bannerEl.appendChild(firebaseIcon);
251+
bannerEl.appendChild(firebaseText);
252+
bannerEl.appendChild(popOver);
253+
bannerEl.appendChild(statusEl);
254+
banner.element.onclick = () => {
255+
const popover = document.getElementById('firebase__popover');
256+
if (popover) {
257+
if (popover?.style.display === 'none') {
258+
popover.style.display = 'flex';
259+
} else {
260+
popover.style.display = 'none';
261+
}
262+
}
263+
};
264+
document.body.appendChild(banner.element);
191265
}
192-
element.style.color = isRunningEmulator ? 'green' : 'red';
193-
element.innerHTML = `${name} is running in ${
194-
isRunningEmulator ? 'emulator' : 'prod'
195-
} mode`;
196266
}
197267
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
198268
if (document.readyState === 'loading') {
@@ -202,3 +272,17 @@ export function updateStatus(
202272
}
203273
}
204274
}
275+
276+
function updateStatusCount(element: HTMLElement) {
277+
const products = Object.keys(emulatorStatus);
278+
let prodCount = 0;
279+
let localCount = 0;
280+
for(let product in products) {
281+
if(emulatorStatus[product]) {
282+
localCount++;
283+
} else {
284+
prodCount++;
285+
}
286+
}
287+
element.innerText = `Prod (${prodCount}) Local (${localCount})`;
288+
}

0 commit comments

Comments
 (0)