diff --git a/src/components/app/CreatorView.svelte b/src/components/app/CreatorView.svelte index 63ad55bad..387d7fb83 100644 --- a/src/components/app/CreatorView.svelte +++ b/src/components/app/CreatorView.svelte @@ -14,7 +14,7 @@ >{/if}{creator ? creator.email === null ? '—' - : anonymize + : anonymize || creator.email.endsWith('@wordplay.dev') ? creator.email.split('@')[0].substring(0, 4) : creator.email : $locales.get((l) => l.ui.page.login.anonymous)} div { font-style: italic; - font-size: 14pt; + font-size: calc(var(--wordplay-font-size) - 2pt); color: var(--wordplay-inactive-color); } diff --git a/src/components/widgets/TextField.svelte b/src/components/widgets/TextField.svelte index 4cfdb388b..eb7275536 100644 --- a/src/components/widgets/TextField.svelte +++ b/src/components/widgets/TextField.svelte @@ -13,7 +13,7 @@ export let right = false; export let defaultFocus = false; export let editable = true; - export let email = false; + export let kind: 'email' | 'password' | undefined = undefined; let width = 0; @@ -23,7 +23,8 @@ } onMount(() => { - if (email && view) view.type = 'email'; + if (kind === 'email' && view) view.type = 'email'; + else if (kind === 'password' && view) view.type = 'password'; }); @@ -47,7 +48,11 @@ on:blur={() => (done ? done(text) : undefined)} /> {text.length === 0 ? placeholder : text}{text.length === 0 + ? placeholder + : kind === 'password' + ? '•'.repeat(text.length) + : text} diff --git a/src/locale/UITexts.ts b/src/locale/UITexts.ts index 81fff7c76..221126df4 100644 --- a/src/locale/UITexts.ts +++ b/src/locale/UITexts.ts @@ -647,6 +647,12 @@ type UITexts = { login: { /** Header for the login page when not logged in */ header: string; + subheader: { + /** Header for logging in via email */ + email: string; + /** Header for logging in via username and password */ + username: string; + }; prompt: { /** Prompts creator to login to save their work */ login: string; @@ -658,6 +664,10 @@ type UITexts = { enter: string; /** Encouragement to go create after logging in. */ play: string; + /** Gives rules for emails above the login form */ + emailrules: string; + /** Gives rules for usernames and passwords above the login form */ + usernamerules: string; /** Offers to log out the creator. */ logout: string; /** Shown briefly before page redirects to projects */ @@ -670,12 +680,18 @@ type UITexts = { reallyDelete: string; /** Pick an emoji as a name */ name: string; + /** Text for age prompt */ + age: ModeText<[string, string]>; }; /** Shown in the footer a creator is not logged in. */ anonymous: string; field: { /** The login email */ email: FieldText; + /** The login username */ + username: FieldText; + /** The login password */ + password: FieldText; }; feedback: { /** Change email pending */ @@ -694,10 +710,12 @@ type UITexts = { failure: string; /** When there's no connection to Firebase */ offline: string; - /** When the email addres couldn't be changed for unknown reasons. */ + /** When the email address couldn't be changed for unknown reasons. */ unchanged: string; /** When account deletion failed */ delete: string; + /** When a password is wrong */ + wrongPassword: string; }; button: { /** Log out of the account */ diff --git a/src/locale/en-US.json b/src/locale/en-US.json index 611fd264d..ad3051df9 100644 --- a/src/locale/en-US.json +++ b/src/locale/en-US.json @@ -4168,11 +4168,21 @@ }, "login": { "header": "Login", + "subheader": { + "email": "email link", + "username": "username + password" + }, "anonymous": "login", "prompt": { "login": "Your performances are saving on this device, but not online. If this is your device, that's okay! If it's not, others may see or delete your projects. To save them online and keep them private, log in.", + "age": { + "label": "What's your age?", + "modes": ["12 or younger", "13 or older"] + }, "enter": "It looks like your login link came from a different browser or device. Can you enter your email again, just so we're sure it's you?", "play": "You're logged in, we can save your projects online now! Want to create something?", + "emailrules": "Don't provide your email if you are 12 or younger.", + "usernamerules": "Usernames should not contain identiable information. Passwords must be at least 10 characters long.", "change": "Want to change your email? Submit a new one and we'll send a confirmation to the old one.", "sent": "Check your email for a login link.", "logout": "Leaving a shared device and want to keep your projects private? Logout and we'll remove your projects from this device. They will still be stored online.", @@ -4189,7 +4199,8 @@ "failure": "Unable to login :(", "offline": "You appear to be offline.", "unchanged": "We couldn't change your email address, but we don't know why.", - "delete": "We couldn't delete your account, but we don't know why." + "delete": "We couldn't delete your account, but we don't know why.", + "wrongPassword": "Not a valid username and password" }, "feedback": { "changing": "Submitting new email...", @@ -4199,6 +4210,14 @@ "email": { "description": "edit login email", "placeholder": "email" + }, + "username": { + "description": "login username, don't use personally identifiable information", + "placeholder": "username" + }, + "password": { + "description": "login password, at least 10 character", + "placeholder": "password" } }, "button": { @@ -4226,11 +4245,11 @@ "The first thing to know is that we are not a commercial entity. We are a community-based research project housed at a not-for-profit university. Our goal is to create a platform that brings you joy and helps us make discoveries about a more equitable and just world of computing. We have no interest in making money on this platform; any money we gather (usually through public funding) is used to sustain the platform, not to enrich anyone who works on it (or contributes to it).", "Because we are not seeking profit, this also means that we can't make any promises about the reliability, availability, or longevity of this platform. That said, is committed long term to sustaining it, and as a tenured professor, she's got a pretty stable gig.", "That brings is to *data*. Here's what we gather and store in the cloud:", - "• Your *email address*. We use this to ensure that only you and anyone you share with can access your projects and settings.", - "• Your *projects*. We store any projects you contribute. This includes any personally identifiable information you put in your projects (which could be anything, since it's all text!)", + "• If you are 13 or older, your *email address*. We use this to ensure that only you and anyone you share with can access your projects and settings. If you are younger than 13, then we only store the username you choose, which you should ensure doesn't contain any personally identiable information.", + "• Your *projects*. We store any projects you contribute.", "• Your *settings*. This includes the locales you choose, your animation preferences, and your tutorial progress. Everything else is stored on your device.", - "• *Traffic*. We use basic to gather aggregate, anonymous data about which pages people and projects people are visiting and how many times. We use this to help raise funding to sustain the platform.", - "We don't store anything else. No 'cookies' other than those used by Google Analytics, no tracking identifiers, no recordings of camera or microphone input. Our is public, so you can verify this if you wish at any time.", + "• Your anonymized *activity*. We track the projects you view, the size of your screen, when you leave the site, when you read to the end of a page, and when you login. We use this to help prioritize engineering work, and to help raise funding by reporting how much the platform is being used in the aggregate. None of these events are in linked to you, and we do not track you across websites.", + "We don't store anything else. No 'cookies', no IP tracking, no recordings of any camera or microphone input. Our is public, so anyone can verify this, and report any unintended tracking.", "*You* own your data, not us. That means:", "• You control who can access your projects. They are private by default, but you can share them with individuals, groups, or make them entirely public.", "• You can fully delete any project or your own account at any time.", diff --git a/src/routes/login/Login.svelte b/src/routes/login/Login.svelte index f8eb2930d..415401ce3 100644 --- a/src/routes/login/Login.svelte +++ b/src/routes/login/Login.svelte @@ -1,7 +1,9 @@
{user.displayName ?? '😃'}{user.email}
{user.email?.replace('@wordplay.dev', '')}
@@ -113,33 +118,36 @@ >

-
-

{$locales.get((l) => l.ui.page.login.prompt.change)}

-
- l.ui.page.login.field.email.description - )} - placeholder={$locales.get( - (l) => l.ui.page.login.field.email.placeholder - )} - bind:text={newEmail} - editable={!changeSubmitted} - /> - {#if changeSubmitted} l.ui.page.login.feedback.changing + {#if user && !username} +
+

{$locales.get((l) => l.ui.page.login.prompt.change)}

+ + l.ui.page.login.field.email.description )} - /> - {:else if changeFeedback}{changeFeedback}{/if} - -
+ placeholder={$locales.get( + (l) => l.ui.page.login.field.email.placeholder + )} + bind:text={newEmail} + editable={!changeSubmitted} + /> + {#if changeSubmitted} l.ui.page.login.feedback.changing + )} + /> + {:else if changeFeedback}{changeFeedback}{/if} + +
+ {/if}
{#if !deleteSubmitted}

{$locales.get((l) => l.ui.page.login.prompt.delete)}

@@ -166,13 +174,18 @@ : undefined} > l.ui.page.login.field.email.description + description={$locales.get((l) => + username + ? l.ui.page.login.field.username.description + : l.ui.page.login.field.email.description )} - placeholder={$locales.get( - (l) => l.ui.page.login.field.email.placeholder + placeholder={$locales.get((l) => + username + ? l.ui.page.login.field.username.placeholder + : l.ui.page.login.field.email.placeholder )} fill={true} + kind={username ? undefined : 'email'} bind:text={confirmEmail} />