Add option for coloured edges on progress page
This commit is contained in:
@@ -61,11 +61,12 @@ service cloud.firestore {
|
|||||||
|
|
||||||
function verifyFieldTypes() {
|
function verifyFieldTypes() {
|
||||||
return verifyBoolType("sound") &&
|
return verifyBoolType("sound") &&
|
||||||
|
verifyBoolType("coloredEdges") &&
|
||||||
verifyThemeValue();
|
verifyThemeValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPossibleFields() {
|
function getPossibleFields() {
|
||||||
let requiredFields = ["sound", "theme"];
|
let requiredFields = ["sound", "theme", "coloredEdges"];
|
||||||
let optionalFields = [];
|
let optionalFields = [];
|
||||||
let allFields = requiredFields.concat(optionalFields);
|
let allFields = requiredFields.concat(optionalFields);
|
||||||
return [requiredFields, allFields];
|
return [requiredFields, allFields];
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ exports.userCreated = functions.auth.user().onCreate((user) => {
|
|||||||
return db.collection("users").doc(user.uid).set({
|
return db.collection("users").doc(user.uid).set({
|
||||||
sound: true,
|
sound: true,
|
||||||
theme: "default",
|
theme: "default",
|
||||||
|
coloredEdges: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
22
src/App.js
22
src/App.js
@@ -121,6 +121,7 @@ class App extends React.Component {
|
|||||||
userDataPresent: false,
|
userDataPresent: false,
|
||||||
sound: true,
|
sound: true,
|
||||||
theme: "default",
|
theme: "default",
|
||||||
|
coloredEdges: false,
|
||||||
pageLoading: true,
|
pageLoading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -161,9 +162,11 @@ class App extends React.Component {
|
|||||||
.then((userDoc) => {
|
.then((userDoc) => {
|
||||||
newState.sound = userDoc.data().sound;
|
newState.sound = userDoc.data().sound;
|
||||||
newState.theme = userDoc.data().theme;
|
newState.theme = userDoc.data().theme;
|
||||||
|
newState.coloredEdges = userDoc.data().coloredEdges;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
newState.sound = true;
|
newState.sound = true;
|
||||||
newState.theme = "default";
|
newState.theme = "default";
|
||||||
|
newState.coloredEdges = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,6 +237,17 @@ class App extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleColoredEdgesChange = (newColoredEdges, globalChange = false) => {
|
||||||
|
if (globalChange) firebase.firestore().collection("users")
|
||||||
|
.doc(this.state.user.uid)
|
||||||
|
.update({
|
||||||
|
coloredEdges: newColoredEdges,
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
coloredEdges: newColoredEdges,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
acceptCookies = () => {
|
acceptCookies = () => {
|
||||||
window.removeEventListener('resize', this.updateCookieNoticeMargins);
|
window.removeEventListener('resize', this.updateCookieNoticeMargins);
|
||||||
this.cookieNoticeHeight = this.cookieNotice.offsetHeight;
|
this.cookieNoticeHeight = this.cookieNotice.offsetHeight;
|
||||||
@@ -285,10 +299,14 @@ class App extends React.Component {
|
|||||||
<GroupPage db={db} functions={functions} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
|
<GroupPage db={db} functions={functions} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/settings">
|
<Route path="/settings">
|
||||||
<Settings db={db} user={this.state.user} sound={this.state.sound} handleSoundChange={this.handleSoundChange} theme={this.state.theme} handleThemeChange={this.handleThemeChange} themes={themes} logEvent={analytics.logEvent} page={this.page} />
|
<Settings db={db} user={this.state.user} sound={this.state.sound} coloredEdges={this.state.coloredEdges} handleColoredEdgesChange={this.handleColoredEdgesChange} handleSoundChange={this.handleSoundChange} theme={this.state.theme} handleThemeChange={this.handleThemeChange} themes={themes} logEvent={analytics.logEvent} page={this.page} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/progress/:progressId" exact>
|
<Route path="/progress/:progressId" exact>
|
||||||
<Progress db={db} functions={functions} user={this.state.user} sound={this.state.sound} handleSoundChange={this.handleSoundChange} theme={this.state.theme} handleThemeChange={this.handleThemeChange} themes={themes} logEvent={analytics.logEvent} page={this.page} />
|
<Progress db={db} functions={functions} user={this.state.user} sound={this.state.sound} coloredEdges={this.state.coloredEdges} handleColoredEdgesChange={this.handleColoredEdgesChange} handleSoundChange={this.handleSoundChange} theme={this.state.theme} handleThemeChange={this.handleThemeChange} themes={themes} logEvent={analytics.logEvent} page={this.page} />
|
||||||
|
{
|
||||||
|
this.state.coloredEdges &&
|
||||||
|
<div className="colored-edges"></div>
|
||||||
|
}
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/create-set" exact>
|
<Route path="/create-set" exact>
|
||||||
<CreateSet db={db} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
|
<CreateSet db={db} user={this.state.user} logEvent={analytics.logEvent} page={this.page} />
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
showSettings: false,
|
showSettings: false,
|
||||||
soundInput: this.props.sound,
|
soundInput: this.props.sound,
|
||||||
themeInput: this.props.theme,
|
themeInput: this.props.theme,
|
||||||
|
coloredEdgesInput: this.props.coloredEdges,
|
||||||
setIds: [],
|
setIds: [],
|
||||||
attemptNumber: 1,
|
attemptNumber: 1,
|
||||||
attemptHistory: {},
|
attemptHistory: {},
|
||||||
@@ -232,9 +233,16 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleColoredEdgesInputChange = (event) => {
|
||||||
|
this.setState({
|
||||||
|
coloredEdgesInput: event.target.checked,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
saveSettings = (globalChange) => {
|
saveSettings = (globalChange) => {
|
||||||
this.props.handleSoundChange(this.state.soundInput, globalChange);
|
this.props.handleSoundChange(this.state.soundInput, globalChange);
|
||||||
this.props.handleThemeChange(this.state.themeInput, globalChange);
|
this.props.handleThemeChange(this.state.themeInput, globalChange);
|
||||||
|
this.props.handleColoredEdgesChange(this.state.coloredEdgesInput, globalChange);
|
||||||
this.hideSettings();
|
this.hideSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,9 +710,11 @@ export default withRouter(class Progress extends React.Component {
|
|||||||
saveSettings={this.saveSettings}
|
saveSettings={this.saveSettings}
|
||||||
handleSoundInputChange={this.handleSoundInputChange}
|
handleSoundInputChange={this.handleSoundInputChange}
|
||||||
handleThemeInputChange={this.handleThemeInputChange}
|
handleThemeInputChange={this.handleThemeInputChange}
|
||||||
|
handleColoredEdgesInputChange={this.handleColoredEdgesInputChange}
|
||||||
themes={this.props.themes}
|
themes={this.props.themes}
|
||||||
soundInput={this.state.soundInput}
|
soundInput={this.state.soundInput}
|
||||||
themeInput={this.state.themeInput}
|
themeInput={this.state.themeInput}
|
||||||
|
coloredEdgesInput={this.state.coloredEdgesInput}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default withRouter(class Settings extends Component {
|
|||||||
],
|
],
|
||||||
soundInput: this.props.sound,
|
soundInput: this.props.sound,
|
||||||
themeInput: this.props.theme,
|
themeInput: this.props.theme,
|
||||||
|
coloredEdgesInput: this.props.coloredEdges,
|
||||||
};
|
};
|
||||||
|
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@@ -60,9 +61,16 @@ export default withRouter(class Settings extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleColoredEdgesInputChange = (event) => {
|
||||||
|
this.setState({
|
||||||
|
coloredEdgesInput: event.target.checked,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
saveSettings = (globalChange) => {
|
saveSettings = (globalChange) => {
|
||||||
this.props.handleSoundChange(this.state.soundInput, globalChange);
|
this.props.handleSoundChange(this.state.soundInput, globalChange);
|
||||||
this.props.handleThemeChange(this.state.themeInput, globalChange);
|
this.props.handleThemeChange(this.state.themeInput, globalChange);
|
||||||
|
this.props.handleColoredEdgesChange(this.state.coloredEdgesInput, globalChange);
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,9 +85,11 @@ export default withRouter(class Settings extends Component {
|
|||||||
saveSettings={this.saveSettings}
|
saveSettings={this.saveSettings}
|
||||||
handleSoundInputChange={this.handleSoundInputChange}
|
handleSoundInputChange={this.handleSoundInputChange}
|
||||||
handleThemeInputChange={this.handleThemeInputChange}
|
handleThemeInputChange={this.handleThemeInputChange}
|
||||||
|
handleColoredEdgesInputChange={this.handleColoredEdgesInputChange}
|
||||||
themes={this.props.themes}
|
themes={this.props.themes}
|
||||||
soundInput={this.state.soundInput}
|
soundInput={this.state.soundInput}
|
||||||
themeInput={this.state.themeInput}
|
themeInput={this.state.themeInput}
|
||||||
|
coloredEdgesInput={this.state.coloredEdgesInput}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="settings-save-container">
|
<div className="settings-save-container">
|
||||||
|
|||||||
@@ -10,17 +10,27 @@ export default class SettingsContent extends Component {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="settings-header">Settings</h1>
|
<h1 className="settings-header">Settings</h1>
|
||||||
<label className="settings-sound-container">
|
<div className="settings-options-container">
|
||||||
<Checkbox
|
<label>
|
||||||
checked={this.props.soundInput}
|
<Checkbox
|
||||||
onChange={this.props.handleSoundInputChange}
|
checked={this.props.soundInput}
|
||||||
inputProps={{ 'aria-label': 'checkbox' }}
|
onChange={this.props.handleSoundInputChange}
|
||||||
/>
|
inputProps={{ 'aria-label': 'checkbox' }}
|
||||||
<span>Sound</span>
|
/>
|
||||||
</label>
|
<span>Sound</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<Checkbox
|
||||||
|
checked={this.props.coloredEdgesInput}
|
||||||
|
onChange={this.props.handleColoredEdgesInputChange}
|
||||||
|
inputProps={{ 'aria-label': 'checkbox' }}
|
||||||
|
/>
|
||||||
|
<span>Coloured edges</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2 className="settings-theme-header">Theme</h2>
|
<h2 className="settings-theme-header">Theme</h2>
|
||||||
<div className="settings-themes-container">
|
<div className="settings-options-container">
|
||||||
{
|
{
|
||||||
this.props.themes.map((theme) =>
|
this.props.themes.map((theme) =>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ html {
|
|||||||
--background-color: #111111;
|
--background-color: #111111;
|
||||||
--background-color-dark: #000000;
|
--background-color-dark: #000000;
|
||||||
--overlay-color: #333333;
|
--overlay-color: #333333;
|
||||||
background-color: var(--background-color);
|
|
||||||
|
height: 100%;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
||||||
--default: #2a8c8c;
|
--default: #2a8c8c;
|
||||||
@@ -112,10 +113,6 @@ html {
|
|||||||
--primary-color-dark: var(--orange-dark);
|
--primary-color-dark: var(--orange-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 1080px;
|
max-width: 1080px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@@ -126,6 +123,7 @@ body, #root, #root > div, #root > div > div:first-child {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@@ -409,6 +407,18 @@ label .MuiIconButton-label > input {
|
|||||||
opacity: 0 !important;
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.colored-edges {
|
||||||
|
z-index: -5;
|
||||||
|
background-color: var(--primary-color-dark);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 420px) {
|
@media screen and (max-width: 420px) {
|
||||||
.progress-history-container > div > *:nth-child(2), .progress-history-container--complete > div > *:nth-last-child(3), .progress-history-container--incomplete > div > *:nth-last-child(4) {
|
.progress-history-container > div > *:nth-child(2), .progress-history-container--complete > div > *:nth-last-child(3), .progress-history-container--incomplete > div > *:nth-last-child(4) {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.settings-themes-container {
|
.settings-options-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
column-gap: 16px;
|
column-gap: 16px;
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-header, .settings-sound-container, .settings-themes-container {
|
.settings-header, .settings-options-container {
|
||||||
margin-bottom: 36px;
|
margin-bottom: 36px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,20 +51,20 @@ describe("Parandum Firestore database", () => {
|
|||||||
|
|
||||||
it("Can update current user's user collection", async () => {
|
it("Can update current user's user collection", async () => {
|
||||||
const admin = getAdminFirestore();
|
const admin = getAdminFirestore();
|
||||||
await admin.collection("users").doc(myId).set({ sound: true });
|
await admin.collection("users").doc(myId).set({ sound: true, theme: "default", coloredEdges: false });
|
||||||
|
|
||||||
const db = getFirestore(myAuth);
|
const db = getFirestore(myAuth);
|
||||||
const myTestDoc = db.collection("users").doc(myId);
|
const myTestDoc = db.collection("users").doc(myId);
|
||||||
await firebase.assertSucceeds(myTestDoc.update({ sound: false }));
|
await firebase.assertSucceeds(myTestDoc.update({ sound: false, theme: "pink", coloredEdges: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can't update current user's user collection with invalid data types", async () => {
|
it("Can't update current user's user collection with invalid data types", async () => {
|
||||||
const admin = getAdminFirestore();
|
const admin = getAdminFirestore();
|
||||||
await admin.collection("users").doc(myId).set({ sound: true, theme: "default" });
|
await admin.collection("users").doc(myId).set({ sound: true, theme: "default", coloredEdges: false });
|
||||||
|
|
||||||
const db = getFirestore(myAuth);
|
const db = getFirestore(myAuth);
|
||||||
const myTestDoc = db.collection("users").doc(myId);
|
const myTestDoc = db.collection("users").doc(myId);
|
||||||
await firebase.assertFails(myTestDoc.update({ sound: 0, theme: 0 }));
|
await firebase.assertFails(myTestDoc.update({ sound: 0, theme: 0, coloredEdges: 0 }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can't update current user's user collection with invalid fields", async () => {
|
it("Can't update current user's user collection with invalid fields", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user