mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-02-02 12:00:10 +08:00
Made username non optional (#2859)
* reduced code repetition * throwing error when no name provided * stopping signout if no current user * expanded to 16 chars * moved code to popup file * added google sign up popup * checking if name is available in the backend * deleting account if popup was closed uncommented sign up process addd functions to enable and disable input * added google sign up event to avoid circular dependency * hiding by default * name is no longer optional user is deleted if its database object is not found * using loader to indicate background activity
This commit is contained in:
parent
6c4e19a65a
commit
b60e6905ce
12 changed files with 418 additions and 128 deletions
|
@ -6,6 +6,7 @@ import { MonkeyResponse } from "../../utils/monkey-response";
|
|||
import { linkAccount } from "../../utils/discord";
|
||||
import { buildAgentLog } from "../../utils/misc";
|
||||
import George from "../../tasks/george";
|
||||
import admin from "firebase-admin";
|
||||
|
||||
export async function createNewUser(
|
||||
req: MonkeyTypes.Request
|
||||
|
@ -13,6 +14,11 @@ export async function createNewUser(
|
|||
const { name } = req.body;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
|
||||
const available = await UserDAL.isNameAvailable(name);
|
||||
if (!available) {
|
||||
throw new MonkeyError(409, "Username unavailable");
|
||||
}
|
||||
|
||||
await UserDAL.addUser(name, email, uid);
|
||||
Logger.logToDb("user_created", `${name} ${email}`, uid);
|
||||
|
||||
|
@ -92,22 +98,19 @@ export async function updateEmail(
|
|||
export async function getUser(
|
||||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UserDAL.getUser(uid);
|
||||
} catch (e) {
|
||||
if (email && uid) {
|
||||
userInfo = await UserDAL.addUser(undefined, email, uid);
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
404,
|
||||
"User not found. Could not recreate user document.",
|
||||
"Tried to recreate user document but either email or uid is nullish",
|
||||
uid
|
||||
);
|
||||
}
|
||||
await admin.auth().deleteUser(uid);
|
||||
throw new MonkeyError(
|
||||
404,
|
||||
"User not found. Please try to sign up again.",
|
||||
"get user",
|
||||
uid
|
||||
);
|
||||
}
|
||||
|
||||
const agentLog = buildAgentLog(req);
|
||||
|
|
|
@ -69,7 +69,7 @@ const usernameValidation = joi
|
|||
})
|
||||
.messages({
|
||||
"string.pattern.base":
|
||||
"Username invalid. Name cannot contain special characters or contain more than 14 characters. Can include _ . and -",
|
||||
"Username invalid. Name cannot use special characters or contain more than 16 characters. Can include _ . and -",
|
||||
});
|
||||
|
||||
const languageSchema = joi.string().min(1).required();
|
||||
|
|
|
@ -7,7 +7,7 @@ import MonkeyError from "../utils/error";
|
|||
import { DeleteResult, InsertOneResult, ObjectId, UpdateResult } from "mongodb";
|
||||
|
||||
export async function addUser(
|
||||
name: string | undefined,
|
||||
name: string,
|
||||
email: string,
|
||||
uid: string
|
||||
): Promise<InsertOneResult<MonkeyTypes.User>> {
|
||||
|
|
2
backend/types/types.d.ts
vendored
2
backend/types/types.d.ts
vendored
|
@ -62,7 +62,7 @@ declare namespace MonkeyTypes {
|
|||
lastNameChange?: number;
|
||||
lbMemory?: object;
|
||||
lbPersonalBests?: LbPersonalBests;
|
||||
name?: string;
|
||||
name: string;
|
||||
customThemes?: CustomTheme[];
|
||||
personalBests?: PersonalBests;
|
||||
quoteRatings?: UserQuoteRatings;
|
||||
|
|
|
@ -6,7 +6,7 @@ export function inRange(value: number, min: number, max: number): boolean {
|
|||
}
|
||||
|
||||
export function isUsernameValid(name: string): boolean {
|
||||
if (_.isNil(name) || !inRange(name.length, 1, 14)) {
|
||||
if (_.isNil(name) || !inRange(name.length, 1, 16)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import * as PageTransition from "../states/page-transition";
|
|||
import * as ActivePage from "../states/active-page";
|
||||
import * as TestActive from "../states/test-active";
|
||||
import * as LoadingPage from "../pages/loading";
|
||||
import * as LoginPage from "../pages/login";
|
||||
import * as ResultFilters from "../account/result-filters";
|
||||
import * as PaceCaret from "../test/pace-caret";
|
||||
import * as CommandlineLists from "../elements/commandline-lists";
|
||||
|
@ -45,6 +46,7 @@ import {
|
|||
import { Auth } from "../firebase";
|
||||
import differenceInDays from "date-fns/differenceInDays";
|
||||
import { defaultSnap } from "../constants/default-snapshot";
|
||||
import { dispatch as dispatchSignUpEvent } from "../observables/google-sign-up-event";
|
||||
|
||||
export const gmailProvider = new GoogleAuthProvider();
|
||||
|
||||
|
@ -230,7 +232,7 @@ export async function getDataAndInit(): Promise<boolean> {
|
|||
return true;
|
||||
}
|
||||
|
||||
async function loadUser(user: UserType): Promise<void> {
|
||||
export async function loadUser(user: UserType): Promise<void> {
|
||||
// User is signed in.
|
||||
$(".pageAccount .content p.accountVerificatinNotice").remove();
|
||||
if (user.emailVerified === false) {
|
||||
|
@ -251,7 +253,7 @@ async function loadUser(user: UserType): Promise<void> {
|
|||
// var isAnonymous = user.isAnonymous;
|
||||
// var uid = user.uid;
|
||||
// var providerData = user.providerData;
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
|
||||
// showFavouriteThemesAtTheTop();
|
||||
|
||||
|
@ -319,9 +321,8 @@ const authListener = Auth.onAuthStateChanged(async function (user) {
|
|||
export function signIn(): void {
|
||||
UpdateConfig.setChangedBeforeDb(false);
|
||||
authListener();
|
||||
$(".pageLogin .preloader").removeClass("hidden");
|
||||
$(".pageLogin .button").addClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", true);
|
||||
LoginPage.showPreloader();
|
||||
LoginPage.disableInputs();
|
||||
const email = ($(".pageLogin .login input")[0] as HTMLInputElement).value;
|
||||
const password = ($(".pageLogin .login input")[1] as HTMLInputElement).value;
|
||||
|
||||
|
@ -361,115 +362,29 @@ export function signIn(): void {
|
|||
message = "User not found";
|
||||
}
|
||||
Notifications.add(message, -1);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
LoginPage.hidePreloader();
|
||||
LoginPage.enableInputs();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function signInWithGoogle(): Promise<void> {
|
||||
UpdateConfig.setChangedBeforeDb(false);
|
||||
$(".pageLogin .preloader").removeClass("hidden");
|
||||
$(".pageLogin .button").addClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", true);
|
||||
LoginPage.showPreloader();
|
||||
LoginPage.disableInputs();
|
||||
authListener();
|
||||
let signedInUser;
|
||||
try {
|
||||
const persistence = $(".pageLogin .login #rememberMe input").prop("checked")
|
||||
? browserLocalPersistence
|
||||
: browserSessionPersistence;
|
||||
const persistence = $(".pageLogin .login #rememberMe input").prop("checked")
|
||||
? browserLocalPersistence
|
||||
: browserSessionPersistence;
|
||||
|
||||
await setPersistence(Auth, persistence);
|
||||
signedInUser = await signInWithPopup(Auth, gmailProvider);
|
||||
await setPersistence(Auth, persistence);
|
||||
const signedInUser = await signInWithPopup(Auth, gmailProvider);
|
||||
|
||||
if (getAdditionalUserInfo(signedInUser)?.isNewUser) {
|
||||
//ask for username
|
||||
let nameGood = false;
|
||||
let name = "";
|
||||
|
||||
while (!nameGood) {
|
||||
name =
|
||||
prompt(
|
||||
"Please provide a new username (cannot be longer than 16 characters, can only contain letters, numbers, underscores, dots and dashes):"
|
||||
) || "";
|
||||
|
||||
if (!name) {
|
||||
signOut();
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await Ape.users.getNameAvailability(name);
|
||||
|
||||
if (response.status !== 200) {
|
||||
return Notifications.add(
|
||||
"Failed to check name: " + response.message,
|
||||
-1
|
||||
);
|
||||
}
|
||||
|
||||
nameGood = true;
|
||||
}
|
||||
//create database object for the new user
|
||||
// try {
|
||||
const response = await Ape.users.create(name);
|
||||
if (response.status !== 200) {
|
||||
throw response;
|
||||
}
|
||||
// } catch (e) {
|
||||
// let msg = e?.response?.data?.message ?? e.message;
|
||||
// Notifications.add("Failed to create account: " + msg, -1);
|
||||
// return;
|
||||
// }
|
||||
if (response.status === 200) {
|
||||
await updateProfile(signedInUser.user, { displayName: name });
|
||||
await sendEmailVerification(signedInUser.user);
|
||||
AllTimeStats.clear();
|
||||
Notifications.add("Account created", 1, 3);
|
||||
$("#menu .text-button.account .text").text(name);
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
await loadUser(signedInUser.user);
|
||||
PageController.change("account");
|
||||
if (TestLogic.notSignedInLastResult !== null) {
|
||||
TestLogic.setNotSignedInUid(signedInUser.user.uid);
|
||||
|
||||
const resultsSaveResponse = await Ape.results.save(
|
||||
TestLogic.notSignedInLastResult
|
||||
);
|
||||
|
||||
if (resultsSaveResponse.status === 200) {
|
||||
const result = TestLogic.notSignedInLastResult;
|
||||
DB.saveLocalResult(result);
|
||||
DB.updateLocalStats({
|
||||
time:
|
||||
result.testDuration +
|
||||
result.incompleteTestSeconds -
|
||||
result.afkDuration,
|
||||
started: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await loadUser(signedInUser.user);
|
||||
PageController.change("account");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
const message = Misc.createErrorMessage(e, "Failed to sign in with Google");
|
||||
Notifications.add(message, -1);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
if (signedInUser && getAdditionalUserInfo(signedInUser)?.isNewUser) {
|
||||
await Ape.users.delete();
|
||||
await signedInUser.user.delete();
|
||||
}
|
||||
signOut();
|
||||
return;
|
||||
if (getAdditionalUserInfo(signedInUser)?.isNewUser) {
|
||||
dispatchSignUpEvent(signedInUser, true);
|
||||
} else {
|
||||
await loadUser(signedInUser.user);
|
||||
PageController.change("account");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,6 +477,7 @@ export async function addPasswordAuth(
|
|||
}
|
||||
|
||||
export function signOut(): void {
|
||||
if (!Auth.currentUser) return;
|
||||
Auth.signOut()
|
||||
.then(function () {
|
||||
Notifications.add("Signed out", 0, 2);
|
||||
|
@ -579,9 +495,8 @@ export function signOut(): void {
|
|||
}
|
||||
|
||||
async function signUp(): Promise<void> {
|
||||
$(".pageLogin .button").addClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", true);
|
||||
$(".pageLogin .preloader").removeClass("hidden");
|
||||
LoginPage.disableInputs();
|
||||
LoginPage.showPreloader();
|
||||
const nname = ($(".pageLogin .register input")[0] as HTMLInputElement).value;
|
||||
const email = ($(".pageLogin .register input")[1] as HTMLInputElement).value;
|
||||
const emailVerify = ($(".pageLogin .register input")[2] as HTMLInputElement)
|
||||
|
@ -594,7 +509,7 @@ async function signUp(): Promise<void> {
|
|||
|
||||
if (email !== emailVerify) {
|
||||
Notifications.add("Emails do not match", 0, 3);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
return;
|
||||
|
@ -602,7 +517,7 @@ async function signUp(): Promise<void> {
|
|||
|
||||
if (password !== passwordVerify) {
|
||||
Notifications.add("Passwords do not match", 0, 3);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
return;
|
||||
|
@ -612,7 +527,7 @@ async function signUp(): Promise<void> {
|
|||
|
||||
if (response.status !== 200) {
|
||||
Notifications.add(response.message, -1);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
return;
|
||||
|
@ -643,7 +558,7 @@ async function signUp(): Promise<void> {
|
|||
$("#menu .text-button.account .text").text(nname);
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
await loadUser(createdAuthUser.user);
|
||||
if (TestLogic.notSignedInLastResult !== null) {
|
||||
TestLogic.setNotSignedInUid(createdAuthUser.user.uid);
|
||||
|
@ -673,7 +588,7 @@ async function signUp(): Promise<void> {
|
|||
console.log(e);
|
||||
const message = Misc.createErrorMessage(e, "Failed to create account");
|
||||
Notifications.add(message, -1);
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
LoginPage.hidePreloader();
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
signOut();
|
||||
|
|
|
@ -26,6 +26,7 @@ import "./popups/pb-tables-popup";
|
|||
import "./elements/scroll-to-top";
|
||||
import "./popups/mobile-test-config-popup";
|
||||
import "./popups/edit-tags-popup";
|
||||
import "./popups/google-sign-up-popup";
|
||||
import * as Account from "./pages/account";
|
||||
|
||||
type ExtendedGlobal = typeof globalThis & MonkeyTypes.Global;
|
||||
|
|
26
frontend/src/scripts/observables/google-sign-up-event.ts
Normal file
26
frontend/src/scripts/observables/google-sign-up-event.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { UserCredential } from "firebase/auth";
|
||||
|
||||
type SubscribeFunction = (
|
||||
signedInUser: UserCredential,
|
||||
isNewUser: boolean
|
||||
) => void;
|
||||
|
||||
const subscribers: SubscribeFunction[] = [];
|
||||
|
||||
export function subscribe(fn: SubscribeFunction): void {
|
||||
subscribers.push(fn);
|
||||
}
|
||||
|
||||
export function dispatch(
|
||||
signedInUser: UserCredential,
|
||||
isNewUser: boolean
|
||||
): void {
|
||||
subscribers.forEach((fn) => {
|
||||
try {
|
||||
fn(signedInUser, isNewUser);
|
||||
} catch (e) {
|
||||
console.error("Google Sign Up event subscriber threw an error");
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,5 +1,23 @@
|
|||
import Page from "./page";
|
||||
|
||||
export function enableInputs(): void {
|
||||
$(".pageLogin .button").removeClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", false);
|
||||
}
|
||||
|
||||
export function disableInputs(): void {
|
||||
$(".pageLogin .button").addClass("disabled");
|
||||
$(".pageLogin input").prop("disabled", true);
|
||||
}
|
||||
|
||||
export function showPreloader(): void {
|
||||
$(".pageLogin .preloader").removeClass("hidden");
|
||||
}
|
||||
|
||||
export function hidePreloader(): void {
|
||||
$(".pageLogin .preloader").addClass("hidden");
|
||||
}
|
||||
|
||||
export const page = new Page(
|
||||
"login",
|
||||
$(".page.pageLogin"),
|
||||
|
|
245
frontend/src/scripts/popups/google-sign-up-popup.ts
Normal file
245
frontend/src/scripts/popups/google-sign-up-popup.ts
Normal file
|
@ -0,0 +1,245 @@
|
|||
import * as Notifications from "../elements/notifications";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import {
|
||||
sendEmailVerification,
|
||||
updateProfile,
|
||||
UserCredential,
|
||||
getAdditionalUserInfo,
|
||||
} from "firebase/auth";
|
||||
import Ape from "../ape";
|
||||
import { createErrorMessage } from "../utils/misc";
|
||||
import * as LoginPage from "../pages/login";
|
||||
import * as AllTimeStats from "../account/all-time-stats";
|
||||
import * as AccountController from "../controllers/account-controller";
|
||||
import * as PageController from "../controllers/page-controller";
|
||||
import * as TestLogic from "../test/test-logic";
|
||||
import * as DB from "../db";
|
||||
import * as Loader from "../elements/loader";
|
||||
import { subscribe as subscribeToSignUpEvent } from "../observables/google-sign-up-event";
|
||||
|
||||
let signedInUser: UserCredential | undefined = undefined;
|
||||
|
||||
export function show(credential: UserCredential): void {
|
||||
if ($("#googleSignUpPopupWrapper").hasClass("hidden")) {
|
||||
enableInput();
|
||||
disableButton();
|
||||
signedInUser = credential;
|
||||
$("#googleSignUpPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 0)
|
||||
.removeClass("hidden")
|
||||
.animate({ opacity: 1 }, 100, () => {
|
||||
$("#googleSignUpPopup input").trigger("focus").select();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function hide(): Promise<void> {
|
||||
if (!$("#googleSignUpPopupWrapper").hasClass("hidden")) {
|
||||
if (signedInUser !== undefined) {
|
||||
Notifications.add("Sign up process canceled", 0, 5);
|
||||
LoginPage.hidePreloader();
|
||||
LoginPage.enableInputs();
|
||||
if (signedInUser && getAdditionalUserInfo(signedInUser)?.isNewUser) {
|
||||
Ape.users.delete();
|
||||
signedInUser.user.delete();
|
||||
}
|
||||
AccountController.signOut();
|
||||
signedInUser = undefined;
|
||||
}
|
||||
$("#googleSignUpPopupWrapper")
|
||||
.stop(true, true)
|
||||
.css("opacity", 1)
|
||||
.animate(
|
||||
{
|
||||
opacity: 0,
|
||||
},
|
||||
100,
|
||||
() => {
|
||||
$("#googleSignUpPopupWrapper").addClass("hidden");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function apply(): Promise<void> {
|
||||
if ($("#googleSignUpPopup .button").hasClass("disabled")) return;
|
||||
disableInput();
|
||||
disableButton();
|
||||
if (!signedInUser) {
|
||||
return Notifications.add(
|
||||
"Missing user credential. Please close the popup and try again.",
|
||||
-1
|
||||
);
|
||||
}
|
||||
Loader.show();
|
||||
const name = $("#googleSignUpPopup input").val() as string;
|
||||
try {
|
||||
if (name.length === 0) throw new Error("Name cannot be empty");
|
||||
const response = await Ape.users.create(name);
|
||||
if (response.status !== 200) {
|
||||
throw response;
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
await updateProfile(signedInUser.user, { displayName: name });
|
||||
await sendEmailVerification(signedInUser.user);
|
||||
AllTimeStats.clear();
|
||||
Notifications.add("Account created", 1, 3);
|
||||
$("#menu .text-button.account .text").text(name);
|
||||
LoginPage.enableInputs();
|
||||
LoginPage.hidePreloader();
|
||||
await AccountController.loadUser(signedInUser.user);
|
||||
PageController.change("account");
|
||||
if (TestLogic.notSignedInLastResult !== null) {
|
||||
TestLogic.setNotSignedInUid(signedInUser.user.uid);
|
||||
|
||||
const resultsSaveResponse = await Ape.results.save(
|
||||
TestLogic.notSignedInLastResult
|
||||
);
|
||||
|
||||
if (resultsSaveResponse.status === 200) {
|
||||
const result = TestLogic.notSignedInLastResult;
|
||||
DB.saveLocalResult(result);
|
||||
DB.updateLocalStats({
|
||||
time:
|
||||
result.testDuration +
|
||||
result.incompleteTestSeconds -
|
||||
result.afkDuration,
|
||||
started: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
signedInUser = undefined;
|
||||
Loader.hide();
|
||||
hide();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
const message = createErrorMessage(e, "Failed to sign in with Google");
|
||||
Notifications.add(message, -1);
|
||||
LoginPage.hidePreloader();
|
||||
LoginPage.enableInputs();
|
||||
if (signedInUser && getAdditionalUserInfo(signedInUser)?.isNewUser) {
|
||||
await Ape.users.delete();
|
||||
await signedInUser.user.delete();
|
||||
}
|
||||
AccountController.signOut();
|
||||
signedInUser = undefined;
|
||||
hide();
|
||||
Loader.hide();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function updateIndicator(
|
||||
state: "checking" | "available" | "unavailable" | "taken" | "none",
|
||||
balloon?: string
|
||||
): void {
|
||||
$("#googleSignUpPopup .checkStatus .checking").addClass("hidden");
|
||||
$("#googleSignUpPopup .checkStatus .available").addClass("hidden");
|
||||
$("#googleSignUpPopup .checkStatus .unavailable").addClass("hidden");
|
||||
$("#googleSignUpPopup .checkStatus .taken").addClass("hidden");
|
||||
if (state !== "none") {
|
||||
$("#googleSignUpPopup .checkStatus ." + state).removeClass("hidden");
|
||||
if (balloon) {
|
||||
$("#googleSignUpPopup .checkStatus ." + state).attr(
|
||||
"aria-label",
|
||||
balloon
|
||||
);
|
||||
} else {
|
||||
$("#googleSignUpPopup .checkStatus ." + state).removeAttr("aria-label");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enableButton(): void {
|
||||
$("#googleSignUpPopup .button").removeClass("disabled");
|
||||
}
|
||||
|
||||
function disableButton(): void {
|
||||
$("#googleSignUpPopup .button").addClass("disabled");
|
||||
}
|
||||
|
||||
function enableInput(): void {
|
||||
$("#googleSignUpPopup input").prop("disabled", false);
|
||||
}
|
||||
|
||||
function disableInput(): void {
|
||||
$("#googleSignUpPopup input").prop("disabled", true);
|
||||
}
|
||||
|
||||
$("#googleSignUpPopupWrapper").on("mousedown", (e) => {
|
||||
if ($(e.target).attr("id") === "googleSignUpPopupWrapper") {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
const checkNameDebounced = debounce(1000, async () => {
|
||||
const val = $("#googleSignUpPopup input").val() as string;
|
||||
if (!val) return;
|
||||
const response = await Ape.users.getNameAvailability(val);
|
||||
|
||||
if (response.status === 200) {
|
||||
updateIndicator("available", response.message);
|
||||
enableButton();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status == 422) {
|
||||
updateIndicator("unavailable", response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status == 409) {
|
||||
updateIndicator("taken", response.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
updateIndicator("unavailable");
|
||||
return Notifications.add(
|
||||
"Failed to check name availability: " + response.message,
|
||||
-1
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$("#googleSignUpPopup input").on("input", () => {
|
||||
setTimeout(() => {
|
||||
disableButton();
|
||||
const val = $("#googleSignUpPopup input").val() as string;
|
||||
if (val === "") {
|
||||
return updateIndicator("none");
|
||||
} else {
|
||||
updateIndicator("checking");
|
||||
checkNameDebounced();
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
|
||||
$("#googleSignUpPopup input").on("keypress", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
apply();
|
||||
}
|
||||
});
|
||||
|
||||
$("#googleSignUpPopup .button").on("click", () => {
|
||||
apply();
|
||||
});
|
||||
|
||||
$(document).on("keydown", (event) => {
|
||||
if (
|
||||
event.key === "Escape" &&
|
||||
!$("#googleSignUpPopupWrapper").hasClass("hidden")
|
||||
) {
|
||||
hide();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
subscribeToSignUpEvent((signedInUser, isNewUser) => {
|
||||
if (signedInUser && isNewUser) {
|
||||
show(signedInUser);
|
||||
}
|
||||
});
|
|
@ -349,6 +349,56 @@
|
|||
}
|
||||
}
|
||||
|
||||
#googleSignUpPopup {
|
||||
background: var(--bg-color);
|
||||
border-radius: var(--roundness);
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
width: 400px;
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--sub-color);
|
||||
}
|
||||
.inputAndIndicator {
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
position: relative;
|
||||
.checkStatus {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
/* background: red; */
|
||||
display: grid;
|
||||
grid-template-columns: 2.25rem;
|
||||
grid-template-rows: 2.25rem;
|
||||
place-items: center center;
|
||||
cursor: pointer;
|
||||
|
||||
.checking,
|
||||
.available,
|
||||
.unavailable,
|
||||
.taken {
|
||||
grid-column: 1/2;
|
||||
grid-row: 1/2;
|
||||
}
|
||||
.checking {
|
||||
color: var(--sub-color);
|
||||
}
|
||||
.available {
|
||||
color: var(--main-color);
|
||||
}
|
||||
.unavailable,
|
||||
.taken {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pbTablesPopupWrapper #pbTablesPopup {
|
||||
.title {
|
||||
color: var(--text-color);
|
||||
|
|
|
@ -297,6 +297,38 @@
|
|||
<div class="button">add</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="googleSignUpPopupWrapper" class="popupWrapper hidden">
|
||||
<div id="googleSignUpPopup">
|
||||
<div class="title">Account name</div>
|
||||
<div class="text">You need to choose a username before continuing</div>
|
||||
<div class="inputAndIndicator">
|
||||
<input type="text" placeholder="username" />
|
||||
<div class="checkStatus">
|
||||
<div
|
||||
class="checking hidden"
|
||||
data-balloon-length="large"
|
||||
data-balloon-pos="up"
|
||||
>
|
||||
<i class="fas fa-fw fa-circle-notch fa-spin"></i>
|
||||
</div>
|
||||
<div class="available hidden" data-balloon-pos="up">
|
||||
<i class="fas fa-fw fa-check"></i>
|
||||
</div>
|
||||
<div
|
||||
class="unavailable hidden"
|
||||
data-balloon-length="large"
|
||||
data-balloon-pos="up"
|
||||
>
|
||||
<i class="fas fa-fw fa-times"></i>
|
||||
</div>
|
||||
<div class="taken hidden" data-balloon-pos="up">
|
||||
<i class="fas fa-fw fa-user-friends"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button disabled">Sign up</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="customWordAmountPopupWrapper" class="popupWrapper hidden">
|
||||
<div id="customWordAmountPopup">
|
||||
<div class="title">Word amount</div>
|
||||
|
|
Loading…
Reference in a new issue