diff --git a/.gitignore b/.gitignore index c6f8bcd..3f51e21 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,31 @@ node_modules/ # Settings .idea/ +======= +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +api/config/config.env +database diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c21f396..eb78a80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ Follow the instructions with [local](README.md#local-environment) or [container] ## Languages and style guide -The project currently uses static files to render the content, and run client-side JavaScript. HTML, JavaScript and CSS knowledge is recommended for code contributions. +The project currently uses static files to render the content, and run database-side JavaScript. HTML, JavaScript and CSS knowledge is recommended for code contributions. Please follow the existing code style, and avoid changes in Pull Requests, for example indent changes. diff --git a/api/auth/strategy/GoogleStrategy.js b/api/auth/strategy/GoogleStrategy.js new file mode 100644 index 0000000..968bd3e --- /dev/null +++ b/api/auth/strategy/GoogleStrategy.js @@ -0,0 +1,39 @@ +const GoogleStrategy = require("passport-google-oauth20").Strategy; +const User = require("../../models/User"); +const defaultHelpers = require("../../serverHelpers/defaultsHelpers"); + +const googleStrategy = new GoogleStrategy({ + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: "/api/auth/google/callback", +}, authCallback); + +// We identify users at login using emails, which allows users to sign in with +// multiple kinds of accounts sharing the same email. +async function authCallback(accessToken, refreshToken, profile, done) { + try { + const currentEmail = profile["emails"][0]["value"]; + const existingUser = await User.findOne({email: currentEmail}); + if (existingUser) { + done(null, existingUser); + } else { + const newUser = new User({ + googleId: profile["id"], + email: currentEmail, + profilePicture: profile["photos"][0]["value"], + displayName: profile["displayName"], + name: { + givenName: profile["name"]["givenName"], + familyName: profile["name"]["familyName"], + } + }); + await newUser.save(); + await defaultHelpers.loadButtonDefaultsForNewUser(newUser._id.toString()); + done(null, newUser); + } + } catch (e) { + console.error(e); + } +} + +module.exports = googleStrategy; \ No newline at end of file diff --git a/api/config/cloudinary.js b/api/config/cloudinary.js new file mode 100644 index 0000000..a96f49b --- /dev/null +++ b/api/config/cloudinary.js @@ -0,0 +1,9 @@ +const cloudinary = require("cloudinary").v2; + +cloudinary.config({ + cloud_name: process.env.CLOUDINARY_CLOUD_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, +}); + +module.exports = cloudinary; \ No newline at end of file diff --git a/api/config/database.js b/api/config/database.js new file mode 100644 index 0000000..bffca00 --- /dev/null +++ b/api/config/database.js @@ -0,0 +1,22 @@ +const mongoose = require("mongoose"); + +let database = new mongoose.Mongoose(); +let dbname = "userdata"; +let parameters = "?retryWrites=true&w=majority"; +let dbConnectionString; + +if (process.env.MODE === "production") { + dbConnectionString = process.env.MONGODB_URI_PRODUCTION + dbname + parameters; +} else { + dbConnectionString = process.env.MONGODB_URI_DEVELOPMENT + dbname + parameters; +} + +database.connect(dbConnectionString).catch(e => { + console.log(e.message); + process.exit(); +}); + +module.exports = { + database, + dbConnectionString +}; \ No newline at end of file diff --git a/api/config/multer.js b/api/config/multer.js new file mode 100644 index 0000000..e8c760c --- /dev/null +++ b/api/config/multer.js @@ -0,0 +1,19 @@ +let multer = require("multer"); +let path = require("path"); + +const VALID_EXT_NAMES = [".png", ".jpeg", ".jpg", ".svg"]; +const MAX_MEGABYTES_ALLOWED = 5; + +module.exports = multer({ + dest: path.join(__dirname, "..", "tmp", "uploads"), + fileFilter(req, file, callback) { + let ext = path.extname(file.originalname); + if (!VALID_EXT_NAMES.includes(ext)) { + callback(new Error(`File must be one of these types: ${VALID_EXT_NAMES.join(", ")}`), false); + } else if (file.size >= MAX_MEGABYTES_ALLOWED * 1024 ** 2) { + callback(new Error(`File must be smaller than ${MAX_MEGABYTES_ALLOWED} megabytes.`), false); + } else { + callback(null, true); + } + }, +}); \ No newline at end of file diff --git a/api/config/passport.js b/api/config/passport.js new file mode 100644 index 0000000..50b31ce --- /dev/null +++ b/api/config/passport.js @@ -0,0 +1,46 @@ +const passport = require("passport"); +const session = require("express-session"); +const googleStrategy = require("../auth/strategy/GoogleStrategy"); +const Users = require("../models/User"); +const MongoStore = require("connect-mongo"); +const {dbConnectionString} = require("./database"); + +/** + * Adds Google authorization and other OAuth middlewares in the future. + * + * @param expressApp {Express} The ExpressJS application to add patches to. + */ +function addAuthorization(expressApp) { + const newSession = session({ + secret: "TalkToMe", + resave: false, + saveUninitialized: false, + store: new MongoStore({ + mongoUrl: dbConnectionString + }), + }); + + expressApp.use(newSession); + expressApp.use(passport.initialize({})); + expressApp.use(passport.session({})); + passport.use(googleStrategy); + + passport.serializeUser((user, done) => { + console.log(user._id); + done(null, user._id); + }); + passport.deserializeUser((id, done) => { + void async function() { + let user, error = null; + try { + user = await Users.findById(id); + } catch (e) { + error = e; + } + done(error, user); + }(); + }); +} + + +module.exports = addAuthorization; \ No newline at end of file diff --git a/api/config/server.js b/api/config/server.js new file mode 100644 index 0000000..e23ecf5 --- /dev/null +++ b/api/config/server.js @@ -0,0 +1,32 @@ +// Environment setup. +const dotenv = require("dotenv"); +const path = require("path"); +dotenv.config({path: path.join(__dirname, "config.env")}); + +// Database setup. +require("./database"); +console.log("Connecting to MongoDB server."); + +// Servers and middlewares. +const express = require("express"); +const cors = require("cors"); +const app = express(); + +app.use(express.urlencoded({extended: true})); +app.use(express.json()); +app.use(cors()); + +// Authorization middleware and session login. +const addAuthorization = require("./passport"); +addAuthorization(app); + +let PORT = process.env.PORT || process.env.PORT_DEVELOPMENT; +if (process.env.MODE !== "production") { + PORT = process.env.PORT_DEVELOPMENT; +} + +app.listen(PORT, () => { + console.log("Listening at port " + PORT); +}); + +module.exports = {app}; \ No newline at end of file diff --git a/api/defaults/defaultConfiguration.js b/api/defaults/defaultConfiguration.js new file mode 100644 index 0000000..2739086 --- /dev/null +++ b/api/defaults/defaultConfiguration.js @@ -0,0 +1,590 @@ +module.exports = [ + { + title: 'I', + imageSrc: '/assets/images/default/I_23542.png', + imageAlt: 'pointing at self', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'me', + imageSrc: '/assets/images/default/face_15743.png', + imageAlt: 'person', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'my', + imageSrc: '/assets/images/default/self%20esteem_19219.png', + imageAlt: 'someone thinking about their stuff', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'it', + imageSrc: '/assets/images/default/It_23474.png', + imageAlt: 'arrows pointing at an item', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'your', + imageSrc: '/assets/images/default/Your%20Turn!_47716.png', + imageAlt: 'pointing at someone for your', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'you', + imageSrc: '/assets/images/default/Point_36483.png', + imageAlt: 'pointing at person', + bgColor: 'yellow', + textColor: '#000' + }, + { + title: 'how', + imageSrc: '/assets/images/default/How__14819.png', + imageAlt: 'question mark', + bgColor: 'purple', + textColor: '#fff' + }, + { + title: 'who', + imageSrc: '/assets/images/default/who_14192.png', + imageAlt: 'question mark on a persons face', + bgColor: 'purple', + textColor: '#fff' + }, + { + title: 'why', + imageSrc: '/assets/images/default/why_46802.png', + imageAlt: 'person asking why', + bgColor: 'purple', + textColor: '#fff' + }, + { + title: 'yes', + imageSrc: '/assets/images/default/yes_17011.png', + imageAlt: 'green check mark', + bgColor: 'white', + textColor: '#000' + }, + { + title: 'no', + imageSrc: '/assets/images/default/no_16959.png', + imageAlt: 'red x mark', + bgColor: 'red', + textColor: '#fff' + }, + { + title: 'again', + imageSrc: '/assets/images/default/again_47245.png', + imageAlt: 'loop going around again', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'problem', + imageSrc: '/assets/images/default/problem_23791.png', + imageAlt: 'someone showing they have a problem', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'and', + imageSrc: '/assets/images/default/and_47645.svg', + imageAlt: 'the word and', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'all', + imageSrc: '/assets/images/default/Everything_27600.png', + imageAlt: 'pointing at all the shapes', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'at', + imageSrc: '/assets/images/default/At%20Sign_36289.png', + imageAlt: 'at sign', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'some', + imageSrc: '/assets/images/default/Some_47563.svg', + imageAlt: 'showing some of something and not all of it', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'for', + imageSrc: '/assets/images/default/for_47639.png', + imageAlt: 'word for', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'on', + imageSrc: '/assets/images/default/Green%20Circle_43665.png', + imageAlt: 'green on button', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'here', + imageSrc: '/assets/images/default/Arrow%20down_44351.png', + imageAlt: 'arrow pointing down', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'in', + imageSrc: '/assets/images/default/in_17714.png', + imageAlt: 'arrow pointing into a box', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'up', + imageSrc: '/assets/images/default/Arrow%20up_44348.png', + imageAlt: 'arrow pointing up', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'off', + imageSrc: '/assets/images/default/Red%20Circle_43662.png', + imageAlt: 'red off button', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'out', + imageSrc: '/assets/images/default/Garbage%20Bag%20Out_40186.png', + imageAlt: 'garbage bag being taken out', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'down', + imageSrc: '/assets/images/default/Down%20Arrow_38048.png', + imageAlt: 'arrow pointing down', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'with', + imageSrc: '/assets/images/default/Carry_33621.png', + imageAlt: 'a person carrying an object with them', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'are', + imageSrc: '/assets/images/default/transparent.svg', + imageAlt: 'arrow pointing to person', + bgColor: 'skyBlue', + textColor: '#000' + }, + { + title: 'now', + imageSrc: '/assets/images/default/now_44934.png', + imageAlt: 'clock showing time is now', + bgColor: 'lightPurple', + textColor: '#000' + }, + { + title: 'please', + imageSrc: '/assets/images/default/Palms%20Up%20Together_41055.svg', + imageAlt: 'hands in a please position', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'thank-you', + imageSrc: '/assets/images/default/please_18234.png', + imageAlt: 'box showing arrow to', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'happy', + imageSrc: '/assets/images/default/happy_16265.png', + imageAlt: 'someone smiling', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'sad', + imageSrc: '/assets/images/default/sad_15665.png', + imageAlt: 'someone with a sad face', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'tired', + imageSrc: '/assets/images/default/tired_15386.png', + imageAlt: 'someone with a tired face', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'ok', + imageSrc: '/assets/images/default/ok_23430.png', + imageAlt: 'ok sign in sign language', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'that', + imageSrc: '/assets/images/default/head_15732.png', + imageAlt: 'arrow pointing at head', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'a', + imageSrc: '/assets/images/default/a_16046.png', + imageAlt: 'the letter a', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'the', + imageSrc: '/assets/images/default/the_47642.svg', + imageAlt: 'the word the', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'more', + imageSrc: '/assets/images/default/Add%20Button_44228.svg', + imageAlt: 'add button', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'cool', + imageSrc: '/assets/images/default/ice_17807.png', + imageAlt: 'ice cube', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'first', + imageSrc: '/assets/images/default/first_23704.png', + imageAlt: 'showing someone standing first in line', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'then', + imageSrc: '/assets/images/default/Think%20Then%20Do_37833.png', + imageAlt: 'think then do', + bgColor: 'lightPink', + textColor: '#000' + }, + { + title: 'bad', + imageSrc: '/assets/images/default/bad_16939.png', + imageAlt: 'thumb pointing down', + bgColor: 'darkPink', + textColor: '#000' + }, + { + title: 'good', + imageSrc: '/assets/images/default/Thumbs%20Up_41001.svg', + imageAlt: 'thumb pointing up', + bgColor: 'darkPink', + textColor: '#000' + }, + { + title: 'am', + imageSrc: '/assets/images/default/I%20am%20fine_15187.png', + imageAlt: 'I am fine drawing', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'is', + imageSrc: '/assets/images/default/what%20is%20that__14281.png', + imageAlt: 'finger pointing at question mark', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'will', + imageSrc: '/assets/images/default/White%20Question%20Mark_43583.svg', + imageAlt: 'white question mark', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'do', + imageSrc: '/assets/images/default/Do_26106.png', + imageAlt: 'list of stuff to do', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'can', + imageSrc: '/assets/images/default/Question_32387.png', + imageAlt: 'question thought bubble', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'have', + imageSrc: '/assets/images/default/have_17947.png', + imageAlt: 'red ball in someones hand', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'wait', + imageSrc: '/assets/images/default/stop_17875.png', + imageAlt: 'someone holding their hand up in a stop position', + bgColor: 'lightGreen', + textColor: '#000' + }, + { + title: 'be', + imageSrc: '/assets/images/default/be_46567.png', + imageAlt: 'pointing at self', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'feel', + imageSrc: '/assets/images/default/feelings_15127.png', + imageAlt: 'faces showing feelings', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'give', + imageSrc: '/assets/images/default/give_21931.png', + imageAlt: 'showing someone giving someone a ball', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'listen', + imageSrc: '/assets/images/default/listen%20to%20music_22609.png', + imageAlt: 'someone listening to music', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'come', + imageSrc: '/assets/images/default/Please%20come%20here_6404.svg', + imageAlt: 'hand waving come', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'hurt', + imageSrc: '/assets/images/default/Hurt_6390.svg', + imageAlt: 'face with band-aid showing hurt', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'hear', + imageSrc: '/assets/images/default/Hear%20,%20To_4756.svg', + imageAlt: 'ear with hand up to it', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'know', + imageSrc: '/assets/images/default/know_19512.png', + imageAlt: 'someone raising their hand in class', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'eat', + imageSrc: '/assets/images/default/Eat%20Bread_28117.png', + imageAlt: 'someone eating', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'drink', + imageSrc: '/assets/images/default/Drink_33627.png', + imageAlt: 'someone drinking', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'finish', + imageSrc: '/assets/images/default/Run%20To%20Finish_30175.png', + imageAlt: 'running to finish line', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'get', + imageSrc: '/assets/images/default/Target_38031.png', + imageAlt: 'pointing at an object with a question mark', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'love', + imageSrc: '/assets/images/default/Heart_29191.png', + imageAlt: 'heart', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'make', + imageSrc: '/assets/images/default/i%20will%20do_47673.png', + imageAlt: 'someone making something with a hammer', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'need', + imageSrc: '/assets/images/default/can%20I%20help%20you__19233.png', + imageAlt: 'sign language need help', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'go', + imageSrc: '/assets/images/default/Go_26054.png', + imageAlt: 'someone walking', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'help', + imageSrc: '/assets/images/default/I%20need%20help_6403.png', + imageAlt: 'reaching out for help', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'open', + imageSrc: '/assets/images/default/open_14053.png', + imageAlt: 'hands opening', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'put', + imageSrc: '/assets/images/default/put_24019.png', + imageAlt: 'putting a box on a table', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'talk', + imageSrc: '/assets/images/default/talk%20at%20once_46488.png', + imageAlt: 'people talking', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'look', + imageSrc: '/assets/images/default/What%20are%20you%20looking%20at__18027.png', + imageAlt: 'looking at someone', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'like', + imageSrc: '/assets/images/default/like_19945.png', + imageAlt: 'holding something and smiling with a thumbs up', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'play', + imageSrc: '/assets/images/default/play_17437.png', + imageAlt: 'two people playing ball', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'read', + imageSrc: '/assets/images/default/read_17820.png', + imageAlt: 'someone reading a book', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'stop', + imageSrc: '/assets/images/default/stop_18310.png', + imageAlt: 'stop sign', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'walk', + imageSrc: '/assets/images/default/walk_17197.png', + imageAlt: 'someone walking', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'show', + imageSrc: '/assets/images/default/show_14528.png', + imageAlt: 'showing off a ball', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'want', + imageSrc: '/assets/images/default/want_46605.png', + imageAlt: 'someone reaching for an item', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'take', + imageSrc: '/assets/images/default/want_46605.png', + imageAlt: 'reaching for something they want', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'tell', + imageSrc: '/assets/images/default/Talk_30920.png', + imageAlt: 'someone talking', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'turn', + imageSrc: '/assets/images/default/Left%20Arrow%20Curving%20Right_43513.png', + imageAlt: 'arrow turning left', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'watch', + imageSrc: '/assets/images/default/Eyes_30543.png', + imageAlt: 'arrows pointing at eyes', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'wear', + imageSrc: '/assets/images/default/Dressed%20Properly_34899.png', + imageAlt: 'someone wearing clothes', + bgColor: 'darkGreen', + textColor: '#fff' + }, + { + title: 'work', + imageSrc: '/assets/images/default/Work_32615.png', + imageAlt: 'hands working', + bgColor: 'darkGreen', + textColor: '#fff' + } +]; \ No newline at end of file diff --git a/api/models/User.js b/api/models/User.js new file mode 100644 index 0000000..81f883f --- /dev/null +++ b/api/models/User.js @@ -0,0 +1,64 @@ +const bcrypt = require("bcrypt"); +const mongoose = require("mongoose"); +const database = require("../config/database").database; + +const NameSchema = new mongoose.Schema({ + givenName: { + required: true, + type: String, + }, + familyName: { + required: true, + type: String, + } +}) + +// Note: Users are identified by email, so that it's the same account when they use Google OAuth 2.0 or manually login. +const UsersSchema = new mongoose.Schema({ + googleId: { + type: String, + }, + password: { + type: String, + }, + email: { + type: String, + required: true, + }, + profilePicture: { + type: String, + default: () => "/assets/profile/default/profilePicture.png", + }, + displayName: { + type: String, + required: true, + }, + name: { + type: NameSchema, + required: true, + }, +}); + +UsersSchema.pre("save", async function save(next) { + const currentUser = this; + if (!currentUser.isModified("password")) { + return next(); + } else { + try { + const salt = await bcrypt.genSalt(10); + const hash = await bcrypt.hash(currentUser.password, salt); + currentUser.password = hash; + return next(); + } catch (e) { + return next(e); + } + } +}); + +UsersSchema.methods.comparePassword = async function comparePassword(candidate) { + return await bcrypt.compare(candidate, this.password); +} + +const User = database.model("Users", UsersSchema); + +module.exports = User; \ No newline at end of file diff --git a/api/models/UserButton.js b/api/models/UserButton.js new file mode 100644 index 0000000..02725f6 --- /dev/null +++ b/api/models/UserButton.js @@ -0,0 +1,37 @@ +const mongoose = require("mongoose"); +const database = require("../config/database").database; + +const UserButtonSchema = new mongoose.Schema({ + title: { + required: true, + type: String, + }, + imageSrc: { + required: true, + type: String, + }, + imageAlt: { + type: String, + }, + bgColor: { + type: String, + required: true, + default: "#ffffff", + }, + textColor: { + type: String, + required: true, + default: "#000000", + }, + userID: { + type: String, + required: true, + }, + cloudinaryID: { + type: String, + } +}); + +const UserButton = database.model("userButtons", UserButtonSchema); + +module.exports = UserButton; \ No newline at end of file diff --git a/api/routes/auth.js b/api/routes/auth.js new file mode 100644 index 0000000..790c63b --- /dev/null +++ b/api/routes/auth.js @@ -0,0 +1,73 @@ +const express = require("express"); +const passport = require("passport"); + +const router = express.Router(); + +/** + * @desc Get the information of the current user. + * @route GET /api/auth/currentUser + */ +router.get("/currentUser", async (req, res) => { + try { + const {user} = req; + if (!user) { + res.status(200).json({ + success: true, + user: null, + }); + } else { + res.status(200).json({ + success: true, + user: { + email: user.email, + profilePicture: user.profilePicture, + displayName: user.displayName, + name: { + givenName: user.name.givenName, + familyName: user.name.familyName, + }, + }, + }); + } + } catch (e) { + res.status(500).json({ + success: false, + }); + } +}); + +/** + * @desc Perform user logout. + * @route GET /api/auth/logout + */ +router.get("/logout", async (req, res) => { + try { + req.logout(() => { + req.session.destroy(); + res.redirect("/"); + }); + } catch (e) { + console.error(e); + } + + +}); + +/** + * @desc Authorization with Google. + * @route GET /api/auth/google + */ +router.get("/google", passport.authenticate("google", { + scope: ["profile", "email"] +})); + +/** + * @desc Successful authorization after the function. + * @route GET /api/auth/google/callback + */ +router.get("/google/callback", passport.authenticate("google"), (req, res) => { + res.redirect("/"); +}); + + +module.exports = router; \ No newline at end of file diff --git a/api/routes/configuration.js b/api/routes/configuration.js new file mode 100644 index 0000000..a111ab1 --- /dev/null +++ b/api/routes/configuration.js @@ -0,0 +1,99 @@ +const express = require("express"); +const {respondWithObject, respondWithFailure} = require("../serverHelpers/jsonResponses"); +const defaultConfiguration = require("../defaults/defaultConfiguration"); +const multerUploader = require("../config/multer"); +const cloudinary = require("../config/cloudinary"); +const fs = require("fs").promises; +const UserButton = require("../models/UserButton"); + +const router = express.Router(); + +router.get("/", async (req, res) => { + try { + const user = req.user; + if (!user || !req.isAuthenticated?.()) { + respondWithObject(res, defaultConfiguration); + } else { + const userButtons = await UserButton.find({ + userID: user._id.toString(), + }).lean(); + respondWithObject(res, userButtons); + } + } catch (e) { + respondWithFailure(res, { + code: 504, + error: e, + }); + } +}); + +router.post("/button", multerUploader.single("image"), async (req, res) => { + const newImage = req.file; + const currentUser = req.user; + if (!currentUser) { + respondWithFailure(res, { + code: 403, + error: null, + reason: "You must have an account to be able to add a button to our servers.", + }); + } + try { + const uploadResult = await cloudinary.uploader.upload(newImage.path); + await UserButton.create({ + title: req.body.title, + imageSrc: uploadResult.secure_url, + cloudinaryID: uploadResult.public_id, + imageAlt: req.body.imageAlt, + bgColor: req.body.bgColor, + textColor: req.body.textColor, + userID: currentUser._id, + }); + respondWithObject(res, { + success: true, + reason: null, + }) + } catch (e) { + console.error(e); + respondWithFailure(res, { + code: 500, + error: e, + reason: "Server error." + }) + } finally { + try { + await fs.rm(newImage.path); + } catch (e) { + } + } +}); + +router.delete("/button", async (req, res) => { + try { + const currentUser = req.user; + if (!currentUser) { + respondWithFailure(res, { + code: 403, + error: null, + reason: "You must be signed in to delete a custom button.", + }); + } + const userButton = await UserButton.findOne({_id: req.body.id}); + if (userButton.userID !== req.user._id.toString()) { + respondWithFailure(res, { + code: 403, + error: null, + reason: "This custom button belongs to another user, and you can't delete it.", + }); + } + const assetIDToDelete = userButton.cloudinaryID; + if (assetIDToDelete) { + console.log(await cloudinary.uploader.destroy(assetIDToDelete)); + } + await UserButton.remove({_id: req.body.id}); + respondWithObject(res, {}); + } catch (e) { + respondWithFailure(res); + } +}) + +module.exports = router; \ No newline at end of file diff --git a/api/routes/main.js b/api/routes/main.js new file mode 100644 index 0000000..2747bdf --- /dev/null +++ b/api/routes/main.js @@ -0,0 +1,12 @@ +const express = require("express"); +const morgan = require("morgan"); + +const router = express.Router(); + +if (process.env.MODE !== "production") { + router.use(morgan("dev")); +} +router.use("/configuration", require("./configuration")); +router.use("/auth", require("./auth")); + +module.exports = router; \ No newline at end of file diff --git a/api/serverHelpers/defaultsHelpers.js b/api/serverHelpers/defaultsHelpers.js new file mode 100644 index 0000000..e3d44d0 --- /dev/null +++ b/api/serverHelpers/defaultsHelpers.js @@ -0,0 +1,13 @@ +const UserButtons = require("../models/UserButton"); +const defaultConfiguration = require("../defaults/defaultConfiguration"); + +async function loadButtonDefaultsForNewUser(id) { + await UserButtons.insertMany(defaultConfiguration.map(button => ({ + ...button, + userID: id, + }))); +} + +module.exports = { + loadButtonDefaultsForNewUser +}; \ No newline at end of file diff --git a/api/serverHelpers/jsonResponses.js b/api/serverHelpers/jsonResponses.js new file mode 100644 index 0000000..2a196c7 --- /dev/null +++ b/api/serverHelpers/jsonResponses.js @@ -0,0 +1,30 @@ +/** + * Responds to an HTTP request as JSON with code 200 and an object, stating that the request has succeeded. + * @param {http.ServerResponse} response The response object to be handled. + * @param {object} object This data will be sent */ +function respondWithObject(response, object) { + const httpReturnCode = 200; + response.status(httpReturnCode).json({ + success: true, + code: httpReturnCode, + data: object, + }); +} + +/** + * Responds to an HTTP request as JSON with code 500 by default, stating that the user request has failed. + * @param {number} code The HTTP response code you want to use. + * @param {http.ServerResponse} response The response object to be handled. + * @param {Error} error The error for debugging. + * @param {string} reason The message to be sent to the user. + * */ +function respondWithFailure(response, {code=500, error=undefined, reason="Internal server error."}) { + if (error) console.error(error); + response.status(code).json({ + success: false, + code, + reason + }); +} + +module.exports = {respondWithFailure, respondWithObject}; \ No newline at end of file diff --git a/api/tmp/uploads/0e1c0f2a21e6d379fab1f19fda07458a b/api/tmp/uploads/0e1c0f2a21e6d379fab1f19fda07458a new file mode 100644 index 0000000..cb63380 Binary files /dev/null and b/api/tmp/uploads/0e1c0f2a21e6d379fab1f19fda07458a differ diff --git a/api/tmp/uploads/2355d687d049f1067589af4ecb0874dc b/api/tmp/uploads/2355d687d049f1067589af4ecb0874dc new file mode 100644 index 0000000..cb63380 Binary files /dev/null and b/api/tmp/uploads/2355d687d049f1067589af4ecb0874dc differ diff --git a/api/tmp/uploads/37dafe1232dff73d0ac21b9197f7dd3d b/api/tmp/uploads/37dafe1232dff73d0ac21b9197f7dd3d new file mode 100644 index 0000000..cb63380 Binary files /dev/null and b/api/tmp/uploads/37dafe1232dff73d0ac21b9197f7dd3d differ diff --git a/api/tmp/uploads/d5f44909f6cdd6ded43b3d029d372214 b/api/tmp/uploads/d5f44909f6cdd6ded43b3d029d372214 new file mode 100644 index 0000000..cb63380 Binary files /dev/null and b/api/tmp/uploads/d5f44909f6cdd6ded43b3d029d372214 differ diff --git a/api/tmp/uploads/ff855ea944b740c6c30e44930cc014e6 b/api/tmp/uploads/ff855ea944b740c6c30e44930cc014e6 new file mode 100644 index 0000000..cb63380 Binary files /dev/null and b/api/tmp/uploads/ff855ea944b740c6c30e44930cc014e6 differ diff --git a/img/Add Button_44228.svg b/assets/images/default/Add Button_44228.svg similarity index 100% rename from img/Add Button_44228.svg rename to assets/images/default/Add Button_44228.svg diff --git a/img/Arrow down_44351.png b/assets/images/default/Arrow down_44351.png similarity index 100% rename from img/Arrow down_44351.png rename to assets/images/default/Arrow down_44351.png diff --git a/img/Arrow up_44348.png b/assets/images/default/Arrow up_44348.png similarity index 100% rename from img/Arrow up_44348.png rename to assets/images/default/Arrow up_44348.png diff --git a/img/At Sign_36289.png b/assets/images/default/At Sign_36289.png similarity index 100% rename from img/At Sign_36289.png rename to assets/images/default/At Sign_36289.png diff --git a/img/Carry_33621.png b/assets/images/default/Carry_33621.png similarity index 100% rename from img/Carry_33621.png rename to assets/images/default/Carry_33621.png diff --git a/img/Do_26106.png b/assets/images/default/Do_26106.png similarity index 100% rename from img/Do_26106.png rename to assets/images/default/Do_26106.png diff --git a/img/Down Arrow_38048.png b/assets/images/default/Down Arrow_38048.png similarity index 100% rename from img/Down Arrow_38048.png rename to assets/images/default/Down Arrow_38048.png diff --git a/img/Dressed Properly_34899.png b/assets/images/default/Dressed Properly_34899.png similarity index 100% rename from img/Dressed Properly_34899.png rename to assets/images/default/Dressed Properly_34899.png diff --git a/img/Drink_33627.png b/assets/images/default/Drink_33627.png similarity index 100% rename from img/Drink_33627.png rename to assets/images/default/Drink_33627.png diff --git a/img/Eat Bread_28117.png b/assets/images/default/Eat Bread_28117.png similarity index 100% rename from img/Eat Bread_28117.png rename to assets/images/default/Eat Bread_28117.png diff --git a/img/Everything_27600.png b/assets/images/default/Everything_27600.png similarity index 100% rename from img/Everything_27600.png rename to assets/images/default/Everything_27600.png diff --git a/img/Eyes_30543.png b/assets/images/default/Eyes_30543.png similarity index 100% rename from img/Eyes_30543.png rename to assets/images/default/Eyes_30543.png diff --git a/img/Garbage Bag Out_40186.png b/assets/images/default/Garbage Bag Out_40186.png similarity index 100% rename from img/Garbage Bag Out_40186.png rename to assets/images/default/Garbage Bag Out_40186.png diff --git a/img/Go_26054.png b/assets/images/default/Go_26054.png similarity index 100% rename from img/Go_26054.png rename to assets/images/default/Go_26054.png diff --git a/img/Green Circle_43665.png b/assets/images/default/Green Circle_43665.png similarity index 100% rename from img/Green Circle_43665.png rename to assets/images/default/Green Circle_43665.png diff --git a/img/Hear , To_4756.svg b/assets/images/default/Hear , To_4756.svg similarity index 100% rename from img/Hear , To_4756.svg rename to assets/images/default/Hear , To_4756.svg diff --git a/img/Heart_29191.png b/assets/images/default/Heart_29191.png similarity index 100% rename from img/Heart_29191.png rename to assets/images/default/Heart_29191.png diff --git a/img/How__14819.png b/assets/images/default/How__14819.png similarity index 100% rename from img/How__14819.png rename to assets/images/default/How__14819.png diff --git a/img/Hurt_6390.svg b/assets/images/default/Hurt_6390.svg similarity index 100% rename from img/Hurt_6390.svg rename to assets/images/default/Hurt_6390.svg diff --git a/img/I am fine_15187.png b/assets/images/default/I am fine_15187.png similarity index 100% rename from img/I am fine_15187.png rename to assets/images/default/I am fine_15187.png diff --git a/img/I need help_6403.png b/assets/images/default/I need help_6403.png similarity index 100% rename from img/I need help_6403.png rename to assets/images/default/I need help_6403.png diff --git a/img/I_23542.png b/assets/images/default/I_23542.png similarity index 100% rename from img/I_23542.png rename to assets/images/default/I_23542.png diff --git a/img/It_23474.png b/assets/images/default/It_23474.png similarity index 100% rename from img/It_23474.png rename to assets/images/default/It_23474.png diff --git a/img/Left Arrow Curving Right_43513.png b/assets/images/default/Left Arrow Curving Right_43513.png similarity index 100% rename from img/Left Arrow Curving Right_43513.png rename to assets/images/default/Left Arrow Curving Right_43513.png diff --git a/img/Light Bulb_43333.png b/assets/images/default/Light Bulb_43333.png similarity index 100% rename from img/Light Bulb_43333.png rename to assets/images/default/Light Bulb_43333.png diff --git a/img/Palms Up Together_41055.svg b/assets/images/default/Palms Up Together_41055.svg similarity index 100% rename from img/Palms Up Together_41055.svg rename to assets/images/default/Palms Up Together_41055.svg diff --git a/img/Please come here_6404.svg b/assets/images/default/Please come here_6404.svg similarity index 100% rename from img/Please come here_6404.svg rename to assets/images/default/Please come here_6404.svg diff --git a/img/Point_36483.png b/assets/images/default/Point_36483.png similarity index 100% rename from img/Point_36483.png rename to assets/images/default/Point_36483.png diff --git a/img/Question_32387.png b/assets/images/default/Question_32387.png similarity index 100% rename from img/Question_32387.png rename to assets/images/default/Question_32387.png diff --git a/img/R.svg b/assets/images/default/R.svg similarity index 100% rename from img/R.svg rename to assets/images/default/R.svg diff --git a/img/Red Circle_43662.png b/assets/images/default/Red Circle_43662.png similarity index 100% rename from img/Red Circle_43662.png rename to assets/images/default/Red Circle_43662.png diff --git a/img/Run To Finish_30175.png b/assets/images/default/Run To Finish_30175.png similarity index 100% rename from img/Run To Finish_30175.png rename to assets/images/default/Run To Finish_30175.png diff --git a/img/Some_47563.svg b/assets/images/default/Some_47563.svg similarity index 100% rename from img/Some_47563.svg rename to assets/images/default/Some_47563.svg diff --git a/img/Switch off TV_6844.png b/assets/images/default/Switch off TV_6844.png similarity index 100% rename from img/Switch off TV_6844.png rename to assets/images/default/Switch off TV_6844.png diff --git a/img/Switch on TV_6843.png b/assets/images/default/Switch on TV_6843.png similarity index 100% rename from img/Switch on TV_6843.png rename to assets/images/default/Switch on TV_6843.png diff --git a/img/Talk_30920.png b/assets/images/default/Talk_30920.png similarity index 100% rename from img/Talk_30920.png rename to assets/images/default/Talk_30920.png diff --git a/img/Target_38031.png b/assets/images/default/Target_38031.png similarity index 100% rename from img/Target_38031.png rename to assets/images/default/Target_38031.png diff --git a/img/Think Then Do_37833.png b/assets/images/default/Think Then Do_37833.png similarity index 100% rename from img/Think Then Do_37833.png rename to assets/images/default/Think Then Do_37833.png diff --git a/img/Thumbs Up_41001.svg b/assets/images/default/Thumbs Up_41001.svg similarity index 100% rename from img/Thumbs Up_41001.svg rename to assets/images/default/Thumbs Up_41001.svg diff --git a/img/What are you looking at__18027.png b/assets/images/default/What are you looking at__18027.png similarity index 100% rename from img/What are you looking at__18027.png rename to assets/images/default/What are you looking at__18027.png diff --git a/img/White Question Mark_43583.svg b/assets/images/default/White Question Mark_43583.svg similarity index 100% rename from img/White Question Mark_43583.svg rename to assets/images/default/White Question Mark_43583.svg diff --git a/img/Work_32615.png b/assets/images/default/Work_32615.png similarity index 100% rename from img/Work_32615.png rename to assets/images/default/Work_32615.png diff --git a/img/Your Turn!_47716.png b/assets/images/default/Your Turn!_47716.png similarity index 100% rename from img/Your Turn!_47716.png rename to assets/images/default/Your Turn!_47716.png diff --git a/img/a_16046.png b/assets/images/default/a_16046.png similarity index 100% rename from img/a_16046.png rename to assets/images/default/a_16046.png diff --git a/img/again_47245.png b/assets/images/default/again_47245.png similarity index 100% rename from img/again_47245.png rename to assets/images/default/again_47245.png diff --git a/img/and_47645.svg b/assets/images/default/and_47645.svg similarity index 100% rename from img/and_47645.svg rename to assets/images/default/and_47645.svg diff --git a/img/bad_16939.png b/assets/images/default/bad_16939.png similarity index 100% rename from img/bad_16939.png rename to assets/images/default/bad_16939.png diff --git a/img/be_46567.png b/assets/images/default/be_46567.png similarity index 100% rename from img/be_46567.png rename to assets/images/default/be_46567.png diff --git a/img/can I help you__19233.png b/assets/images/default/can I help you__19233.png similarity index 100% rename from img/can I help you__19233.png rename to assets/images/default/can I help you__19233.png diff --git a/img/face_15743.png b/assets/images/default/face_15743.png similarity index 100% rename from img/face_15743.png rename to assets/images/default/face_15743.png diff --git a/img/feelings_15127.png b/assets/images/default/feelings_15127.png similarity index 100% rename from img/feelings_15127.png rename to assets/images/default/feelings_15127.png diff --git a/img/first_23704.png b/assets/images/default/first_23704.png similarity index 100% rename from img/first_23704.png rename to assets/images/default/first_23704.png diff --git a/img/for_47639.png b/assets/images/default/for_47639.png similarity index 100% rename from img/for_47639.png rename to assets/images/default/for_47639.png diff --git a/img/give_21931.png b/assets/images/default/give_21931.png similarity index 100% rename from img/give_21931.png rename to assets/images/default/give_21931.png diff --git a/img/happy_16265.png b/assets/images/default/happy_16265.png similarity index 100% rename from img/happy_16265.png rename to assets/images/default/happy_16265.png diff --git a/img/have_17947.png b/assets/images/default/have_17947.png similarity index 100% rename from img/have_17947.png rename to assets/images/default/have_17947.png diff --git a/img/head_15732.png b/assets/images/default/head_15732.png similarity index 100% rename from img/head_15732.png rename to assets/images/default/head_15732.png diff --git a/img/i will do_47673.png b/assets/images/default/i will do_47673.png similarity index 100% rename from img/i will do_47673.png rename to assets/images/default/i will do_47673.png diff --git a/img/ice_17807.png b/assets/images/default/ice_17807.png similarity index 100% rename from img/ice_17807.png rename to assets/images/default/ice_17807.png diff --git a/img/in_17714.png b/assets/images/default/in_17714.png similarity index 100% rename from img/in_17714.png rename to assets/images/default/in_17714.png diff --git a/img/know_19512.png b/assets/images/default/know_19512.png similarity index 100% rename from img/know_19512.png rename to assets/images/default/know_19512.png diff --git a/img/like_19945.png b/assets/images/default/like_19945.png similarity index 100% rename from img/like_19945.png rename to assets/images/default/like_19945.png diff --git a/img/listen to music_22609.png b/assets/images/default/listen to music_22609.png similarity index 100% rename from img/listen to music_22609.png rename to assets/images/default/listen to music_22609.png diff --git a/img/no_16959.png b/assets/images/default/no_16959.png similarity index 100% rename from img/no_16959.png rename to assets/images/default/no_16959.png diff --git a/img/now_44934.png b/assets/images/default/now_44934.png similarity index 100% rename from img/now_44934.png rename to assets/images/default/now_44934.png diff --git a/img/ok_23430.png b/assets/images/default/ok_23430.png similarity index 100% rename from img/ok_23430.png rename to assets/images/default/ok_23430.png diff --git a/img/open_14053.png b/assets/images/default/open_14053.png similarity index 100% rename from img/open_14053.png rename to assets/images/default/open_14053.png diff --git a/img/plastic_bag.svg b/assets/images/default/plastic_bag.svg similarity index 100% rename from img/plastic_bag.svg rename to assets/images/default/plastic_bag.svg diff --git a/img/play_17437.png b/assets/images/default/play_17437.png similarity index 100% rename from img/play_17437.png rename to assets/images/default/play_17437.png diff --git a/img/please_18234.png b/assets/images/default/please_18234.png similarity index 100% rename from img/please_18234.png rename to assets/images/default/please_18234.png diff --git a/img/problem_23791.png b/assets/images/default/problem_23791.png similarity index 100% rename from img/problem_23791.png rename to assets/images/default/problem_23791.png diff --git a/img/put_24019.png b/assets/images/default/put_24019.png similarity index 100% rename from img/put_24019.png rename to assets/images/default/put_24019.png diff --git a/img/read_17820.png b/assets/images/default/read_17820.png similarity index 100% rename from img/read_17820.png rename to assets/images/default/read_17820.png diff --git a/img/sad_15665.png b/assets/images/default/sad_15665.png similarity index 100% rename from img/sad_15665.png rename to assets/images/default/sad_15665.png diff --git a/img/self esteem_19219.png b/assets/images/default/self esteem_19219.png similarity index 100% rename from img/self esteem_19219.png rename to assets/images/default/self esteem_19219.png diff --git a/img/show_14528.png b/assets/images/default/show_14528.png similarity index 100% rename from img/show_14528.png rename to assets/images/default/show_14528.png diff --git a/img/stop_17875.png b/assets/images/default/stop_17875.png similarity index 100% rename from img/stop_17875.png rename to assets/images/default/stop_17875.png diff --git a/img/stop_18310.png b/assets/images/default/stop_18310.png similarity index 100% rename from img/stop_18310.png rename to assets/images/default/stop_18310.png diff --git a/img/talk at once_46488.png b/assets/images/default/talk at once_46488.png similarity index 100% rename from img/talk at once_46488.png rename to assets/images/default/talk at once_46488.png diff --git a/img/the_47642.svg b/assets/images/default/the_47642.svg similarity index 100% rename from img/the_47642.svg rename to assets/images/default/the_47642.svg diff --git a/img/tired_15386.png b/assets/images/default/tired_15386.png similarity index 100% rename from img/tired_15386.png rename to assets/images/default/tired_15386.png diff --git a/img/to_17721.png b/assets/images/default/to_17721.png similarity index 100% rename from img/to_17721.png rename to assets/images/default/to_17721.png diff --git a/img/transparent.svg b/assets/images/default/transparent.svg similarity index 100% rename from img/transparent.svg rename to assets/images/default/transparent.svg diff --git a/img/walk_17197.png b/assets/images/default/walk_17197.png similarity index 100% rename from img/walk_17197.png rename to assets/images/default/walk_17197.png diff --git a/img/want_46605.png b/assets/images/default/want_46605.png similarity index 100% rename from img/want_46605.png rename to assets/images/default/want_46605.png diff --git a/img/what is that__14281.png b/assets/images/default/what is that__14281.png similarity index 100% rename from img/what is that__14281.png rename to assets/images/default/what is that__14281.png diff --git a/img/who_14192.png b/assets/images/default/who_14192.png similarity index 100% rename from img/who_14192.png rename to assets/images/default/who_14192.png diff --git a/img/why_46802.png b/assets/images/default/why_46802.png similarity index 100% rename from img/why_46802.png rename to assets/images/default/why_46802.png diff --git a/img/yes_17011.png b/assets/images/default/yes_17011.png similarity index 100% rename from img/yes_17011.png rename to assets/images/default/yes_17011.png diff --git a/css/normalize.css b/css/normalize.css deleted file mode 100644 index 911c8ee..0000000 --- a/css/normalize.css +++ /dev/null @@ -1,355 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - - -p { - margin: 0; -} - - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} diff --git a/css/reset.css b/css/reset.css deleted file mode 100644 index 5bc7dcd..0000000 --- a/css/reset.css +++ /dev/null @@ -1,47 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 67c1415..0000000 --- a/css/style.css +++ /dev/null @@ -1,210 +0,0 @@ -/****************************************** -/* CSS -/*******************************************/ - -/* Box Model Hack */ -*{ - box-sizing: border-box; -} - -/****************************************** -/* LAYOUT -/*******************************************/ - -body { - height: 100vh; - width: 100%; - background:#093c43; - margin: 0 auto; - font-family: 'Roboto', sans-serif; - -} - -h1{ - color:white; - font-style: italic; - letter-spacing: 1px; - font-size: 3rem; -} - -html{ - font-size: 62.5%; - color: black; -} -/****************************************** -/* LAYOUT -/*******************************************/ - - -h1, h2, h3, p{ - text-align: center; -} -p { - font-size: 2rem; -} - -#mainContainer{ - display: flex; - flex-wrap: wrap; - justify-content: center; -} -.boxTemplate { - text-align: center; - border: .1rem solid black; - flex-basis: 10%; - height: 16rem; - font-size: 1.6rem; - border-radius: 5px; - margin: .05rem; - display: grid; - place-items: center; - grid-template-rows: 1fr 1fr; -} - - -.yellow { - background-color: #edff00; -} - - -.purple{ - background-color: #e60af5; -} - -.skyBlue{ - background-color: #0ef1d6; -} - - -.lightPink{ - background-color: #fcaab4; - -} - -.lightPurple{ - background-color: #f985bb; -} - -.darkPink{ - background-color: #ff4167; -} - -.lightGreen{ - background-color: #59ff5b; -} - -.darkGreen{ - background-color: #00c503; -} - -.red{ - background-color: #ff1801; -} - -.white{ - background-color: white; -} - -/*------------------images----------------------*/ -img{ - height: 5rem; - width: auto; -} - - -/*-------typing input container and style----*/ - - -/****************************************** -/* ADDITIONAL STYLES -/*******************************************/ - -/*--------keyboard input area styling----------*/ -.customInputWrapper, #iSaid { - display: flex; - color: black; - flex-direction: column; - text-align: center; -} -.customInputWrapper { - margin-top: 3rem; -} -#customInputLabel { - color: white; - font-size: 4rem; -} -#keyboard { - margin: 1rem auto; - height: 4rem; - width: 35%; - text-align: center; - font-size: 1.6rem; -} -#keyboard::placeholder { - text-align: center; - font-size: 1.6rem; - color: #555; -} -#submitText, #addBtn{ - color: white; - background: black; - padding: 1rem; - width: 10%; - height: 3rem; - margin: 0 auto 2rem auto; - border: none; -} -#submitText:hover { - cursor: pointer; -} - - - -/****************************************** -/* ADDITIONAL STYLES -/********************************************/ - - -@media screen and (max-width: 800px){ - .boxTemplate{ - height: 10rem; - } - img { - height: 3.5rem; - width: auto; - } - p { - font-size: 2rem; - } - #customInputLabel{ - font-size: 3rem; - } - #keyboard { - width: 45%; - } - } - @media screen and (max-width: 600px) { - .boxTemplate { - flex-basis: 15%; - } - #keyboard { - width: 60%; - } - #submitText { - width: 20%; - } - } - @media screen and (max-width: 400px) { - body { - width: 100%; - } - #mainContainer{ - justify-content: space-around; - } - .boxTemplate { - flex-basis: 23%; - } - #keyboard { - width: 80%; - } -} diff --git a/index.html b/index.html index d8975f0..b9bb266 100644 --- a/index.html +++ b/index.html @@ -1,49 +1,12 @@ -
- - - - - - - - - - -