├── public ├── temp │ ├── .gitkeep │ └── about-this-folder.txt ├── users │ ├── 5 │ │ └── user.jpg │ ├── 6 │ │ ├── smoke.jpg │ │ └── user.jpg │ ├── 7 │ │ └── user.jpg │ ├── 8 │ │ └── user.jpg │ └── about_this_folder.txt ├── images │ ├── large.jpg │ ├── images (11).png │ ├── spacecraft.jpg │ ├── favicon │ │ └── favicon.png │ └── tumblr_nk2y2oVEjp1rn9vmdo1_500.gif ├── js │ └── src │ │ ├── reducers │ │ ├── user-reducer.js │ │ ├── explore-reducer.js │ │ ├── note-int-reducer.js │ │ ├── notes-reducer.js │ │ └── follow-reducer.js │ │ ├── actions │ │ ├── explore-action.js │ │ ├── user-action.js │ │ ├── note-int-action.js │ │ ├── notes-action.js │ │ └── follow-action.js │ │ ├── components │ │ ├── others │ │ │ ├── title-comp.js │ │ │ ├── end-comp.js │ │ │ ├── overlay-comp.js │ │ │ ├── nothing-comp.js │ │ │ ├── header-comp.js │ │ │ ├── goto-comp.js │ │ │ └── prompt-comp.js │ │ ├── profile │ │ │ ├── filter-notes-comp.js │ │ │ ├── notes-comp.js │ │ │ ├── follow │ │ │ │ ├── followings-comp.js │ │ │ │ ├── followers-comp.js │ │ │ │ ├── following-items.js │ │ │ │ └── follower-items.js │ │ │ ├── profile-comp.js │ │ │ └── banner-comp.js │ │ ├── home │ │ │ ├── feeds-comp.js │ │ │ └── home-comp.js │ │ ├── email-verification │ │ │ └── email-ver-comp.js │ │ ├── note │ │ │ ├── note-comp.js │ │ │ ├── likes-comp.js │ │ │ ├── create-note-comp.js │ │ │ ├── like-items.js │ │ │ └── view-note-comp.js │ │ ├── explore │ │ │ ├── explore-comp.js │ │ │ └── explores-list.js │ │ ├── error │ │ │ └── error-comp.js │ │ ├── app-comp.js │ │ ├── deactivate │ │ │ └── deactivate-comp.js │ │ └── edit │ │ │ └── edit-comp.js │ │ ├── main.js │ │ ├── store │ │ └── store.js │ │ ├── user-system │ │ └── user-system.js │ │ └── functions │ │ └── functions.js └── styles │ ├── dist │ ├── perfect-scrollbar.css │ └── styles.css │ └── src │ ├── styles.less │ └── defaults.less ├── .gitignore ├── .gitattributes ├── views ├── partials │ ├── footer.hbs │ ├── needs.hbs │ ├── nHeader.hbs │ ├── yHeader.hbs │ └── tophead.hbs ├── registered.hbs ├── welcome.hbs ├── app.hbs ├── 404.hbs ├── login.hbs └── signup.hbs ├── tsconfig.json ├── screenshots ├── Snap 2017-07-27 at 00.27.11.png ├── Snap 2017-07-27 at 00.27.24.png ├── Snap 2017-07-27 at 00.27.34.png ├── Snap 2017-07-27 at 00.27.45.png ├── Snap 2017-07-27 at 00.28.54.png ├── Snap 2017-07-27 at 00.29.13.png ├── Snap 2017-07-27 at 00.29.35.png ├── Snap 2017-07-27 at 00.30.13.png ├── Snap 2017-07-27 at 00.31.06.png └── Snap 2017-09-17 at 13.30.18.png ├── less-watch-compiler.config.json ├── .editorconfig ├── .babelrc ├── models ├── mysql.js ├── mail.js ├── middlewares.js ├── db.js └── userFn.js ├── routes ├── main-routes.js ├── user-routes.js ├── api-routes.js ├── note-int-routes.js ├── note_routes.js ├── follow-routes.js └── edit-routes.js ├── webpack.config.js ├── LICENSE ├── app.js ├── package.json ├── README.md └── db.sql /public/temp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | .vscode 4 | -------------------------------------------------------------------------------- /public/users/about_this_folder.txt: -------------------------------------------------------------------------------- 1 | THIS FOLDER CONTAINS FOLDER OF USERS -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # * linguist-vendored 2 | # *.js linguist-vendored=false 3 | -------------------------------------------------------------------------------- /views/partials/footer.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/images/large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/images/large.jpg -------------------------------------------------------------------------------- /public/users/5/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/users/5/user.jpg -------------------------------------------------------------------------------- /public/users/6/smoke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/users/6/smoke.jpg -------------------------------------------------------------------------------- /public/users/6/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/users/6/user.jpg -------------------------------------------------------------------------------- /public/users/7/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/users/7/user.jpg -------------------------------------------------------------------------------- /public/users/8/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/users/8/user.jpg -------------------------------------------------------------------------------- /public/images/images (11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/images/images (11).png -------------------------------------------------------------------------------- /public/images/spacecraft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/images/spacecraft.jpg -------------------------------------------------------------------------------- /public/temp/about-this-folder.txt: -------------------------------------------------------------------------------- 1 | THIS FOLDER CONTAINS ALL THE TEMPORARY IMAGES WHICH WILL BE DELETED WHEN AVATAR CHANGED!! 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowJs": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/images/favicon/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/images/favicon/favicon.png -------------------------------------------------------------------------------- /views/registered.hbs: -------------------------------------------------------------------------------- 1 | {{> yHeader }} 2 | 3 |
4 | {{ options.mssg }} 5 |
6 | 7 | {{> footer }} 8 | -------------------------------------------------------------------------------- /views/partials/needs.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.27.11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.27.11.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.27.24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.27.24.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.27.34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.27.34.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.27.45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.27.45.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.28.54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.28.54.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.29.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.29.13.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.29.35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.29.35.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.30.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.30.13.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-07-27 at 00.31.06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-07-27 at 00.31.06.png -------------------------------------------------------------------------------- /screenshots/Snap 2017-09-17 at 13.30.18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/screenshots/Snap 2017-09-17 at 13.30.18.png -------------------------------------------------------------------------------- /public/images/tumblr_nk2y2oVEjp1rn9vmdo1_500.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yTakkar/React-Mini-Social-Network/HEAD/public/images/tumblr_nk2y2oVEjp1rn9vmdo1_500.gif -------------------------------------------------------------------------------- /views/welcome.hbs: -------------------------------------------------------------------------------- 1 | {{> nHeader }} 2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 | {{> footer }} -------------------------------------------------------------------------------- /less-watch-compiler.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowedExtensions":[".less"], 3 | "minified": false, 4 | "sourceMap": false, 5 | "watchFolder": "public/styles/src", 6 | "outputFolder": "public/styles/dist" 7 | } -------------------------------------------------------------------------------- /views/partials/nHeader.hbs: -------------------------------------------------------------------------------- 1 | {{> tophead }} 2 | 3 |
4 |
5 | Home 6 | Login 7 | Signup 8 |
9 |
10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http.editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /views/partials/yHeader.hbs: -------------------------------------------------------------------------------- 1 | {{> tophead }} 2 | 3 |
4 |
5 | Home 6 |
7 |
8 | Profile 9 | Logout 10 |
11 |
12 | -------------------------------------------------------------------------------- /views/app.hbs: -------------------------------------------------------------------------------- 1 | {{> tophead }} 2 | 3 |
11 | 12 |
13 | 14 | {{> footer }} 15 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "react-html-attrs", 9 | "transform-class-properties", 10 | "transform-decorators-legacy", 11 | "transform-react-jsx-source", 12 | ["transform-runtime", {"polyfill": false, "regenerator": true }] 13 | ] 14 | } -------------------------------------------------------------------------------- /public/js/src/reducers/user-reducer.js: -------------------------------------------------------------------------------- 1 | const def_user = { 2 | user_details: {} 3 | } 4 | 5 | const user = (state=def_user, action) => { 6 | switch (action.type) { 7 | case "USER_DETAILS": 8 | return { ...state, user_details: action.payload } 9 | break 10 | } 11 | return state 12 | } 13 | 14 | export default user 15 | -------------------------------------------------------------------------------- /public/js/src/actions/explore-action.js: -------------------------------------------------------------------------------- 1 | import { post } from 'axios' 2 | 3 | const get_explores = () => { 4 | return dispatch => { 5 | post('/api/explore') 6 | .then(exp => dispatch({ type: "GET_EXPLORES", payload: exp.data }) ) 7 | .catch(err => console.log(err) ) 8 | } 9 | } 10 | 11 | module.exports = { 12 | get_explores 13 | } 14 | -------------------------------------------------------------------------------- /public/js/src/reducers/explore-reducer.js: -------------------------------------------------------------------------------- 1 | const explore_defaults = { 2 | explores: [] 3 | } 4 | 5 | const explore = (state=explore_defaults, action) => { 6 | switch (action.type) { 7 | case "GET_EXPLORES": 8 | return { ...state, explores: action.payload } 9 | break 10 | } 11 | return state 12 | } 13 | 14 | export default explore 15 | -------------------------------------------------------------------------------- /public/js/src/actions/user-action.js: -------------------------------------------------------------------------------- 1 | import { post } from 'axios' 2 | 3 | const user_details = get => { 4 | return dispatch => { 5 | post('/api/get-details', { get }) 6 | .then(get => dispatch({type: "USER_DETAILS", payload: get.data }) ) 7 | .catch(err => console.log(err) ) 8 | } 9 | } 10 | 11 | module.exports = { 12 | user_details 13 | } 14 | -------------------------------------------------------------------------------- /public/js/src/components/others/title-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import PropTypes from 'prop-types' 4 | 5 | export default class Title extends React.Component { 6 | render(){ 7 | return ( 8 | 9 | { this.props.value } • Mini Social Network 10 | 11 | ) 12 | } 13 | } 14 | 15 | Title.propTypes = { 16 | value: PropTypes.string 17 | } 18 | -------------------------------------------------------------------------------- /models/mysql.js: -------------------------------------------------------------------------------- 1 | const 2 | mysql = require('mysql'), 3 | hl = require('handy-log'), 4 | { MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE } = process.env 5 | 6 | const db = mysql.createConnection({ 7 | host: MYSQL_HOST, 8 | user: MYSQL_USER, 9 | password: MYSQL_PASSWORD, 10 | database: MYSQL_DATABASE, 11 | charset: "utf8mb4" 12 | }) 13 | 14 | db.connect(err => { 15 | if(err){ 16 | hl.error(err) 17 | } 18 | }) 19 | 20 | module.exports = db 21 | -------------------------------------------------------------------------------- /public/js/src/main.js: -------------------------------------------------------------------------------- 1 | // USER SYSTEM 2 | import './user-system/user-system' 3 | 4 | // FOR LOGGEDIN USER 5 | import React from 'react' 6 | import ReactDOM from 'react-dom' 7 | import store from './store/store' 8 | import { Provider } from 'react-redux' 9 | import App from './components/app-comp' 10 | 11 | let element = document.getElementById('root') 12 | if (element) { 13 | ReactDOM.render( 14 | 15 | 16 | , 17 | element 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /public/js/src/components/others/end-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | 4 | export default class End extends React.Component{ 5 | 6 | toTop = () => { 7 | $('html, body').animate({ scrollTop: 0 }, 450) 8 | } 9 | 10 | render(){ 11 | return( 12 |
13 | {this.props.mssg} 14 |
15 | ) 16 | } 17 | } 18 | 19 | End.defaultProps = { 20 | mssg: "Looks like you've reached the end" 21 | } 22 | -------------------------------------------------------------------------------- /views/404.hbs: -------------------------------------------------------------------------------- 1 | {{> nHeader}} 2 | 3 |
4 |
5 |
6 | Oops, the page you're looking for does not exist!! 7 |
8 | 9 |
10 | Go to home 11 | 12 |
13 |
14 |
15 | 16 | {{> footer}} -------------------------------------------------------------------------------- /models/mail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require('nodemailer') 2 | 3 | let transporter = nodemailer.createTransport({ 4 | service: "gmail", 5 | auth: { 6 | user: process.env.MAIL, 7 | pass: process.env.MAIL_PASSWORD 8 | } 9 | }) 10 | 11 | let mail = options => { 12 | return new Promise((resolve, reject) => { 13 | let o = Object.assign({}, { 14 | from: `"Notes App" <${process.env.MAIL}>` 15 | }, options) 16 | transporter.sendMail(o, (err, res) => err ? reject(err) : resolve('Mail sent!!')) 17 | }) 18 | } 19 | 20 | module.exports = mail 21 | -------------------------------------------------------------------------------- /routes/main-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | mw = require('../models/middlewares') 4 | 5 | app.get('/welcome', mw.NotLoggedIn, (req, res) => { 6 | let options = { title: "Welcome to Notes App!" } 7 | res.render('welcome', { options }) 8 | }) 9 | 10 | app.get('/404', (req, res) => { 11 | let options = { title: "Oops!! Error • Notes App" } 12 | req.session.id ? res.redirect('/error') : res.render('404', { options }) 13 | }) 14 | 15 | app.get('/*', mw.LoggedIn, (req, res) => { 16 | res.render('app') 17 | }) 18 | 19 | module.exports = app 20 | -------------------------------------------------------------------------------- /public/js/src/components/others/overlay-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default class Overlay extends React.Component{ 5 | render(){ 6 | let 7 | { type } = this.props, 8 | cls 9 | 10 | if(type == 'black'){ 11 | cls = 'overlay' 12 | } else if (type == 'white'){ 13 | cls = 'hidden_overlay' 14 | } else if (type == 'colored'){ 15 | cls = 'colored_overlay' 16 | } 17 | 18 | return ( 19 |
20 | ) 21 | } 22 | } 23 | 24 | Overlay.defaultProps = { 25 | type: 'black' 26 | } 27 | 28 | Overlay.propTypes = { 29 | type: PropTypes.string 30 | } 31 | -------------------------------------------------------------------------------- /public/js/src/components/others/nothing-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default class Nothing extends React.Component{ 5 | 6 | render(){ 7 | let { mssg, showMssg } = this.props 8 | return( 9 |
10 | 11 | { showMssg ? {mssg} : null } 12 |
13 | ) 14 | } 15 | } 16 | 17 | Nothing.defaultProps = { 18 | mssg: "Hello, a message for you!", 19 | showMssg: true 20 | } 21 | 22 | Nothing.propTypes = { 23 | mssg: PropTypes.string, 24 | showMssg: PropTypes.bool 25 | } 26 | -------------------------------------------------------------------------------- /public/js/src/components/profile/filter-notes-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default class Filter_notes extends React.Component { 4 | 5 | render(){ 6 | let { notes_length, filter } = this.props 7 | 8 | return ( 9 |
10 | { 11 | notes_length != 0 ? 12 | 0} 16 | autoComplete={false} 17 | spellCheck={false} 18 | onChange={filter} 19 | /> 20 | : 21 | null 22 | } 23 |
24 | ) 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | entry: "./public/js/src/main.js", 5 | output: { 6 | path: path.join(__dirname,"/public/js/dist/"), 7 | filename: "bundle.js" 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.js$/, 13 | exclude: /node_modules/, 14 | loader: "babel-loader", 15 | query: { 16 | "presets": ["env", "react", "stage-0"], 17 | "plugins": ["react-html-attrs", "transform-class-properties", "transform-decorators-legacy", "transform-react-jsx-source"] 18 | } 19 | } 20 | ] 21 | }, 22 | watch: true 23 | } 24 | -------------------------------------------------------------------------------- /views/partials/tophead.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ options.title }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{> needs }} 17 | -------------------------------------------------------------------------------- /views/login.hbs: -------------------------------------------------------------------------------- 1 | {{> nHeader }} 2 | 3 |
4 |
5 | Need an account? 6 |
7 | 8 |
9 |
10 | Get started again 11 |
12 | 17 |
18 |
19 | 20 | {{> footer }} -------------------------------------------------------------------------------- /public/js/src/actions/note-int-action.js: -------------------------------------------------------------------------------- 1 | import { post } from 'axios' 2 | 3 | const note_details = note => { 4 | return dispatch => { 5 | post('/api/get-note-details', { note }) 6 | .then(s => dispatch({ type: "NOTE_DETAILS", payload: s.data }) ) 7 | .catch(e => console.log(err) ) 8 | } 9 | } 10 | 11 | const likes = note => { 12 | return dispatch => { 13 | post('/api/likes', { note }) 14 | .then(likes => dispatch({ type: "LIKES", payload: likes.data }) ) 15 | .catch(err => console.log(err) ) 16 | } 17 | } 18 | 19 | const liked = obj => { 20 | return { 21 | type: "LIKED", 22 | payload: obj 23 | } 24 | } 25 | 26 | const unliked = note => { 27 | return { 28 | type: "UNLIKED", 29 | payload: note 30 | } 31 | } 32 | 33 | module.exports = { 34 | note_details, 35 | likes, 36 | liked, 37 | unliked 38 | } 39 | -------------------------------------------------------------------------------- /public/js/src/store/store.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, combineReducers, createStore } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import logger from 'redux-logger' 4 | import promise from 'redux-promise-middleware' 5 | 6 | import user from '../reducers/user-reducer' 7 | import notes from '../reducers/notes-reducer' 8 | import follow from '../reducers/follow-reducer' 9 | import explore from '../reducers/explore-reducer' 10 | import note_int from '../reducers/note-int-reducer' 11 | 12 | const reducers = combineReducers({ 13 | user, 14 | notes, 15 | follow, 16 | explore, 17 | note_int 18 | }) 19 | 20 | const middlewares = applyMiddleware(promise(), thunk, logger) 21 | 22 | const store = createStore( 23 | reducers, 24 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), 25 | middlewares 26 | ) 27 | 28 | export default store 29 | -------------------------------------------------------------------------------- /public/js/src/components/home/feeds-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import { connect } from 'react-redux' 4 | 5 | import Nothing from '../others/nothing-comp' 6 | import End from '../others/end-comp' 7 | import Note from '../note/note-comp' 8 | import * as fn from '../../functions/functions' 9 | 10 | @connect(store => { 11 | return { 12 | notes: store.notes 13 | } 14 | }) 15 | 16 | export default class Feeds extends React.Component{ 17 | render(){ 18 | let 19 | { notes: { feeds } } = this.props, 20 | map_feeds = feeds.map(feed => 21 | 22 | ) 23 | 24 | return( 25 |
26 | { feeds.length == 0 ? : map_feeds } 27 | { feeds.length != 0 ? : null } 28 |
29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/js/src/components/email-verification/email-ver-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Title from '../others/title-comp' 4 | import { FadeIn } from 'animate-components' 5 | 6 | export default class EmailVerification extends React.Component{ 7 | render(){ 8 | let 9 | { params: { is } } = this.props.match, 10 | mssg 11 | 12 | if(is == "yes"){ 13 | mssg = "You email has been verified successfully!" 14 | } else if(is == "alr"){ 15 | mssg = "Email already verified!" 16 | } else { 17 | mssg = "Something went wrong!" 18 | } 19 | 20 | return( 21 |
22 | 23 | <FadeIn duration="300ms" > 24 | <div class="registered"> 25 | <span>{mssg}</span> 26 | </div> 27 | </FadeIn> 28 | </div> 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/js/src/reducers/note-int-reducer.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | 3 | const note_int_def = { 4 | note_details: {}, 5 | likes: [] 6 | } 7 | 8 | const note_int = (state=note_int_def, action) => { 9 | let { payload: py } = action 10 | 11 | switch (action.type) { 12 | case "NOTE_DETAILS": 13 | return { ...state, note_details: py } 14 | break 15 | 16 | case "LIKES": 17 | return { ...state, likes: py } 18 | break 19 | 20 | case "LIKED": 21 | return { ...state, likes: liked(state.likes, py) } 22 | break 23 | 24 | case "UNLIKED": 25 | return { ...state, likes: unliked(state.likes, py) } 26 | break 27 | } 28 | return state 29 | } 30 | 31 | const liked = (likes, like) => { 32 | likes.unshift(like) 33 | return likes 34 | } 35 | 36 | const unliked = (likes, note) => { 37 | let user = $('#data').data('session') 38 | return likes.filter(l => l.like_by != user && l.note_id == note ) 39 | } 40 | 41 | export default note_int 42 | -------------------------------------------------------------------------------- /public/js/src/components/profile/notes-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | 4 | import Nothing from '../others/nothing-comp' 5 | import End from '../others/end-comp' 6 | import Note from '../note/note-comp' 7 | import * as fn from '../../functions/functions' 8 | 9 | @connect(store => { 10 | return { 11 | user: store.user, 12 | note: store.notes 13 | } 14 | }) 15 | 16 | export default class Notes extends React.Component{ 17 | render(){ 18 | let 19 | { notes, 20 | user: { user_details: { username, id } } 21 | } = this.props, 22 | map_notes = notes.map(note => 23 | <Note key={note.note_id} {...note} /> 24 | ) 25 | 26 | return( 27 | <div class='notes' > 28 | { 29 | notes.length == 0 ? 30 | <Nothing mssg={ fn.Me(id) ? "You got no notes" : `${username} got no notes!`} /> 31 | : 32 | map_notes 33 | } 34 | { notes.length != 0 ? <End/> : null } 35 | </div> 36 | ) 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/js/src/actions/notes-action.js: -------------------------------------------------------------------------------- 1 | import { post } from 'axios' 2 | 3 | const getNotes = get => { 4 | return dispatch => { 5 | post('/api/get-notes', { get }) 6 | .then(notes => dispatch({ type: "GET_NOTES", payload: notes.data }) ) 7 | .catch(err => dispatch({ type: "GET_NOTES_ERR", payload: err }) ) 8 | } 9 | } 10 | 11 | const updateNote = note => { 12 | return { 13 | type: "UPDATE_NOTES", 14 | payload: note 15 | } 16 | } 17 | 18 | const deleteNote = note => { 19 | return { 20 | type: "DELETE_NOTE", 21 | payload: note 22 | } 23 | } 24 | 25 | const editNote = note_details => { 26 | return { 27 | type: "EDIT_NOTE", 28 | payload: note_details 29 | } 30 | } 31 | 32 | const getFeeds = () => { 33 | return dispatch => { 34 | post('/api/feeds') 35 | .then(notes => dispatch({ type: "GET_FEEDS", payload: notes.data }) ) 36 | .catch(err => console.log(err) ) 37 | } 38 | } 39 | 40 | module.exports = { 41 | getNotes, 42 | updateNote, 43 | deleteNote, 44 | editNote, 45 | getFeeds 46 | } 47 | -------------------------------------------------------------------------------- /public/js/src/components/others/header-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import { NavLink } from 'react-router-dom' 4 | 5 | export default class Header extends React.Component{ 6 | render(){ 7 | let 8 | username = $('.data').data('username'), 9 | id = $('.data').data('session') 10 | 11 | return ( 12 | <div class='header_loggedin' > 13 | <div class="left"> 14 | <NavLink activeClassName="ha_active" exact={true} to="/" >Home</NavLink> 15 | <NavLink activeClassName="ha_active" to="/explore" >Explore</NavLink> 16 | <NavLink activeClassName="ha_active" to="/deactivate" >Deactivate</NavLink> 17 | </div> 18 | <div className="right"> 19 | <NavLink activeClassName="ha_active" to={`/profile/${username}`} className='vp' >{username}</NavLink> 20 | <NavLink activeClassName="ha_active" to="/edit" >Edit profile</NavLink> 21 | <a href="/logout" >Logout</a> 22 | </div> 23 | </div> 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /routes/user-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | mw = require('../models/middlewares'), 4 | login = require('../models//userFn'), 5 | P = require('bluebird') 6 | 7 | app.get('/login', mw.NotLoggedIn, (req, res) => { 8 | let options = { title: "Login to note" } 9 | res.render('login', { options }) 10 | }) 11 | 12 | app.get('/signup', mw.NotLoggedIn, (req, res) => { 13 | let options = { title: "Signup to note" } 14 | res.render('signup', { options }) 15 | }) 16 | 17 | app.get('/registered', mw.LoggedIn, (req, res) => { 18 | login.registered(req, res) 19 | }) 20 | 21 | app.get('/deep/most/topmost/activate/:id', mw.LoggedIn, (req, res) => { 22 | login.activate(req, res) 23 | }) 24 | 25 | app.post('/user/signup', (req, res) => { 26 | login.signup(req, res) 27 | }) 28 | 29 | app.post('/user/login', (req, res) => { 30 | login.login(req, res) 31 | }) 32 | 33 | app.get('/logout', mw.LoggedIn, (req, res) => { 34 | let url = req.session.reset() ? "/login" : "/" 35 | res.redirect(url) 36 | }) 37 | 38 | module.exports = app 39 | -------------------------------------------------------------------------------- /views/signup.hbs: -------------------------------------------------------------------------------- 1 | {{> nHeader}} 2 | 3 | <div class="notes_wrapper"> 4 | <div class="log_sign"> 5 | <a href="/login" class="pri_btn">Already have an account?</a> 6 | </div> 7 | 8 | <div class="register cua " > 9 | <div class="display_text"> 10 | <span>Get started now and let the fun begins</span> 11 | </div> 12 | <form class="form_register" > 13 | <input type="text" name="username" value="" class="r_username" autofocus spellcheck="false" autocomplete='false' placeholder='Username' required > 14 | <input type="email" name="email" value="" class="r_email" spellcheck="false" autocomplete='false' placeholder='Email' required> 15 | <input type="password" name="password" value="" class="r_password" placeholder='Password' required> 16 | <input type="password" name="password_again" value="" class="r_password_again" placeholder='Password again' required> 17 | <input type="submit" name="" value="Sign up for free" class="r_submit"> 18 | </form> 19 | </div> 20 | </div> 21 | 22 | {{> footer}} -------------------------------------------------------------------------------- /public/js/src/components/others/goto-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import * as fn from '../../functions/functions' 4 | import { Link } from 'react-router-dom' 5 | 6 | export default class Goto extends React.Component{ 7 | 8 | toggle = e => { 9 | let op = e.currentTarget.parentNode.nextSibling 10 | fn.toggle(op) 11 | } 12 | 13 | render(){ 14 | let username = $('.data').data('username') 15 | return( 16 | <div class='goto' > 17 | <div className="goto_link"> 18 | <span className="goto_label">Go to</span> 19 | <span class="show_more" onClick={this.toggle} > 20 | <i class="material-icons">expand_more</i> 21 | </span> 22 | </div> 23 | <div className="options goto_options" style={{ display: "none" }} > 24 | <ul className="o_ul"> 25 | <li className="o_li" ><Link to="/" className="o_a">Home</Link></li> 26 | <li className="o_li" ><Link to={`/profile/${username}`} className="o_a">Profile</Link></li> 27 | </ul> 28 | </div> 29 | </div> 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Faiyaz Shaikh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/js/src/reducers/notes-reducer.js: -------------------------------------------------------------------------------- 1 | const def_notes = { 2 | notes: [], 3 | feeds: [] 4 | } 5 | 6 | const notes = (state=def_notes, action) => { 7 | let { payload: py } = action 8 | 9 | switch (action.type) { 10 | case "GET_NOTES": 11 | return { ...state, notes: py } 12 | break 13 | 14 | case "GET_FEEDS": 15 | return { ...state, feeds: py } 16 | break 17 | 18 | case "UPDATE_NOTES": 19 | return { ...state, notes: update(state.notes, py) } 20 | break 21 | 22 | case "DELETE_NOTES": 23 | return { ...state, notes: dlt(state.notes, py) } 24 | break 25 | 26 | case "EDIT_NOTE": 27 | return { ...state, notes: edit(state.notes, py) } 28 | break 29 | 30 | } 31 | return state 32 | } 33 | 34 | const update = (notes, note) => { 35 | notes.unshift(note) 36 | return notes 37 | } 38 | 39 | const dlt = (notes, note) => notes.filter(n => n.note_id != parseInt(note) ) 40 | 41 | const edit = (notes, note) => { 42 | return notes.map(n => { 43 | if(n.note_id == note.note_id){ 44 | n.title = note.title 45 | n.content = note.content 46 | } 47 | return n 48 | }) 49 | } 50 | 51 | export default notes 52 | -------------------------------------------------------------------------------- /models/middlewares.js: -------------------------------------------------------------------------------- 1 | const db = require('./db') 2 | const P = require('bluebird') 3 | 4 | const LoggedIn = (req, res, next) => { 5 | !req.session.id ? res.redirect('/login') : next() 6 | } 7 | 8 | const NotLoggedIn = (req, res, next) => { 9 | req.session.id ? res.redirect('/') : next() 10 | } 11 | 12 | const MainRedirect = (req, res, next) => { 13 | req.session.id ? next() : res.redirect('/welcome') 14 | } 15 | 16 | const variables = (req, res, next) => { 17 | let loggedIn = (req.session.id) ? true : false 18 | res.locals.loggedIn = loggedIn 19 | res.locals.session = req.session 20 | next() 21 | } 22 | 23 | const not_found = (req, res, next) => { 24 | let options = { 25 | title: "Oops!" 26 | } 27 | res.status(404).render('error', { options }) 28 | } 29 | 30 | const MeOrNot = (req, res, next) => { 31 | db.query('SELECT COUNT(id) as e FROM users WHERE id=?', [req.params.id]) 32 | .then(is => is[0].e == 0 ? res.redirect('/error') : next()) 33 | .catch(err => console.log(err)) 34 | } 35 | 36 | module.exports = { 37 | LoggedIn, 38 | NotLoggedIn, 39 | MainRedirect, 40 | variables, 41 | not_found, 42 | MeOrNot, 43 | } 44 | -------------------------------------------------------------------------------- /public/js/src/components/note/note-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import * as fn from '../../functions/functions' 4 | import TimeAgo from 'handy-timeago' 5 | import { Link } from 'react-router-dom' 6 | 7 | export default class Note extends React.Component{ 8 | render(){ 9 | let { title, content, note_id, user, username, note_time } = this.props 10 | 11 | return( 12 | <Link to={{ pathname:`/view-note/${note_id}`, state: { modal: true } }}> 13 | <div class='note' data-note={note_id} > 14 | <div className="note_header common_header"> 15 | <img src={ user ? `/users/${user}/user.jpg` : '/images/spacecraft.jpg' } alt=""/> 16 | <div className="note_h_left"> 17 | <span className="note_username">{username}</span> 18 | <span className='note_time' >{TimeAgo(note_time)}</span> 19 | </div> 20 | </div> 21 | <div className="note_title"> 22 | <span>{fn.c_first(title)}</span> 23 | </div> 24 | <div className="note_content"> 25 | <span>{fn.shortener(fn.c_first(content), 500)}</span> 26 | </div> 27 | </div> 28 | </Link> 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/js/src/components/explore/explore-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import Title from '../others/title-comp' 4 | import { FadeIn } from 'animate-components' 5 | import { get_explores } from '../../actions/explore-action' 6 | import Explores_list from './explores-list' 7 | import Nothing from '../others/nothing-comp' 8 | import End from '../others/end-comp' 9 | 10 | @connect(store => { 11 | return { 12 | explore: store.explore.explores 13 | } 14 | }) 15 | 16 | export default class Explore extends React.Component { 17 | 18 | componentDidMount = () => this.props.dispatch(get_explores()) 19 | 20 | render(){ 21 | let 22 | { explore } = this.props, 23 | map_explore = explore.map(e => 24 | <Explores_list key={e.id} {...e} /> 25 | ) 26 | 27 | return( 28 | <div className="explore" > 29 | 30 | <Title value="Explore" /> 31 | 32 | <FadeIn duration="300ms" > 33 | <div className="explores" > 34 | {explore.length == 0 ? <Nothing mssg="No one to explore!!" /> : map_explore} 35 | {explore.length != 0 ? <End /> : null} 36 | </div> 37 | </FadeIn> 38 | 39 | </div> 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /routes/api-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | db = require('../models/db'), 4 | P = require('bluebird') 5 | 6 | // FOR GETTING THE ID OF ANY USER 7 | app.post('/get-id', (req, res) => { 8 | db.getId(req.body.username) 9 | .then(s => res.json(s) ) 10 | .catch(e => res.json(e) ) 11 | }) 12 | 13 | // FOR CHECKING IF IT'S A VALID USER 14 | app.post('/is-user-valid', (req, res) => { 15 | db.query('SELECT COUNT(id) AS userCount FROM users WHERE username=? LIMIT 1', [req.body.username]) 16 | .then(is => res.json(is[0].userCount == 1 ? true : false) ) 17 | .catch(err => res.json(err) ) 18 | }) 19 | 20 | // /FOR DETAILS OF GIVEN USER 21 | app.post('/get-details', (req, res) => { 22 | db.query('SELECT * FROM users WHERE username=?', [req.body.get]) 23 | .then(get => res.json(get[0]) ) 24 | .catch(err => res.json(err) ) 25 | }) 26 | 27 | // FOR EXPLORING NEW USERS 28 | app.post('/explore', async (req, res) => { 29 | let 30 | { id: session } = req.session, 31 | exp = [], 32 | followings = await db.query('SELECT id, username, email FROM users WHERE id <> ? ORDER BY RAND() LIMIT 10', [session]) 33 | 34 | for (let f of followings) { 35 | let is = await db.is_following(session, f.id) 36 | !is ? exp.push(f) : null 37 | } 38 | 39 | res.json(exp) 40 | }) 41 | 42 | module.exports = app 43 | -------------------------------------------------------------------------------- /public/js/src/components/error/error-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import { Link } from 'react-router-dom' 4 | import Title from '../others/title-comp' 5 | import { FadeIn } from 'animate-components' 6 | 7 | export default class Error extends React.Component{ 8 | render(){ 9 | let 10 | username = $('.data').data('username'), 11 | { params: { what } } = this.props.match, 12 | title, 13 | desc 14 | 15 | if(what == "notfound"){ 16 | title = "User not found" 17 | desc = "user" 18 | } else if(what == "note_notfound"){ 19 | title = "Note not found" 20 | desc = "note" 21 | } else { 22 | title = "Error" 23 | desc = "page" 24 | } 25 | 26 | return( 27 | <div class='error' > 28 | <Title value="Oops! {title}" /> 29 | <FadeIn duration="300ms" > 30 | <div className="welcome_div error_div"> 31 | <div className="error_info"> 32 | <span>Oops, the {desc} you're looking for does not exist!!</span> 33 | </div> 34 | <img src="/images/error-3.svg" alt="" /> 35 | <div class="error_bottom"> 36 | <Link to={`/profile/${username}`} className="sec_btn error_home" >View profile</Link> 37 | <Link to='/' className="pri_btn error_login" >Try going to homepage</Link> 38 | </div> 39 | </div> 40 | </FadeIn> 41 | </div> 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/js/src/components/home/home-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import Title from '../others/title-comp' 4 | import { FadeIn } from 'animate-components' 5 | import { connect } from 'react-redux' 6 | import { Link } from 'react-router-dom' 7 | 8 | import Feeds from './feeds-comp' 9 | import * as fn from '../../functions/functions' 10 | import * as note_action from '../../actions/notes-action' 11 | 12 | @connect(store => { 13 | return { 14 | notes: store.notes 15 | } 16 | }) 17 | 18 | export default class Home extends React.Component{ 19 | 20 | componentDidMount = () => 21 | this.props.dispatch(note_action.getFeeds()) 22 | 23 | render(){ 24 | let 25 | s_username = $('.data').data('username'), 26 | { notes: { feeds } } = this.props, 27 | no_of_feeds = feeds.length == 0 ? "No feeds" : feeds.length == 1 ? '1 feed' : `${feeds.length} notes` 28 | 29 | return( 30 | <div class='home' > 31 | <Title value="Home" /> 32 | <FadeIn duration="300ms" > 33 | <div className="home_info"> 34 | <span>{no_of_feeds}</span> 35 | <Link 36 | to={{ pathname: `/profile/${s_username}/create-note` }} 37 | class={`pri_btn ${!fn.e_v() ? "a_disabled" : ""}`} 38 | >{fn.e_v() ? "Create note" : "Verify email to create note"} 39 | </Link> 40 | </div> 41 | <Feeds /> 42 | </FadeIn> 43 | </div> 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /routes/note-int-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | P = require('bluebird'), 4 | db = require('../models/db') 5 | 6 | // CHECK IF SESSION LIKED THE NOTE OR NOT 7 | app.post('/liked-or-not', (req, res) => { 8 | P.coroutine(function* () { 9 | let 10 | { body, session } = req, 11 | [{ l }] = yield db.query('SELECT COUNT(like_id) AS l FROM likes WHERE like_by = ? AND note_id = ?', [session.id, body.note]) 12 | res.json(l == 0 ? false : true) 13 | })() 14 | }) 15 | 16 | // FOR LIKING THE NOTE 17 | app.post('/like', (req, res) => { 18 | P.coroutine(function* () { 19 | let 20 | { session, body } = req, 21 | insert = { 22 | like_by: session.id, 23 | like_by_username: session.username, 24 | note_id: parseInt(body.note), 25 | like_time: new Date().getTime() 26 | }, 27 | like = yield db.query('INSERT INTO likes SET ?', insert) 28 | res.json(Object.assign({}, insert, { like_id: like.insertId })) 29 | })() 30 | }) 31 | 32 | app.post('/unlike', (req, res) => { 33 | P.coroutine(function* () { 34 | let { session, body } = req 35 | yield db.query('DELETE FROM likes WHERE note_id=? AND like_by=?', [body.note, session.id]) 36 | res.json(null) 37 | })() 38 | }) 39 | 40 | // GET LIKES OF THE NOTE 41 | app.post('/likes', (req, res) => { 42 | db.query('SELECT * FROM likes WHERE note_id=? ORDER BY like_id DESC', [req.body.note]) 43 | .then(likes => res.json(likes)) 44 | .catch(err => res.json(err)) 45 | }) 46 | 47 | module.exports = app 48 | -------------------------------------------------------------------------------- /public/js/src/components/others/prompt-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import PropTypes from 'prop-types' 4 | import { FadeIn } from 'animate-components' 5 | 6 | export default class Prompt extends React.Component{ 7 | 8 | componentDidMount = () => $('.prompt-done').focus() 9 | 10 | render(){ 11 | let { title, content, actionText, action, state_updater, close } = this.props 12 | 13 | return ( 14 | <div class="prompt"> 15 | <FadeIn duration="200ms" > 16 | <div class="prompt-top"> 17 | <span class="prompt-title">{title}</span> 18 | <span onClick={() => close(null, state_updater)} ><i class="material-icons">clear</i></span> 19 | </div> 20 | <div class="prompt-middle"> 21 | <span class="prompt-content">{content}</span> 22 | </div> 23 | <div class="prompt-bottom"> 24 | <a href="#" class="sec_btn prompt-cancel" onClick={e => close(e, state_updater)} >Cancel</a> 25 | <a href="#" class="pri_btn prompt-done" onClick={action} >{actionText}</a> 26 | </div> 27 | </FadeIn> 28 | </div> 29 | ) 30 | 31 | } 32 | 33 | } 34 | 35 | Prompt.defaultProps = { 36 | title: "Title", 37 | content: "Main content goes here. Content should be of 2 lines to avoid the blur that Chrome creates!", 38 | actionText: "Action", 39 | action: () => { return false; } 40 | } 41 | 42 | Prompt.propTypes = { 43 | title: PropTypes.string, 44 | content: PropTypes.string, 45 | actionText: PropTypes.string, 46 | action: PropTypes.func 47 | } 48 | -------------------------------------------------------------------------------- /models/db.js: -------------------------------------------------------------------------------- 1 | const 2 | db = require('./mysql'), 3 | util = require('util'), 4 | bcrypt = require('bcrypt-nodejs') 5 | 6 | const query = (q, data) => { 7 | return new Promise((resolve, reject) => { 8 | db.query(q, data, (err, res) => { 9 | err ? reject(err) : resolve(res) 10 | }) 11 | }) 12 | } 13 | 14 | const createUser = user => { 15 | return new Promise((resolve, reject) => { 16 | bcrypt.hash(user.password, null, null, (error, hash) => { 17 | user.password = hash 18 | db.query('INSERT INTO users SET ?', user, (err, res) => { 19 | err ? reject(err) : resolve(res) 20 | }) 21 | }) 22 | }) 23 | } 24 | 25 | const comparePassword = (password, hash) => { 26 | return new Promise((resolve, reject) => { 27 | bcrypt.compare(password, hash, (err, res) => { 28 | err ? reject(err) : resolve(res) 29 | }) 30 | }) 31 | } 32 | 33 | // FUNCTION TO GET ID FROM USERNAME 34 | const getId = username => { 35 | return new Promise((resolve, reject) => { 36 | query('SELECT id FROM users WHERE username=? LIMIT 1', [username]) 37 | .then(s => resolve(s[0].id)) 38 | .catch(e => reject(e)) 39 | }) 40 | } 41 | 42 | const is_following = (session, user) => { 43 | return new Promise((resolve, reject) => { 44 | query('SELECT COUNT(follow_id) AS is_following FROM follow_system WHERE follow_by=? AND follow_to=? LIMIT 1', [session, user]) 45 | .then(is => resolve((is[0].is_following == 1) ? true : false)) 46 | .catch(e => reject(e)) 47 | }) 48 | } 49 | 50 | module.exports = { 51 | query, 52 | createUser, 53 | comparePassword, 54 | getId, 55 | is_following 56 | } 57 | -------------------------------------------------------------------------------- /public/js/src/user-system/user-system.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import Notify from 'handy-notification' 3 | import { commonLogin } from '../functions/functions' 4 | 5 | $('form.form_register').submit(e => { 6 | e.preventDefault() 7 | 8 | let 9 | username = $('.r_username').val(), 10 | email = $('.r_email').val(), 11 | password = $('.r_password').val(), 12 | password_again = $('.r_password_again').val() 13 | 14 | if(!username || !email || !password || !password_again ){ 15 | Notify({ value: "Values are missing!!" }) 16 | } else if(password != password_again){ 17 | Notify({ value: "Passwords don't match!!" }) 18 | } else { 19 | 20 | let signupOpt = { 21 | data: { 22 | username, 23 | email, 24 | password, 25 | password_again 26 | }, 27 | btn: $('.r_submit'), 28 | url: "/user/signup", 29 | redirect: "/registered", 30 | defBtnValue: "Sign up for free", 31 | } 32 | commonLogin(signupOpt) 33 | 34 | } 35 | 36 | }) 37 | 38 | $('form.form_login').submit(e => { 39 | e.preventDefault() 40 | 41 | let 42 | username = $('.l_username').val(), 43 | password = $('.l_password').val() 44 | 45 | if(!username || !password){ 46 | Notify({ value: "Values are missing!" }) 47 | } else { 48 | 49 | let loginOpt = { 50 | data: { 51 | username: $('.l_username').val(), 52 | password: $('.l_password').val() 53 | }, 54 | btn: $('.l_submit'), 55 | url: "/user/login", 56 | redirect: "/", 57 | defBtnValue: "Login to continue", 58 | } 59 | commonLogin(loginOpt) 60 | 61 | } 62 | 63 | }) 64 | -------------------------------------------------------------------------------- /public/js/src/components/app-comp.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 3 | 4 | import Header from './others/header-comp' 5 | import Profile from './profile/profile-comp' 6 | import Home from './home/home-comp' 7 | import Edit from './edit/edit-comp' 8 | import Explore from './explore/explore-comp' 9 | import Error from './error/error-comp' 10 | import EmailVerification from './email-verification/email-ver-comp' 11 | import Deactivate from './deactivate/deactivate-comp' 12 | import Viewnote from './note/view-note-comp' 13 | import Overlay from './others/overlay-comp' 14 | 15 | export default class App extends Component{ 16 | render(){ 17 | return( 18 | <Router> 19 | <div className="app"> 20 | <Header /> 21 | <div className="notes_wrapper"> 22 | <Route path="/view-note/:note" component={() => <Overlay type='colored' /> } /> 23 | <Switch> 24 | <Route path="/" exact component={Home} /> 25 | <Route path="/explore" component={Explore} /> 26 | <Route path="/edit" component={Edit} /> 27 | <Route path="/profile/:username" component={Profile} /> 28 | <Route path="/email-verification/:is" component={EmailVerification} /> 29 | <Route path="/view-note/:note" component={Viewnote} /> 30 | <Route path='/deactivate' component={Deactivate} /> 31 | <Route path="/error/:what" component={Error} /> 32 | <Route component={Error} /> 33 | </Switch> 34 | </div> 35 | </div> 36 | </Router> 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/js/src/reducers/follow-reducer.js: -------------------------------------------------------------------------------- 1 | const follow_defaults = { 2 | is_following: false, 3 | profile_views: 0, 4 | followers: [], 5 | followings: [] 6 | } 7 | 8 | const follow = (state=follow_defaults, action) => { 9 | let { payload: py } = action 10 | 11 | switch (action.type) { 12 | case "IS_FOLLOWING": 13 | return { ...state, is_following: py } 14 | break 15 | 16 | case "GET_PROFILE_VIEWS": 17 | return { ...state, profile_views: py } 18 | break 19 | 20 | case "GET_FOLLOWERS": 21 | return { ...state, followers: py } 22 | break 23 | 24 | case "GET_FOLLOWINGS": 25 | return { ...state, followings: py } 26 | break 27 | 28 | case "FOLLOWER": 29 | return { ...state, followers: follower(state.followers, py) } 30 | break 31 | 32 | case "UNFOLLOWER": 33 | return { ...state, followers: unfollower(state.followers, py) } 34 | break 35 | 36 | case "FOLLOWING": 37 | return { ...state, followings: following(state.followings, py) } 38 | break 39 | 40 | case "UNFOLLOWING": 41 | return { ...state, followings: unfollowing(state.followings, py) } 42 | break 43 | 44 | } 45 | return state 46 | } 47 | 48 | const follower = (followers, n) => { 49 | followers.unshift(n) 50 | return followers 51 | } 52 | 53 | const unfollower = (followers, n) => { 54 | return followers.filter(ff => ff.follow_by !== parseInt(n) ) 55 | } 56 | 57 | const following = (followings, n) => { 58 | followings.unshift(n) 59 | return followings 60 | } 61 | 62 | const unfollowing = (followings, n) => { 63 | return followings.filter(ff => ff.follow_to !== parseInt(n) ) 64 | } 65 | 66 | export default follow 67 | -------------------------------------------------------------------------------- /public/js/src/components/note/likes-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import Title from '../others/title-comp' 4 | import { FadeIn } from 'animate-components' 5 | import { Scrollbars } from 'react-custom-scrollbars' 6 | import * as fn from '../../functions/functions' 7 | 8 | import Goto from '../others/goto-comp' 9 | import Nothing from '../others/nothing-comp' 10 | import Like_items from './like-items' 11 | 12 | @connect(store => { 13 | return { 14 | note_int: store.note_int 15 | } 16 | }) 17 | 18 | export default class Likes extends React.Component{ 19 | 20 | back = e => fn.back(e, this.props.history) 21 | 22 | componentDidMount = () => fn.last_line_remover() 23 | componentDidUpdate = () => fn.last_line_remover() 24 | 25 | render(){ 26 | let 27 | { note_int: { likes, note_details: { note_id } } } = this.props, 28 | map_l = likes.map(l => 29 | <Like_items key={l.like_id} {...l} /> 30 | ) 31 | 32 | return( 33 | <div class='likes modal modal_big' > 34 | <Title value="Likes" /> 35 | <FadeIn duration="300ms" > 36 | <div className="likes_header modal_header"> 37 | <span className="title" >Likes</span> 38 | <Goto /> 39 | </div> 40 | <Scrollbars style={{ height: 450 }} className="likes_middle modal_middle"> 41 | <div className="modal_main"> 42 | { likes.length == 0 ? <Nothing showMssg={false} /> : map_l } 43 | </div> 44 | </Scrollbars> 45 | <div className="likes_bottom modal_bottom"> 46 | <a href='#' className='likes_cancel pri_btn' onClick={this.back} >Back</a> 47 | </div> 48 | </FadeIn> 49 | </div> 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /public/js/src/actions/follow-action.js: -------------------------------------------------------------------------------- 1 | import { post } from 'axios' 2 | 3 | const get_profile_views = username => { 4 | return dispatch => { 5 | post('/api/get-profile-views', { username }) 6 | .then(view => dispatch({ type: "GET_PROFILE_VIEWS", payload: view.data }) ) 7 | .catch(err => console.log(err) ) 8 | } 9 | } 10 | 11 | const is_following = username => { 12 | return dispatch => { 13 | post('/api/is-following', { username }) 14 | .then(is => dispatch({ type: "IS_FOLLOWING", payload: is.data }) ) 15 | .catch(err => console.log(err) ) 16 | } 17 | } 18 | 19 | const get_followers = username => { 20 | return dispatch => { 21 | post('/api/get-followers', { username }) 22 | .then(followers => dispatch({ type: "GET_FOLLOWERS", payload: followers.data }) ) 23 | .catch(err => console.log(err) ) 24 | } 25 | } 26 | 27 | const get_followings = username => { 28 | return dispatch => { 29 | post('/api/get-followings', { username }) 30 | .then(following => dispatch({ type: "GET_FOLLOWINGS", payload: following.data }) ) 31 | .catch(err => console.log(err) ) 32 | } 33 | } 34 | 35 | const follower = follower => { 36 | return { 37 | type: "FOLLOWER", 38 | payload: follower 39 | } 40 | } 41 | 42 | const unfollower = unfollower => { 43 | return{ 44 | type: "UNFOLLOWER", 45 | payload: unfollower 46 | } 47 | } 48 | 49 | const following = following => { 50 | return{ 51 | type: "FOLLOWING", 52 | payload: following 53 | } 54 | } 55 | 56 | const unfollowing = unfollowing => { 57 | return { 58 | type: "UNFOLLOWING", 59 | payload: unfollowing 60 | } 61 | } 62 | 63 | module.exports = { 64 | get_profile_views, 65 | is_following, 66 | get_followers, 67 | get_followings, 68 | follower, 69 | unfollower, 70 | following, 71 | unfollowing 72 | } 73 | -------------------------------------------------------------------------------- /public/js/src/components/note/create-note-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import { FadeIn } from 'animate-components' 4 | import Title from '../others/title-comp' 5 | import { connect } from 'react-redux' 6 | import * as fn from '../../functions/functions' 7 | import Goto from '../others/goto-comp' 8 | 9 | @connect(store => { 10 | return { 11 | notes: store.notes 12 | } 13 | }) 14 | 15 | export default class Create_note extends React.Component{ 16 | 17 | back = e => fn.back(e, this.props.history) 18 | 19 | addNote = e => { 20 | e.preventDefault() 21 | let 22 | title = $('.c_n_middle input[type="text"]').val(), 23 | content = $('.c_n_middle textarea').val(), 24 | { dispatch, history } = this.props 25 | fn.createNote({ title, content, dispatch, history }) 26 | } 27 | 28 | render(){ 29 | return ( 30 | <div class='create_note modal'> 31 | <Title value="Create note" /> 32 | <FadeIn duration="300ms" > 33 | <form onSubmit={this.addNote} > 34 | <div className="c_n_header modal_header"> 35 | <span className="title" >Create a note</span> 36 | <Goto /> 37 | </div> 38 | <div className="c_n_middle modal_middle"> 39 | <input type="text" placeholder='Title..' required spellCheck="false" autoComplete="false" autoFocus /> 40 | <textarea placeholder='Your note..' required spellCheck='false' autoComplete='false' ></textarea> 41 | </div> 42 | <div className="c_n_bottom modal_bottom"> 43 | <a href="#" className='c_n_cancel sec_btn' onClick={this.back} >Back</a> 44 | <input type="submit" className='c_n_add pri_btn' value='Add note' /> 45 | </div> 46 | </form> 47 | </FadeIn> 48 | </div> 49 | ) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /public/js/src/components/profile/follow/followings-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Title from '../../others/title-comp' 3 | import { FadeIn } from 'animate-components' 4 | import { connect } from 'react-redux' 5 | import { Scrollbars } from 'react-custom-scrollbars' 6 | 7 | import Following_items from './following-items' 8 | import Goto from '../../others/goto-comp' 9 | import Nothing from '../../others/nothing-comp' 10 | import * as fn from '../../../functions/functions' 11 | 12 | @connect(store => { 13 | return { 14 | follow: store.follow, 15 | user: store.user 16 | } 17 | }) 18 | 19 | export default class Followings extends React.Component{ 20 | 21 | back = e => fn.back(e, this.props.history) 22 | 23 | componentWillReceiveProps = props => fn.last_line_remover() 24 | componentDidMount = () => fn.last_line_remover() 25 | 26 | render(){ 27 | let 28 | { follow: { followings }, user: { user_details: { username } } } = this.props, 29 | map_f = followings.map(f => <Following_items key={f.follow_id} {...f} /> ) 30 | 31 | return( 32 | <div class='followers modal modal_big' > 33 | 34 | <Title value={`Followings • @${username}`} /> 35 | 36 | <FadeIn duration="300ms" > 37 | <div className="fer_header modal_header"> 38 | <span className="title" >Followers</span> 39 | <Goto /> 40 | </div> 41 | <Scrollbars style={{ height: 450 }} className="fer_middle modal_middle"> 42 | <div className="modal_main"> 43 | { followings.length == 0 ? <Nothing showMssg={false} /> : map_f } 44 | </div> 45 | </Scrollbars> 46 | <div className="fer_bottom modal_bottom"> 47 | <a href='#' className='fer_cancel pri_btn' onClick={this.back} >Back</a> 48 | </div> 49 | </FadeIn> 50 | 51 | </div> 52 | ) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /public/js/src/components/profile/follow/followers-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Title from '../../others/title-comp' 3 | import { FadeIn } from 'animate-components' 4 | import { connect } from 'react-redux' 5 | import { Scrollbars } from 'react-custom-scrollbars' 6 | 7 | import Follower_items from './follower-items' 8 | import Goto from '../../others/goto-comp' 9 | import Nothing from '../../others/nothing-comp' 10 | import * as fn from '../../../functions/functions' 11 | 12 | @connect(store => { 13 | return { 14 | follow: store.follow, 15 | user: store.user 16 | } 17 | }) 18 | 19 | export default class Followers extends React.Component{ 20 | 21 | back = e => fn.back(e, this.props.history) 22 | 23 | componentDidMount = () => fn.last_line_remover() 24 | componentWillReceiveProps = props => fn.last_line_remover() 25 | 26 | render(){ 27 | let 28 | { 29 | follow: { followers }, 30 | user: { user_details: { username } } 31 | } = this.props, 32 | map_f = followers.map(f => 33 | <Follower_items key={f.follow_id} {...f} /> 34 | ) 35 | 36 | return ( 37 | <div class='followers modal modal_big' > 38 | 39 | <Title value={`Followers • @${username}`} /> 40 | 41 | <FadeIn duration="300ms" > 42 | <div className="fer_header modal_header"> 43 | <span className="title" >Followers</span> 44 | <Goto /> 45 | </div> 46 | <Scrollbars style={{ height: 450 }} className="fer_middle modal_middle" > 47 | <div className="modal_main"> 48 | { followers.length == 0 ? <Nothing showMssg={false} /> : map_f } 49 | </div> 50 | </Scrollbars> 51 | <div className="fer_bottom modal_bottom"> 52 | <a href='#' className='fer_cancel pri_btn' onClick={this.back} >Back</a> 53 | </div> 54 | </FadeIn> 55 | 56 | </div> 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | // Installed packages 4 | const 5 | express = require('express'), 6 | { env: { PORT, SESSION_SECRET_LETTER } } = process, 7 | hbs = require('express-handlebars'), 8 | path = require('path'), 9 | logger = require('morgan'), 10 | favicon = require('serve-favicon'), 11 | bodyParser = require('body-parser'), 12 | validator = require('express-validator'), 13 | session = require('client-sessions'), 14 | hl = require('handy-log'), 15 | app = express() 16 | 17 | // Requiring project files 18 | const 19 | uRoutes = require('./routes/user-routes'), 20 | apiRoutes = require('./routes/api-routes'), 21 | mRoutes = require('./routes/main-routes'), 22 | followRoutes = require('./routes/follow-routes'), 23 | noteRoutes = require('./routes/note_routes'), 24 | nIntRoutes = require('./routes/note-int-routes'), 25 | editRoutes = require('./routes/edit-routes'), 26 | mw = require('./models/middlewares') 27 | 28 | // View engine 29 | app.engine('hbs', hbs({ 30 | extname: "hbs" 31 | })) 32 | app.set('view engine', 'hbs') 33 | 34 | // Middlewares 35 | app.use(favicon( 36 | path.join(__dirname + "/public/images/favicon/favicon.png") 37 | )) 38 | // app.use(logger("dev")) 39 | app.use(bodyParser.json()) 40 | app.use(bodyParser.urlencoded({ 41 | extended: false 42 | })) 43 | app.use(validator()) 44 | app.use(session({ 45 | cookieName: "session", 46 | secret: SESSION_SECRET_LETTER, 47 | duration: 60 * 60 * 1000, 48 | activeDuration: 5 * 60 * 1000 49 | })) 50 | app.use(express.static(path.join(__dirname + "/public/"))) 51 | 52 | // Middleware for some local variables to be used in the template 53 | app.use(mw.variables) 54 | 55 | // Route files (Order is important) 56 | app.use('/', uRoutes) 57 | app.use('/api', apiRoutes) 58 | app.use('/api', followRoutes) 59 | app.use('/api', noteRoutes) 60 | app.use('/api', nIntRoutes) 61 | app.use('/api', editRoutes) 62 | app.use('/', mRoutes) 63 | 64 | app.listen(PORT, () => hl.rainbow('App running..')) 65 | -------------------------------------------------------------------------------- /public/js/src/components/deactivate/deactivate-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import axios from 'axios' 4 | import { FadeIn } from 'animate-components' 5 | import Title from '../others/title-comp' 6 | import * as fn from '../../functions/functions' 7 | 8 | import Overlay from '../others/overlay-comp' 9 | import Prompt from '../others/prompt-comp' 10 | 11 | export default class Deactivate extends React.Component{ 12 | 13 | state = { deactivate: false } 14 | 15 | toggle_ = (e, what) => { 16 | e ? e.preventDefault() : null 17 | switch (what) { 18 | case "deactivate": 19 | this.setState(state => ({ deactivate: !state.deactivate })) 20 | break 21 | } 22 | } 23 | 24 | deactivate = e => { 25 | e.preventDefault() 26 | fn.deactivate() 27 | } 28 | 29 | render(){ 30 | let { deactivate } = this.state 31 | 32 | return ( 33 | <div> 34 | <Title value="Deactivate your account" /> 35 | <FadeIn duration="300ms" > 36 | <div class="registered deactivate" > 37 | <span className="deactivate_title" >Deactivate your account?</span> 38 | <span>All of your notes, followers, followings & info will be permanently deleted. And you won't be able to find it again.</span> 39 | <div className="deactivate_btn"> 40 | <a href="#" className="pri_btn d_btn" onClick={e => this.toggle_(e, "deactivate")} >Deactivate</a> 41 | </div> 42 | </div> 43 | </FadeIn> 44 | 45 | {deactivate ? <Overlay /> : null} 46 | { 47 | deactivate ? 48 | <Prompt 49 | title="Deactivate your account" 50 | content="Are you sure, you wanna permanently deactivate your account? There's no undo so you won't be able login with this account." 51 | actionText="Deactivate" 52 | action={this.deactivate} 53 | state_updater="deactivate" 54 | close={this.toggle_} 55 | /> 56 | : null 57 | } 58 | 59 | </div> 60 | ) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-mini-social-network", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "nodemon app.js", 8 | "dev": "webpack -d --watch", 9 | "build": "webpack -p --watch", 10 | "less": "less-watch-compiler", 11 | "public": "concurrently \"npm run dev\" \"npm run less\"" 12 | }, 13 | "author": "takkar <www.shtakkar@gmail.com>", 14 | "license": "ISC", 15 | "dependencies": { 16 | "animate-components": "^1.4.2", 17 | "axios": "^0.16.1", 18 | "bcrypt-nodejs": "^0.0.3", 19 | "bluebird": "^3.5.0", 20 | "body-parser": "^1.17.2", 21 | "client-sessions": "^0.8.0", 22 | "concurrently": "^3.5.0", 23 | "dotenv": "^4.0.0", 24 | "express": "^4.15.2", 25 | "express-handlebars": "^3.0.0", 26 | "express-validator": "^3.2.0", 27 | "handy-copy": "^1.0.6", 28 | "handy-image-processor": "^1.0.1", 29 | "handy-log": "^1.0.2", 30 | "handy-notification": "^1.0.23", 31 | "handy-timeago": "^1.0.1", 32 | "handy-tooltip": "^1.0.10", 33 | "jquery": "^3.2.1", 34 | "morgan": "^1.8.2", 35 | "multer": "^1.3.0", 36 | "mysql": "^2.13.0", 37 | "nodemailer": "^4.0.1", 38 | "prop-types": "^15.5.8", 39 | "react": "^15.5.4", 40 | "react-custom-scrollbars": "^4.1.2", 41 | "react-dom": "^15.5.4", 42 | "react-helmet": "^5.1.3", 43 | "react-redux": "^5.0.4", 44 | "react-router": "^4.1.2", 45 | "react-router-dom": "^4.1.2", 46 | "redux": "^3.6.0", 47 | "redux-logger": "^3.0.1", 48 | "redux-promise-middleware": "4.2.1", 49 | "redux-thunk": "^2.2.0", 50 | "serve-favicon": "^2.4.3" 51 | }, 52 | "devDependencies": { 53 | "babel-core": "^6.24.1", 54 | "babel-loader": "^7.0.0", 55 | "babel-plugin-react-html-attrs": "^2.0.0", 56 | "babel-plugin-transform-class-properties": "^6.24.1", 57 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 58 | "babel-plugin-transform-react-jsx-source": "^6.22.0", 59 | "babel-plugin-transform-runtime": "^6.23.0", 60 | "babel-preset-env": "^1.4.0", 61 | "babel-preset-es2015": "^6.24.1", 62 | "babel-preset-react": "^6.24.1", 63 | "babel-preset-stage-0": "^6.24.1" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /public/js/src/components/explore/explores-list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import axios from 'axios' 3 | import { Link } from 'react-router-dom' 4 | import { connect } from 'react-redux' 5 | import * as fn from '../../functions/functions' 6 | 7 | export default class Explores_list extends React.Component{ 8 | 9 | state = { 10 | no_of_notes: 0, 11 | is_following: false 12 | } 13 | 14 | componentDidMount = async () => { 15 | let 16 | { dispatch, id, username } = this.props, 17 | { data: no_of_notes } = await axios.post('/api/no-of-notes', { user: id }), 18 | { data: is_following } = await axios.post('/api/is-following', { username }) 19 | this.setState({ no_of_notes, is_following }) 20 | } 21 | 22 | follow = e => { 23 | e.preventDefault() 24 | let 25 | { id, username } = this.props, 26 | obj = { 27 | user: id, 28 | username, 29 | done: () => this.setState({ is_following: true }) 30 | } 31 | fn.follow(obj) 32 | } 33 | 34 | unfollow = e => { 35 | e.preventDefault() 36 | let 37 | { id, username } = this.props, 38 | obj = { 39 | user: id, 40 | done: () => this.setState({ is_following: false }) 41 | } 42 | fn.unfollow(obj) 43 | } 44 | 45 | render(){ 46 | let 47 | { id, username, email } = this.props, 48 | { no_of_notes, is_following } = this.state, 49 | n = no_of_notes == 0 ? '0 notes' : no_of_notes == 1 ? '1 note' : `${no_of_notes} notes` 50 | 51 | return( 52 | <div className="explores_list" > 53 | <div className="exl_main"> 54 | <img src={id ? `/users/${id}/user.jpg` : '/images/spacecraft.jpg'} /> 55 | <div className="exl_content"> 56 | <Link to={`/profile/${username}`} className="exl_username" >{username}</Link> 57 | <div className="exl_desc"> 58 | <span className="exl_email">{email}</span> 59 | <span className="exl_desc_sep">•</span> 60 | <span className="exl_followers">{n}</span> 61 | </div> 62 | </div> 63 | </div> 64 | <div className="exl_ff"> 65 | { 66 | is_following ? 67 | <a href="#" className="pri_btn unfollow exl_unfollow" onClick={this.unfollow} >Followed</a> 68 | : 69 | <a href="#" className="pri_btn follow exl_follow" onClick={this.follow} >Follow</a> 70 | } 71 | </div> 72 | </div> 73 | ) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /routes/note_routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | P = require('bluebird'), 4 | db = require('../models/db') 5 | 6 | // FOR GETTING ALL THE USER NOTES 7 | app.post('/get-notes', (req, res) => { 8 | P.coroutine(function* () { 9 | let 10 | id = yield db.getId(req.body.get), 11 | notes = yield db.query("SELECT * FROM notes WHERE user = ? ORDER BY note_id DESC", [id]) 12 | res.json(notes) 13 | })() 14 | }) 15 | 16 | // FOR GETTING ALL THE DETAILS OF A NOTE BY A NOTE_ID 17 | app.post('/get-note-details', (req, res) => { 18 | db.query('SELECT * FROM notes WHERE note_id=? LIMIT 1', [req.body.note]) 19 | .then(s => res.json(s[0])) 20 | .catch(e => res.json(e)) 21 | }) 22 | 23 | // FOR GETTING ALL THE DETAILS OF A NOTE BY A NOTE_ID 24 | app.post('/delete-note', (req, res) => { 25 | P.coroutine(function* () { 26 | let { note } = req.body 27 | yield db.query('DELETE FROM likes WHERE note_id=?', [note]), 28 | yield db.query('DELETE FROM notes WHERE note_id=?', [note]) 29 | res.json({ mssg: "Note Deleted!!" }) 30 | })() 31 | }) 32 | 33 | // FOR EDITING THE NOTE 34 | app.post('/edit-note', (req, res) => { 35 | let { title, content, note_id } = req.body 36 | db.query('UPDATE notes SET title=?, content=? WHERE note_id=? AND user=?', [title, content, note_id, req.session.id]) 37 | .then(update => res.json({ mssg: 'Note edited!' }) ) 38 | .catch(err => res.json(err)) 39 | }) 40 | 41 | // FOR CREATING A NOTE 42 | app.post('/create-note', (req, res) => { 43 | let 44 | { session, body } = req, 45 | insert = { 46 | user: session.id, 47 | username: session.username, 48 | title: body.title, 49 | content: body.content, 50 | note_time: new Date().getTime() 51 | } 52 | db.query('INSERT INTO notes SET ?', insert) 53 | .then(s => { 54 | let n = Object.assign({}, insert, { note_id: s.insertId }) 55 | res.json(n) 56 | }) 57 | .catch(e => res.json(e)) 58 | }) 59 | 60 | app.post('/no-of-notes', (req, res) => { 61 | P.coroutine(function* () { 62 | let [{ count }] = yield db.query('SELECT COUNT(note_id) AS count FROM notes WHERE user=?', [req.body.user]) 63 | res.json(count) 64 | })() 65 | }) 66 | 67 | // GET ALL FEEDS 68 | app.post('/feeds', (req, res) => { 69 | db.query('SELECT notes.note_id, notes.user, notes.username, notes.title, notes.content, notes.note_time FROM notes, follow_system WHERE follow_system.follow_by = ? AND follow_system.follow_to = notes.user ORDER BY notes.note_time DESC', [req.session.id]) 70 | .then(feed => res.json(feed)) 71 | .catch(err => res.json(err)) 72 | }) 73 | 74 | module.exports = app 75 | -------------------------------------------------------------------------------- /public/js/src/components/note/like-items.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import axios from 'axios' 3 | import $ from 'jquery' 4 | import TimeAgo from 'handy-timeago' 5 | import { Link } from 'react-router-dom' 6 | import { connect } from 'react-redux' 7 | import * as fn from '../../functions/functions' 8 | 9 | @connect(store => { 10 | return {} 11 | }) 12 | 13 | export default class Like_items extends React.Component{ 14 | 15 | state = { is_following: false } 16 | 17 | componentDidMount = async () => { 18 | let { like_by, like_by_username: username } = this.props 19 | if(!fn.Me(like_by)) { 20 | let { data } = await axios.post('/api/is-following', { username }) 21 | this.setState({ is_following: data }) 22 | } 23 | } 24 | 25 | follow = e => { 26 | e.preventDefault() 27 | let 28 | { like_by, like_by_username, dispatch } = this.props, 29 | getid = $('.profile_data').data('getid'), 30 | obj = { 31 | user: like_by, 32 | username: like_by_username, 33 | done: () => this.setState({ is_following: true }) 34 | } 35 | fn.follow(obj) 36 | } 37 | 38 | unfollow = e => { 39 | e.preventDefault() 40 | let 41 | { like_by, dispatch } = this.props, 42 | getid = $('.profile_data').data('getid'), 43 | obj = { 44 | user: like_by, 45 | done: () => this.setState({ is_following: false }) 46 | } 47 | fn.unfollow(obj) 48 | } 49 | 50 | render(){ 51 | let 52 | { like_by, like_by_username, like_time } = this.props, 53 | { is_following } = this.state 54 | 55 | return( 56 | <div className="modal_items fer_items" > 57 | <div className="modal_it_img"> 58 | <img src={ like_by ? `/users/${like_by}/user.jpg` : `/images/spacecraft.jpg`} /> 59 | </div> 60 | <div className="modal_it_content"> 61 | <div className="modal_it_info"> 62 | <Link to={`/profile/${like_by_username}`} class='modal_it_username' >{like_by_username}</Link> 63 | <span class='modal_it_light' >{TimeAgo(like_time)}</span> 64 | </div> 65 | <div className="modal_ff"> 66 | { 67 | fn.Me(like_by) ? 68 | <Link to={`/profile/${like_by}`} class='pri_btn follow' >Profile</Link> 69 | : is_following ? 70 | <a href="#" class='pri_btn unfollow' onClick={this.unfollow} >Unfollow</a> 71 | : 72 | <a href="#" class='pri_btn follow' onClick={this.follow} >Follow</a> 73 | } 74 | </div> 75 | </div> 76 | <hr /> 77 | </div> 78 | ) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /public/js/src/components/profile/follow/following-items.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import axios from 'axios' 3 | import $ from 'jquery' 4 | import TimeAgo from 'handy-timeago' 5 | import { connect } from 'react-redux' 6 | import { Link } from 'react-router-dom' 7 | import * as fn from '../../../functions/functions' 8 | 9 | @connect(store => { 10 | return { 11 | user: store.user 12 | } 13 | }) 14 | 15 | export default class Following_items extends React.Component{ 16 | 17 | state = { is_following: false } 18 | 19 | componentDidMount = async () => { 20 | let { follow_to, follow_to_username } = this.props 21 | if(!fn.Me(follow_to)) { 22 | let { data } = await axios.post('/api/is-following', { username: follow_to_username }) 23 | this.setState({ is_following: data }) 24 | } 25 | } 26 | 27 | follow = e => { 28 | e.preventDefault() 29 | let 30 | { dispatch, follow_to, follow_to_username, user: { user_details } } = this.props, 31 | obj = { 32 | user: follow_to, 33 | username: follow_to_username, 34 | dispatch, 35 | update_followings: fn.Me(user_details.id) 36 | } 37 | fn.follow(obj) 38 | this.setState({ is_following: true }) 39 | } 40 | 41 | unfollow = e => { 42 | e.preventDefault() 43 | let 44 | { dispatch, follow_to, user: { user_details } } = this.props, 45 | obj = { 46 | user: follow_to, 47 | dispatch, 48 | update_followings: fn.Me(user_details.id) 49 | } 50 | fn.unfollow(obj) 51 | this.setState({ is_following: false }) 52 | } 53 | 54 | render(){ 55 | let 56 | { follow_to, follow_to_username, follow_time } = this.props, 57 | { is_following } = this.state 58 | 59 | return ( 60 | <div className="modal_items fer_items" > 61 | <div className="modal_it_img"> 62 | <img src={ follow_to ? `/users/${follow_to}/user.jpg` : `/images/spacecraft.jpg`} /> 63 | </div> 64 | <div className="modal_it_content"> 65 | <div className="modal_it_info"> 66 | <Link to={`/profile/${follow_to_username}`} class='modal_it_username' >{follow_to_username}</Link> 67 | <span class='modal_it_light' >{TimeAgo(follow_time)}</span> 68 | </div> 69 | <div className="modal_ff"> 70 | { 71 | fn.Me(follow_to) ? 72 | <Link to={`/profile/${follow_to_username}`} class='pri_btn follow' >Profile</Link> 73 | : is_following ? 74 | <a href="#" class='pri_btn unfollow' onClick={this.unfollow} >Unfollow</a> 75 | : 76 | <a href="#" class='pri_btn follow' onClick={this.follow} >Follow</a> 77 | } 78 | </div> 79 | </div> 80 | <hr /> 81 | </div> 82 | ) 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /public/js/src/components/profile/follow/follower-items.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import axios from 'axios' 3 | import $ from 'jquery' 4 | import TimeAgo from 'handy-timeago' 5 | import { connect } from 'react-redux' 6 | import { Link } from 'react-router-dom' 7 | import * as fn from '../../../functions/functions' 8 | 9 | @connect(store => { 10 | return { 11 | user: store.user 12 | } 13 | }) 14 | 15 | export default class Follower_items extends React.Component{ 16 | 17 | state = { is_following: false } 18 | 19 | componentDidMount = async () => { 20 | let { follow_by, follow_by_username } = this.props 21 | if(!fn.Me(follow_by)) { 22 | let { data } = await axios.post('/api/is-following', { username: follow_by_username }) 23 | this.setState({ is_following: data }) 24 | } 25 | } 26 | 27 | follow = e => { 28 | e.preventDefault() 29 | let 30 | { dispatch, follow_by, follow_by_username, user: { user_details } } = this.props, 31 | obj = { 32 | user: follow_by, 33 | username: follow_by_username, 34 | dispatch, 35 | update_followings: fn.Me(user_details.id), 36 | done: () => this.setState({ is_following: true }) 37 | } 38 | fn.follow(obj) 39 | } 40 | 41 | unfollow = e => { 42 | e.preventDefault() 43 | let 44 | { dispatch, follow_by, user: { user_details } } = this.props, 45 | obj = { 46 | user: follow_by, 47 | dispatch, 48 | update_followings: fn.Me(user_details.id), 49 | done: () => this.setState({ is_following: false }) 50 | } 51 | fn.unfollow(obj) 52 | } 53 | 54 | render(){ 55 | let 56 | { follow_by, follow_by_username, follow_time } = this.props, 57 | { is_following } = this.state 58 | 59 | return ( 60 | <div className="modal_items fer_items" > 61 | <div className="modal_it_img"> 62 | <img src={ follow_by ? `/users/${follow_by}/user.jpg` : `/images/spacecraft.jpg`} /> 63 | </div> 64 | <div className="modal_it_content"> 65 | <div className="modal_it_info"> 66 | <Link to={`/profile/${follow_by_username}`} class='modal_it_username' >{follow_by_username}</Link> 67 | <span class='modal_it_light' >{TimeAgo(follow_time)}</span> 68 | </div> 69 | <div className="modal_ff"> 70 | { 71 | fn.Me(follow_by) ? 72 | <Link to={`/profile/${follow_by_username}`} class='pri_btn follow' >Profile</Link> 73 | : is_following ? 74 | <a href="#" class='pri_btn unfollow' onClick={this.unfollow} >Unfollow</a> 75 | : 76 | <a href="#" class='pri_btn follow' onClick={this.follow} >Follow</a> 77 | } 78 | </div> 79 | </div> 80 | <hr /> 81 | </div> 82 | ) 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Single-Page-Social-Network 2 | A reactive single-page-social-network created with React. Screenshots below!! 3 | 4 | **No longer maintained. But it works** 5 | 6 | **[Please visit this link](https://github.com/yTakkar/React-Instagram-Clone-2.0)** for the updated version of this project. 7 | 8 | # Other Versions 9 | **[Vue Version](https://github.com/yTakkar/Vue-Mini-Social-Network)** 10 | 11 | **[Golang Version](https://github.com/yTakkar/Go-Page-Social-Network)** 12 | 13 | **[Ruby On Rails Version](https://github.com/yTakkar/Rails-Mini-Social-Network)** 14 | 15 | # Quick liks 16 | 1. [Screenshots](#screenshots) 17 | 2. [Own the project](#own-the-project) 18 | 19 | # Screenshots 20 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.27.11.png) 21 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.27.24.png) 22 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.27.34.png) 23 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.27.45.png) 24 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.29.13.png) 25 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.31.06.png) 26 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/15f084078b23b862a7537adbc721623e0b81578d/screenshots/Snap%202017-09-17%20at%2013.30.18.png) 27 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.29.35.png) 28 | ![alt text](https://raw.githubusercontent.com/yTakkar/Single-Page-Social-Network/master/screenshots/Snap%202017-07-27%20at%2000.28.54.png) 29 | 30 | [More screenshots](https://github.com/yTakkar/Single-Page-Social-Network/tree/master/screenshots). 31 | 32 | UI is taken from [Instagam-clone](https://github.com/yTakkar/Instagram-Clone) I created!! 33 | 34 | # Own the project 35 | 1. First install all dependencies: 36 | ```bash 37 | # with npm 38 | npm install 39 | 40 | # or with yarn 41 | yarn 42 | ``` 43 | 44 | 2. Open PHPMyAdmin, create a DB & import `db.sql` file. 45 | 3. Create a `.env` file and insert the following code. Replace values with yours!! 46 | 47 | ```javascript 48 | PORT=YOUR_PORT 49 | MYSQL_HOST="host" 50 | MYSQL_USER="user" 51 | MYSQL_PASSWORD="password" 52 | MYSQL_DATABASE="db" 53 | MAIL="yourgmail@gmail.com" 54 | MAIL_PASSWORD="gmail-password" 55 | SESSION_SECRET_LETTER="anything-secret" 56 | ``` 57 | 58 | 4. Start the server 59 | ```javascript 60 | npm start 61 | ``` 62 | 63 | 5. Now run the app 64 | ```javacript 65 | localhost:[PORT] PORT = YOU DEFINED IN .ENV FILE. 1157 BY DEFAULT!! 66 | ``` 67 | 68 | 6. Enjoy!! 69 | 70 | # Contribute 71 | Show your support by 🌟 the project!! 72 | 73 | Feel free to contribute!! 74 | -------------------------------------------------------------------------------- /routes/follow-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | P = require('bluebird'), 4 | db = require('../models/db') 5 | 6 | // TO CHECK IF SESSION FOLLOWING USER 7 | app.post('/is-following', (req, res) => { 8 | P.coroutine(function* () { 9 | let 10 | { body: { username }, session: { id: session } } = req, 11 | id = yield db.getId(username), 12 | is = yield db.is_following(session, id) 13 | res.json(is) 14 | })().catch(e => res.json(e.stack)) 15 | }) 16 | 17 | app.post('/follow', (req, res) => { 18 | P.coroutine(function* () { 19 | let 20 | { user, username } = req.body, 21 | { username: susername, id: session } = req.session, 22 | insert = { 23 | follow_by: session, 24 | follow_by_username: susername, 25 | follow_to: user, 26 | follow_to_username: username, 27 | follow_time: new Date().getTime() 28 | }, 29 | f = yield db.query('INSERT INTO follow_system SET ?', insert) 30 | res.json({ ...insert, follow_id: f.insertId }) 31 | })() 32 | }) 33 | 34 | // TO UNFOLLOW 35 | app.post('/unfollow', (req, res) => { 36 | db.query('DELETE FROM follow_system WHERE follow_by=? AND follow_to=?', [req.session.id, req.body.user]) 37 | .then(unfollow => res.json(unfollow)) 38 | .catch(err => res.json(err)) 39 | }) 40 | 41 | // TO GET FOLLOWERS 42 | app.post('/get-followers', (req, res) => { 43 | P.coroutine(function* () { 44 | let 45 | id = yield db.getId(req.body.username), 46 | followers = yield db.query('SELECT * FROM follow_system WHERE follow_to = ? ORDER BY follow_time DESC', [ id ]) 47 | res.json(followers) 48 | })() 49 | }) 50 | 51 | // TO GET FOLLOWINGS 52 | app.post('/get-followings', (req, res) => { 53 | P.coroutine(function* () { 54 | let 55 | id = yield db.getId(req.body.username), 56 | followings = yield db.query('SELECT follow_id, follow_to, follow_to_username, follow_time FROM follow_system WHERE follow_by = ? ORDER BY follow_time DESC', [ id ]) 57 | res.json(followings) 58 | })() 59 | }) 60 | 61 | // FOR PROFILE VIEW 62 | app.post('/view-profile', (req, res) => { 63 | P.coroutine(function* () { 64 | let 65 | { username } = req.body, 66 | { id: session } = req.session, 67 | id = yield db.getId(username), 68 | [{ time: dtime }] = yield db.query('SELECT MAX(view_time) as time FROM profile_views WHERE view_by=? AND view_to=?', [session, id]), 69 | time = parseInt(new Date().getTime() - parseInt(dtime)) 70 | 71 | if (time >= 120000 || !dtime) { 72 | let insert = { 73 | view_by: session, 74 | view_by_username: username, 75 | view_to: id, 76 | view_time: new Date().getTime() 77 | } 78 | yield db.query('INSERT INTO profile_views SET ?', insert) 79 | } 80 | 81 | res.json('Hello, World!!') 82 | 83 | })() 84 | }) 85 | 86 | 87 | // FOR GETTING PROFILE VIEWS 88 | app.post('/get-profile-views', (req, res) => { 89 | P.coroutine(function* () { 90 | let 91 | { username } = req.body, 92 | id = yield db.getId(username), 93 | [{ count }] = yield db.query('SELECT COUNT(view_id) AS count FROM profile_views WHERE view_to = ?', [id]) 94 | res.json(count) 95 | })() 96 | }) 97 | 98 | module.exports = app 99 | -------------------------------------------------------------------------------- /public/js/src/components/profile/profile-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import axios from 'axios' 4 | import Title from '../others/title-comp' 5 | import { connect } from 'react-redux' 6 | import { Route, Link, Redirect } from 'react-router-dom' 7 | import { FadeIn } from 'animate-components' 8 | import P from 'bluebird' 9 | 10 | import * as user_actions from '../../actions/user-action' 11 | import * as notes_actions from '../../actions/notes-action' 12 | import * as follow_action from '../../actions/follow-action' 13 | import * as fn from '../../functions/functions' 14 | 15 | import Banner from './banner-comp' 16 | import Filter_notes from './filter-notes-comp' 17 | import Notes from './notes-comp' 18 | import CreateNote from '../note/create-note-comp' 19 | import Overlay from '../others/overlay-comp' 20 | import Followers from './follow/followers-comp' 21 | import Followings from './follow/followings-comp' 22 | 23 | @connect(store => { 24 | return { 25 | store 26 | } 27 | }) 28 | 29 | export default class Profile extends React.Component{ 30 | 31 | state = { 32 | invalid_user: false, 33 | notes: [] 34 | } 35 | 36 | iur = () => this.setState({ invalid_user: true }) 37 | 38 | componentDidMount = () => { 39 | let { 40 | match: { params: { username } }, 41 | dispatch, 42 | store: { user } 43 | } = this.props 44 | fn.forProfile({ dispatch, username, invalidUser: this.iur }) 45 | } 46 | 47 | componentWillReceiveProps = ({ match, dispatch, store }) => { 48 | if(this.props.match.url != match.url){ 49 | fn.forProfile({ dispatch, username: match.params.username, invalidUser: this.iur }) 50 | } 51 | this.setState({ notes: store.notes.notes }) 52 | } 53 | 54 | filter = e => { 55 | let 56 | { store: { notes: { notes } } } = this.props, 57 | { target: { value } } = e, 58 | f = notes.filter(el => el.title.toLowerCase().includes(value.toLowerCase()) ) 59 | this.setState({ notes: f }) 60 | } 61 | 62 | render(){ 63 | let 64 | { invalid_user, notes } = this.state, 65 | { match, match: { params: { username }, url }, store: { user, notes: p_notes } } = this.props, 66 | s_username = $('.data').data('username') 67 | 68 | return( 69 | <div> 70 | 71 | { invalid_user ? <Redirect to="/error/notfound" /> : null } 72 | 73 | <Title value={`@${username}`} /> 74 | 75 | <div 76 | class='profile-data' 77 | id="profile-data" 78 | data-get-username={username} 79 | data-getid={user.user_details.id} 80 | ></div> 81 | 82 | <FadeIn duration="300ms" > 83 | <div className="aligner"> 84 | <Banner url={match.url} notes={notes} /> 85 | <Filter_notes filter={this.filter} notes_length={p_notes.notes.length} /> 86 | <Notes notes={notes} setState={this.setState} /> 87 | </div> 88 | </FadeIn> 89 | 90 | { fn.e_v() ? <Route path={`/profile/${s_username}/create-note`} component={Overlay} /> : null } 91 | { fn.e_v() ? <Route path={`/profile/${s_username}/create-note`} component={CreateNote} /> : null } 92 | 93 | <Route path={`${match.url}/followers`} component={Overlay} /> 94 | <Route path={`${match.url}/followers`} component={Followers} /> 95 | 96 | <Route path={`${match.url}/followings`} component={Overlay} /> 97 | <Route path={`${match.url}/followings`} component={Followings} /> 98 | 99 | </div> 100 | ) 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /public/js/src/components/profile/banner-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import { Link } from 'react-router-dom' 4 | import { connect } from 'react-redux' 5 | import * as fn from '../../functions/functions' 6 | 7 | @connect(store => { 8 | return { 9 | user: store.user, 10 | follow: store.follow, 11 | note: store.notes 12 | } 13 | }) 14 | 15 | export default class Banner extends React.Component { 16 | 17 | state = { is_following: false } 18 | 19 | componentWillReceiveProps = ({ follow: { is_following } }) => 20 | this.setState({ is_following }) 21 | 22 | follow = e => { 23 | e.preventDefault() 24 | let { dispatch, user: { user_details: { id, username } } } = this.props 25 | fn.follow({ 26 | user: id, 27 | username, 28 | dispatch, 29 | update_followers: true, 30 | done: () => this.setState({ is_following: true }) 31 | }) 32 | } 33 | 34 | unfollow = e => { 35 | e.preventDefault() 36 | let { dispatch, user: { user_details: { id } } } = this.props 37 | fn.unfollow({ 38 | user: id, 39 | dispatch, 40 | update_followers: true, 41 | done: () => this.setState({ is_following: false }) 42 | }) 43 | } 44 | 45 | toNotes = () => $('html, body').animate({ scrollTop: 390 }, 450) 46 | 47 | render(){ 48 | let 49 | { url, 50 | user: { user_details }, 51 | notes, 52 | follow: { profile_views, followers, followings} 53 | } = this.props, 54 | { is_following } = this.state, 55 | s_username = $('.data').data('username') 56 | 57 | return( 58 | <div> 59 | <div class='user_banner'> 60 | 61 | <div className="profile_img_div"> 62 | <img src={ user_details.id ? `/users/${user_details.id}/user.jpg` : '/images/spacecraft.jpg' } alt="Your profile"/> 63 | </div> 64 | 65 | <div className="user_buttons"> 66 | { 67 | fn.Me(user_details.id) ? 68 | <Link 69 | to={{ 70 | pathname: `/profile/${s_username}/create-note`, 71 | state: { modal: true } 72 | }} 73 | className={`create_note_btn pri_btn ${!fn.e_v() ? "a_disabled" : ""}`} 74 | >{fn.e_v() ? "Create note" : "Verify email to create note"}</Link> 75 | : 76 | is_following ? 77 | <a href="#" className="unfollow pri_btn" onClick={this.unfollow} >Unfollow</a> 78 | : 79 | <a href="#" className="follow pri_btn" onClick={this.follow} >Follow</a> 80 | } 81 | </div> 82 | 83 | <div className="user_info"> 84 | <Link to='#' className="user_main_link">{user_details.username}</Link> 85 | <span className="user_no_notes">{user_details.email}</span> 86 | <div className={`user_bio ${!user_details.bio ? 'no_bio' : null}`}> 87 | { 88 | user_details.bio ? 89 | <span>{user_details.bio}</span> 90 | : 91 | fn.Me(user_details.id) ? 92 | <span>You have no bio!!</span> 93 | : 94 | <span>{`${user_details.username} has no bio!!`}</span> 95 | } 96 | </div> 97 | <hr /> 98 | <div className="user_stats"> 99 | <div class="stat_post" onClick={this.toNotes} > 100 | <span class="stat_hg">{notes.length}</span> 101 | <span class="stat_nhg">Notes</span> 102 | </div> 103 | <Link to={`${url}/followers`} class="stat_followers" > 104 | <span class="stat_hg">{followers.length}</span> 105 | <span class="stat_nhg">Followers</span> 106 | </Link> 107 | <Link to={`${url}/followings`} class="stat_followings"> 108 | <span class="stat_hg">{followings.length}</span> 109 | <span class="stat_nhg">Followings</span> 110 | </Link> 111 | <div class="stat_views stat_disabled "> 112 | <span class="stat_hg">{profile_views}</span> 113 | <span class="stat_nhg">Profile views</span> 114 | </div> 115 | </div> 116 | </div> 117 | 118 | </div> 119 | 120 | </div> 121 | ) 122 | 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /public/styles/dist/perfect-scrollbar.css: -------------------------------------------------------------------------------- 1 | /* perfect-scrollbar v0.6.10 */ 2 | .ps-container { 3 | -ms-touch-action: none; 4 | touch-action: none; 5 | overflow: hidden !important; 6 | -ms-overflow-style: none; 7 | } 8 | @supports (-ms-overflow-style: none) { 9 | .ps-container { 10 | overflow: auto !important; 11 | } 12 | } 13 | @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { 14 | .ps-container { 15 | overflow: auto !important; 16 | } 17 | } 18 | .ps-container.ps-active-x > .ps-scrollbar-x-rail, 19 | .ps-container.ps-active-y > .ps-scrollbar-y-rail { 20 | display: block; 21 | background-color: transparent; 22 | } 23 | .ps-container.ps-in-scrolling { 24 | pointer-events: none; 25 | } 26 | .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { 27 | background-color: #eee; 28 | opacity: 0.9; 29 | } 30 | .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { 31 | background-color: #999; 32 | } 33 | .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { 34 | background-color: #eee; 35 | opacity: 0.9; 36 | } 37 | .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { 38 | background-color: #999; 39 | } 40 | .ps-container > .ps-scrollbar-x-rail { 41 | display: none; 42 | position: absolute; 43 | /* please don't change 'position' */ 44 | -webkit-border-radius: 4px; 45 | -moz-border-radius: 4px; 46 | border-radius: 4px; 47 | opacity: 0; 48 | -webkit-transition: background-color 0.2s linear, opacity 0.2s linear; 49 | -moz-transition: background-color 0.2s linear, opacity 0.2s linear; 50 | -o-transition: background-color 0.2s linear, opacity 0.2s linear; 51 | transition: background-color 0.2s linear, opacity 0.2s linear; 52 | bottom: 3px; 53 | /* there must be 'bottom' for ps-scrollbar-x-rail */ 54 | height: 8px; 55 | } 56 | .ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x { 57 | position: absolute; 58 | /* please don't change 'position' */ 59 | background-color: #aaa; 60 | -webkit-border-radius: 4px; 61 | -moz-border-radius: 4px; 62 | border-radius: 4px; 63 | -webkit-transition: background-color 0.2s linear; 64 | -moz-transition: background-color 0.2s linear; 65 | -o-transition: background-color 0.2s linear; 66 | transition: background-color 0.2s linear; 67 | bottom: 0; 68 | /* there must be 'bottom' for ps-scrollbar-x */ 69 | height: 8px; 70 | } 71 | .ps-container > .ps-scrollbar-y-rail { 72 | cursor: pointer; 73 | display: none; 74 | position: absolute; 75 | /* please don't change 'position' */ 76 | -webkit-border-radius: 4px; 77 | -moz-border-radius: 4px; 78 | border-radius: 4px; 79 | opacity: 0; 80 | -webkit-transition: background-color 0.2s linear, opacity 0.2s linear; 81 | -moz-transition: background-color 0.2s linear, opacity 0.2s linear; 82 | -o-transition: background-color 0.2s linear, opacity 0.2s linear; 83 | transition: background-color 0.2s linear, opacity 0.2s linear; 84 | right: 3px; 85 | /* there must be 'right' for ps-scrollbar-y-rail */ 86 | width: 8px; 87 | } 88 | .ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y { 89 | position: absolute; 90 | /* please don't change 'position' */ 91 | background-color: #aaa; 92 | -webkit-border-radius: 4px; 93 | -moz-border-radius: 4px; 94 | border-radius: 4px; 95 | -webkit-transition: background-color 0.2s linear; 96 | -moz-transition: background-color 0.2s linear; 97 | -o-transition: background-color 0.2s linear; 98 | transition: background-color 0.2s linear; 99 | right: 0; 100 | /* there must be 'right' for ps-scrollbar-y */ 101 | width: 8px; 102 | } 103 | .ps-container:hover.ps-in-scrolling { 104 | pointer-events: none; 105 | } 106 | .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { 107 | background-color: #eee; 108 | opacity: 0.9; 109 | } 110 | .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { 111 | background-color: #999; 112 | } 113 | .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { 114 | background-color: #eee; 115 | opacity: 0.9; 116 | } 117 | .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { 118 | background-color: #999; 119 | } 120 | .ps-container:hover > .ps-scrollbar-x-rail, 121 | .ps-container:hover > .ps-scrollbar-y-rail { 122 | opacity: 0.6; 123 | } 124 | .ps-container:hover > .ps-scrollbar-x-rail:hover { 125 | background-color: #eee; 126 | opacity: 0.9; 127 | } 128 | .ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x { 129 | background-color: #999; 130 | } 131 | .ps-container:hover > .ps-scrollbar-y-rail:hover { 132 | background-color: #eee; 133 | opacity: 0.9; 134 | } 135 | .ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y { 136 | background-color: #999; 137 | } 138 | -------------------------------------------------------------------------------- /public/js/src/components/edit/edit-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import axios from 'axios' 4 | import { connect } from 'react-redux' 5 | import Title from '../others/title-comp' 6 | import { FadeIn } from 'animate-components' 7 | import Notify from 'handy-notification' 8 | import TimeAgo from 'handy-timeago' 9 | import P from 'bluebird' 10 | import * as fn from '../../functions/functions' 11 | import * as user_action from '../../actions/user-action' 12 | 13 | @connect(store => { 14 | return { 15 | user: store.user 16 | } 17 | }) 18 | 19 | export default class Edit extends React.Component{ 20 | 21 | state = { 22 | username: "", 23 | email: "", 24 | bio: "", 25 | file: "" 26 | } 27 | 28 | componentDidMount = () => { 29 | let 30 | { dispatch } = this.props, 31 | username = $('.data').data('username') 32 | dispatch(user_action.user_details(username)) 33 | } 34 | 35 | componentWillReceiveProps = ({ user: { user_details: { username, email, bio } }}) => 36 | this.setState({ username, email, bio }) 37 | 38 | update_ = (e, what) => { 39 | this.setState({ [what]: e.target.value }) 40 | } 41 | 42 | edit_profile = e => { 43 | e.preventDefault() 44 | let 45 | { username: susername, email: semail } = this.props.user.user_details, 46 | { username, email, bio } = this.state 47 | fn.edit_profile({ susername, semail, username, email, bio }) 48 | } 49 | 50 | change_avatar = e => { 51 | this.update_(e, "file") 52 | fn.change_avatar({ file: e.target.files[0] }) 53 | } 54 | 55 | resend_vl = e => { 56 | e.preventDefault() 57 | fn.resend_vl() 58 | } 59 | 60 | render(){ 61 | let 62 | { username, email, bio, file } = this.state, 63 | { id, joined } = this.props.user.user_details 64 | 65 | return( 66 | <div class='edit'> 67 | 68 | <Title value="Edit profile" /> 69 | 70 | <FadeIn duration="300ms" className="edit_animation" > 71 | <div class="edit_info"> 72 | <img 73 | className="edit_img" 74 | src={id ? `/users/${id}/user.jpg` : "/images/spacecraft.jpg" } 75 | alt="Your avatar" 76 | /> 77 | <span>{`@${username}`}</span> 78 | </div> 79 | <div className="eu_div"> 80 | <span class='edit_span'>Username</span> 81 | <input 82 | type="text" 83 | class='e_username' 84 | placeholder='Username..' 85 | autoComplete='false' 86 | autoFocus 87 | spellCheck='false' 88 | value={username} 89 | onChange={e => this.update_(e, "username")} 90 | /> 91 | </div> 92 | <div className="ee_div"> 93 | <span class='edit_span'>Email</span> 94 | <input 95 | type="email" 96 | class='e_email' 97 | placeholder='Email..' 98 | autoComplete='false' 99 | spellCheck='false' 100 | value={email} 101 | onChange={e => this.update_(e, "email")} 102 | /> 103 | </div> 104 | <div className="eb_div"> 105 | <span class='edit_span'>Bio</span> 106 | <textarea 107 | class="e_bio" 108 | placeholder='Bio..' 109 | spellCheck='false' 110 | value={bio} 111 | onChange={e => this.update_(e, "bio")} 112 | ></textarea> 113 | </div> 114 | <div className="eb_btns"> 115 | <form class='avatar_form' method="post" encType='multipart/formdata' > 116 | <input 117 | type="file" 118 | name="avatar" 119 | id="avatar_file" 120 | accept="image/*" 121 | value={file} 122 | onChange={this.change_avatar} 123 | /> 124 | <label 125 | for="avatar_file" 126 | class={`avatar_span sec_btn ${!fn.e_v() ? "sec_btn_disabled" : ""}`} 127 | >{fn.e_v() ? "Change avatar" : "Verify email to change avatar"}</label> 128 | </form> 129 | <a href="#" className="pri_btn e_done" onClick={this.edit_profile} >Done editing</a> 130 | </div> 131 | <div className="e_joined"> 132 | <span>{`You joined Notes App ${TimeAgo(joined)}`}</span> 133 | </div> 134 | 135 | { 136 | !fn.e_v() ? 137 | <div className="resend_vl_div" > 138 | <a href='#' className="pri_btn resend_vl" onClick={this.resend_vl} >Resend verification link</a> 139 | </div> 140 | : null 141 | } 142 | 143 | </FadeIn> 144 | 145 | </div> 146 | ) 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /routes/edit-routes.js: -------------------------------------------------------------------------------- 1 | const 2 | app = require('express').Router(), 3 | P = require('bluebird'), 4 | root = process.cwd(), 5 | db = require('../models/db'), 6 | mail = require('../models/mail'), 7 | upload = require('multer')({ 8 | dest: `${root}/public/temp/` 9 | }), 10 | fs = require('fs'), 11 | { promisify } = require('util'), 12 | { ProcessImage, DeleteAllOfFolder } = require('handy-image-processor') 13 | 14 | // FOR GETTING THE COUNT OF GIVEN FIELD 15 | app.post('/what-exists', (req, res) => { 16 | let { what, value } = req.body 17 | db.query(`SELECT COUNT(${what}) AS count FROM users WHERE ${what}=?`, [ value ]) 18 | .then(s => res.json(s[0].count)) 19 | .catch(e => res.json(e)) 20 | }) 21 | 22 | // FOR EDTING PROFILE 23 | app.post('/edit-profile', (req, res) => { 24 | P.coroutine(function* () { 25 | let 26 | { username, email, bio } = req.body, 27 | { id: session } = req.session 28 | 29 | req.checkBody('username', 'Username is empty').notEmpty() 30 | req.checkBody('username', 'Username must contain only leters').isAlpha() 31 | req.checkBody('username', 'Username must be greater than 4').isLength({ min: 4 }) 32 | req.checkBody('username', 'Username must be less than 32').isLength({ max: 32 }) 33 | 34 | req.checkBody('email', 'Email is empty').notEmpty() 35 | req.checkBody('email', 'Email is invalid').isEmail() 36 | 37 | let errors = yield req.getValidationResult() 38 | 39 | if (!errors.isEmpty()) { 40 | let array = [] 41 | errors.array().forEach(item => array.push(item.msg)) 42 | res.json({ mssg: array }) 43 | } else { 44 | 45 | req.session.username = username 46 | 47 | yield db.query('UPDATE users SET username=?, email=?, bio=? WHERE id=?', [username, email, bio, session]), 48 | yield db.query('UPDATE notes SET username=? WHERE user=?', [username, session]) 49 | yield db.query('UPDATE profile_views SET view_by_username = ? WHERE view_by=?', [username, session]), 50 | yield db.query('UPDATE follow_system SET follow_by_username = ? WHERE follow_by=?', [username, session]), 51 | yield db.query('UPDATE follow_system SET follow_to_username = ? WHERE follow_to=?', [username, session]) 52 | yield db.query('UPDATE likes SET like_by_username = ? WHERE like_by = ?', [username, session]) 53 | 54 | res.json({ 55 | mssg: 'Profile edited!!', 56 | success: true 57 | }) 58 | 59 | } 60 | 61 | })() 62 | }) 63 | 64 | // FOR CHANGING AVATAR 65 | app.post('/change-avatar', upload.single('avatar'), (req, res) => { 66 | P.coroutine(function* () { 67 | let obj = { 68 | srcFile: req.file.path, 69 | width: 200, 70 | height: 200, 71 | destFile: `${root}/public/users/${req.session.id}/user.jpg` 72 | } 73 | 74 | yield ProcessImage(obj) 75 | yield DeleteAllOfFolder(`${root}/public/temp/`) 76 | 77 | res.json({ mssg: "Avatar changed!" }) 78 | })() 79 | }) 80 | 81 | // FOR RESENDING THE VERIFICATION LINK 82 | app.post('/resend_vl', (req, res) => { 83 | P.coroutine(function* () { 84 | let 85 | { id } = req.session, 86 | e_q = yield db.query("SELECT email FROM users WHERE id=?", [ id ]), 87 | [{ email }] = e_q, 88 | url = `http://localhost:${process.env.PORT}/deep/most/topmost/activate/${id}`, 89 | options = { 90 | to: email, 91 | subject: "Activate your Notes App account", 92 | html: `<span>Hello, You received this message because you created an account on Notes App.<span><br><span>Click on button below to activate your account and explore.</span><br><br><a href='${url}' style='border: 1px solid #1b9be9; font-weight: 600; color: #fff; border-radius: 3px; cursor: pointer; outline: none; background: #1b9be9; padding: 4px 15px; display: inline-block; text-decoration: none;'>Activate</a>` 93 | } 94 | yield mail(options) 95 | res.json({ mssg: "Verification link sent to your email!~" }) 96 | })() 97 | }) 98 | 99 | app.post('/deactivate', (req, res) => { 100 | P.coroutine(function* () { 101 | let 102 | { id, username } = req.session, 103 | rmdir = promisify(fs.rmdir) 104 | 105 | yield db.query('DELETE FROM profile_views WHERE view_by=?', [id]) 106 | yield db.query('DELETE FROM profile_views WHERE view_to=?', [id]) 107 | yield db.query('DELETE FROM follow_system WHERE follow_by=?', [id]) 108 | yield db.query('DELETE FROM follow_system WHERE follow_to=?', [id]) 109 | yield db.query('DELETE FROM likes WHERE like_by=?', [id]) 110 | let notes = yield db.query('SELECT note_id FROM notes WHERE user=?', [id]) 111 | notes.map(n => db.query('DELETE FROM likes WHERE note_id=?', [n.note_id])) 112 | yield db.query('DELETE FROM notes WHERE user=?', [id]) 113 | yield db.query('DELETE FROM users WHERE id=?', [id]) 114 | 115 | yield DeleteAllOfFolder(`${root}/public/users/${id}/`) 116 | yield rmdir(`${root}/public/users/${id}/`) 117 | 118 | req.session.id = null 119 | 120 | res.json({ success: true }) 121 | 122 | })().catch(e => console.log(e.stack)) 123 | 124 | }) 125 | 126 | module.exports = app 127 | -------------------------------------------------------------------------------- /models/userFn.js: -------------------------------------------------------------------------------- 1 | const 2 | db = require('../models/db'), 3 | mail = require('../models/mail'), 4 | hl = require('handy-log'), 5 | P = require('bluebird'), 6 | fs = require('fs'), 7 | { promisify } = require('util'), 8 | path = require('path'), 9 | dir = process.cwd() 10 | 11 | const signup = (req, res) => { 12 | let { 13 | body: { username, email, password, password_again }, 14 | session 15 | } = req 16 | 17 | req.checkBody('username', 'Username is empty').notEmpty() 18 | req.checkBody('username', 'Username must contain only leters').isAlpha() 19 | req.checkBody('username', 'Username must be greater than 4').isLength({ min: 4 }) 20 | req.checkBody('username', 'Username must be less than 32').isLength({ max: 32 }) 21 | 22 | req.checkBody('email', 'Email is empty').notEmpty() 23 | req.checkBody('email', 'Email is invalid').isEmail() 24 | 25 | req.checkBody('password', 'Password field is empty').notEmpty() 26 | req.checkBody('password_again', 'Password field is empty').notEmpty() 27 | req.checkBody('password', 'Passwords don\'t match').equals(password_again) 28 | 29 | P.coroutine(function *(){ 30 | 31 | let errors = yield req.getValidationResult() 32 | 33 | if(!errors.isEmpty()){ 34 | let array = [] 35 | errors.array().forEach(item => array.push(item.msg) ) 36 | res.json({ mssg: array }) 37 | } else { 38 | 39 | let 40 | [{ usernameCount }] = yield db.query('SELECT COUNT(*) as usernameCount from users WHERE username = ?', [username]), 41 | [{ emailCount }] = yield db.query('SELECT COUNT(*) as emailCount FROM users WHERE email = ?', [email]) 42 | 43 | if(usernameCount == 1){ 44 | res.json({ mssg: "Username already exists!" }) 45 | } else if (emailCount == 1) { 46 | res.json({ mssg: "Email already exists!" }) 47 | } else { 48 | 49 | let 50 | newUser = { 51 | username, 52 | email: req.body.email, 53 | password, 54 | email_verified: "no", 55 | joined: new Date().getTime() 56 | }, 57 | { affectedRows, insertId } = yield db.createUser(newUser) 58 | 59 | if (affectedRows == 1) { 60 | 61 | let mkdir = promisify(fs.mkdir) 62 | yield mkdir(dir + `/public/users/${insertId}`) 63 | fs 64 | .createReadStream(dir + '/public/images/spacecraft.jpg') 65 | .pipe(fs.createWriteStream(dir + `/public/users/${insertId}/user.jpg`)) 66 | 67 | let 68 | url = `http://localhost:${process.env.PORT}/deep/most/topmost/activate/${insertId}`, 69 | options = { 70 | to: email, 71 | subject: "Activate your Notes App account", 72 | html: `<span>Hello, You received this message because you created an account on Notes App.<span><br><span>Click on button below to activate your account and explore.</span><br><br><a href='${url}' style='border: 1px solid #1b9be9; font-weight: 600; color: #fff; border-radius: 3px; cursor: pointer; outline: none; background: #1b9be9; padding: 4px 15px; display: inline-block; text-decoration: none;'>Activate</a>` 73 | } 74 | 75 | mail(options) 76 | .then(m => { 77 | hl.success(m) 78 | session.id = insertId 79 | session.username = username 80 | session.email_verified = "no" 81 | res.json({ 82 | mssg: `Hello, ${session.username}!!`, 83 | success: true 84 | }) 85 | }) 86 | .catch(me => { 87 | hl.error(me) 88 | res.json({ 89 | mssg: `Hello, ${session.username}. Mail could not be sent!!`, 90 | success: true 91 | }) 92 | }) 93 | 94 | } 95 | 96 | } 97 | 98 | } 99 | 100 | })() 101 | 102 | } 103 | 104 | const login = (req, res) => { 105 | P.coroutine(function* (){ 106 | let { 107 | body: { username: rusername, password: rpassword }, 108 | session 109 | } = req 110 | 111 | req.checkBody('username', 'Username is empty').notEmpty() 112 | req.checkBody('password', 'Password field is empty').notEmpty() 113 | 114 | let errors = yield req.getValidationResult() 115 | 116 | if(!errors.isEmpty()){ 117 | let array = [] 118 | errors.array().forEach(item => array.push(item.msg) ) 119 | res.json({ mssg: array }) 120 | } else { 121 | 122 | let [{ userCount, id, password, email_verified }] = yield db.query('SELECT COUNT(id) as userCount, id, password, email_verified from users WHERE username = ? LIMIT 1', [rusername]) 123 | 124 | if(userCount == 0){ 125 | res.json({ mssg: "User not found!" }) 126 | } else if(userCount > 0) { 127 | let same = yield db.comparePassword(rpassword, password) 128 | if(!same){ 129 | res.json({ mssg: "Wrong password!" }) 130 | } else { 131 | session.id = id 132 | session.username = rusername 133 | session.email_verified = email_verified 134 | 135 | res.json({ mssg: `Hello, ${session.username}!!`, success: true }) 136 | } 137 | } 138 | 139 | } 140 | 141 | })() 142 | } 143 | 144 | const registered = (req, res) => { 145 | P.coroutine(function *(){ 146 | let 147 | { id } = req.session, 148 | [{ email_verified }] = yield db.query("SELECT email_verified FROM users WHERE id=? LIMIT 1", [id]), 149 | options = { 150 | title: "You are now registered!!", 151 | mssg: "Email has been sent. Check your inbox and click on the provided link!!" 152 | } 153 | 154 | email_verified == "yes" ? 155 | res.redirect('/') 156 | : 157 | res.render("registered", { options }) 158 | 159 | })() 160 | } 161 | 162 | const activate = (req, res) => { 163 | P.coroutine(function *(){ 164 | let 165 | { params: { id }, session } = req, 166 | { changedRows } = yield db.query('UPDATE users SET email_verified=? WHERE id=?', ["yes", id]), 167 | mssg 168 | 169 | session.email_verified = "yes" 170 | mssg = changedRows == 0 ? "alr" : "yes" 171 | 172 | res.redirect(`/email-verification/${mssg}`) 173 | 174 | })() 175 | } 176 | 177 | module.exports = { 178 | signup, 179 | login, 180 | registered, 181 | activate, 182 | } 183 | -------------------------------------------------------------------------------- /public/js/src/components/note/view-note-comp.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import $ from 'jquery' 3 | import axios from 'axios' 4 | import { connect } from 'react-redux' 5 | import Title from '../others/title-comp' 6 | import { FadeIn } from 'animate-components' 7 | import { Link, Route, Redirect } from 'react-router-dom' 8 | import Notify from 'handy-notification' 9 | import Tooltip from 'handy-tooltip' 10 | import Timeago from 'handy-timeago' 11 | 12 | import Goto from '../others/goto-comp' 13 | import Overlay from '../others/overlay-comp' 14 | import Prompt from '../others/prompt-comp' 15 | import Likes from '../note/likes-comp' 16 | 17 | import * as fn from '../../functions/functions' 18 | import * as note_int_action from '../../actions/note-int-action' 19 | 20 | @connect(store => { 21 | return { 22 | note_int: store.note_int 23 | } 24 | }) 25 | 26 | export default class View_note extends React.Component{ 27 | 28 | state = { 29 | deleting: false, 30 | editing: false, 31 | liked: false, 32 | invalid_note: false 33 | } 34 | 35 | componentDidMount = async () => { 36 | let { match: { params: { note } }, dispatch } = this.props 37 | let { data } = await axios.post('/api/liked-or-not', { note }) 38 | this.setState({ liked: data }) 39 | dispatch(note_int_action.note_details(note)) 40 | dispatch(note_int_action.likes(note)) 41 | } 42 | 43 | componentWillReceiveProps = ({ note_int: { note_details: { note_id } } }) => 44 | !note_id ? this.setState({ invalid_note: true }) : null 45 | 46 | toggle_ = (e, what) => { 47 | e ? e.preventDefault() : null 48 | this.setState({ [what]: !this.state[what] }) 49 | what == 'editing' ? $('.v_n_edit').blur() : null 50 | } 51 | 52 | back = e => fn.back(e, this.props.history) 53 | 54 | delete = e => { 55 | e.preventDefault() 56 | let { dispatch, history, match: { params: { note } } } = this.props 57 | fn.deleteNote({ note, dispatch, history }) 58 | } 59 | 60 | edit = e => { 61 | this.toggle_(e, "editing") 62 | let 63 | title = $('.v_n_title').text(), 64 | content = $('.v_n_content').text(), 65 | { dispatch, note_int: { note_details: { note_id } } } = this.props 66 | fn.editNote({ title, content, note_id, setState: this.setState, dispatch }) 67 | } 68 | 69 | like = e => { 70 | let 71 | { dispatch, match: { params: { note } } } = this.props, 72 | options = { note, dispatch, done: () => this.setState({ liked: true }) } 73 | fn.like(options) 74 | } 75 | 76 | unlike = e => { 77 | let 78 | { dispatch, match: { params: { note } } } = this.props, 79 | options = { note, dispatch, done: () => this.setState({ liked: false }) } 80 | fn.unlike(options) 81 | } 82 | 83 | render(){ 84 | let 85 | { deleting, editing, liked, invalid_note } = this.state, 86 | { note_int: { likes, note_details: { user, username, title, content, note_time } }, match } = this.props 87 | 88 | Tooltip({ 89 | selector: $('.like_unlike'), 90 | value: liked ? "Unlike" : "Like" 91 | }) 92 | 93 | return ( 94 | <div class='view_note modal'> 95 | 96 | { invalid_note ? <Redirect to="/error/note_notfound" /> : null } 97 | 98 | <Title value="View note" /> 99 | 100 | <FadeIn duration="300ms" > 101 | <div className="v_n_header modal_header"> 102 | <span class='title' >View note</span> 103 | <Goto /> 104 | </div> 105 | <div className="v_n_middle modal_middle"> 106 | <div className="v_n_info"> 107 | <img src={user ? `/users/${user}/user.jpg` : '/images/spacecraft.jpg'} alt="" /> 108 | <div className="v_n_left"> 109 | <Link to={`/profile/${username}`} className='v_n_username' >{username}</Link> 110 | <span className="v_n_time">{Timeago(note_time)}</span> 111 | </div> 112 | </div> 113 | <span 114 | className='v_n_title' 115 | contentEditable={editing} 116 | spellCheck='false' 117 | suppressContentEditableWarning={true} 118 | >{title}</span> 119 | <span 120 | className={`v_n_content ${editing ? 'content_editor' : ''} `} 121 | contentEditable={editing} 122 | spellCheck='false' 123 | suppressContentEditableWarning={true} 124 | >{content}</span> 125 | </div> 126 | <div className="v_n_bottom modal_bottom"> 127 | <div className="v_n_int"> 128 | { 129 | liked ? 130 | <span 131 | className={`v_n_unlike like_unlike ${editing ? 'like_unlike_disabled' : ''}`} 132 | onClick={this.unlike} 133 | ><i class="material-icons">favorite</i></span> 134 | : 135 | <span 136 | className={`v_n_like like_unlike ${editing ? 'like_unlike_disabled' : ''}`} 137 | onClick={this.like} 138 | ><i class="material-icons">favorite_border</i></span> 139 | } 140 | <Link 141 | to={`${match.url}/likes`} 142 | className={`v_n_likes sec_btn ${editing ? 'sec_btn_disabled' : ''}`} 143 | >{`${likes.length} likes`}</Link> 144 | </div> 145 | 146 | { 147 | fn.Me(user) ? 148 | editing ? 149 | <a 150 | href="#" 151 | className="v_n_edit sec_btn" 152 | onClick={this.edit} 153 | >Done editing</a> 154 | : 155 | <a 156 | href="#" 157 | className="v_n_edit sec_btn" 158 | onClick={e => this.toggle_(e, "editing")} 159 | >Edit note</a> 160 | : null 161 | } 162 | 163 | { 164 | fn.Me(user) ? 165 | <a 166 | href="#" 167 | className={`v_n_delete sec_btn ${editing ? 'sec_btn_disabled' : ''} `} 168 | onClick={e => this.toggle_(e, "deleting")} 169 | >Delete note</a> 170 | : null 171 | } 172 | 173 | <a 174 | href='#' 175 | className={`v_n_cancel pri_btn ${editing ? 'a_disabled' : ''} `} 176 | onClick={this.back} 177 | >Done</a> 178 | 179 | </div> 180 | </FadeIn> 181 | 182 | { (deleting) ? <Overlay type="white" /> : null } 183 | { 184 | deleting ? 185 | <Prompt 186 | title={"Delete note"} 187 | content={"This note will be deleted. There's no undo so you won't be able to find it."} 188 | actionText= "Delete" 189 | action={this.delete} 190 | state_updater="deleting" 191 | close={this.toggle_} 192 | /> 193 | : null 194 | } 195 | 196 | <Route path={`${match.url}/likes`} component={() => <Overlay type="white" /> } /> 197 | <Route path={`${match.url}/likes`} component={Likes} /> 198 | 199 | </div> 200 | ) 201 | 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /db.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.5.1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: 127.0.0.1 6 | -- Generation Time: Aug 16, 2017 at 01:35 PM 7 | -- Server version: 10.1.19-MariaDB 8 | -- PHP Version: 5.6.28 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8mb4 */; 18 | 19 | -- 20 | -- Database: `notesapp` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- Table structure for table `follow_system` 27 | -- 28 | 29 | CREATE TABLE `follow_system` ( 30 | `follow_id` int(11) NOT NULL, 31 | `follow_by` int(11) NOT NULL, 32 | `follow_by_username` varchar(32) NOT NULL, 33 | `follow_to` int(11) NOT NULL, 34 | `follow_to_username` varchar(32) NOT NULL, 35 | `follow_time` varchar(100) NOT NULL 36 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 37 | 38 | -- 39 | -- Dumping data for table `follow_system` 40 | -- 41 | 42 | INSERT INTO `follow_system` (`follow_id`, `follow_by`, `follow_by_username`, `follow_to`, `follow_to_username`, `follow_time`) VALUES 43 | (14, 8, 'coldplay', 6, 'faiyaz', '1501010150463'), 44 | (16, 8, 'coldplay', 5, 'takkar', '1501011954639'), 45 | (29, 7, 'ghalib', 5, 'takkar', '1501092849849'), 46 | (39, 5, 'takkar', 6, 'faiyaz', '1502801315146'), 47 | (43, 5, 'takkar', 7, 'ghalib', '1502801363666'); 48 | 49 | -- -------------------------------------------------------- 50 | 51 | -- 52 | -- Table structure for table `likes` 53 | -- 54 | 55 | CREATE TABLE `likes` ( 56 | `like_id` int(11) NOT NULL, 57 | `like_by` int(11) NOT NULL, 58 | `like_by_username` varchar(32) NOT NULL, 59 | `note_id` int(11) NOT NULL, 60 | `like_time` varchar(100) NOT NULL 61 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 62 | 63 | -- -------------------------------------------------------- 64 | 65 | -- 66 | -- Table structure for table `notes` 67 | -- 68 | 69 | CREATE TABLE `notes` ( 70 | `note_id` int(11) NOT NULL, 71 | `user` int(11) NOT NULL, 72 | `username` varchar(32) NOT NULL, 73 | `title` varchar(200) NOT NULL, 74 | `content` text NOT NULL, 75 | `note_time` varchar(200) NOT NULL 76 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 77 | 78 | -- 79 | -- Dumping data for table `notes` 80 | -- 81 | 82 | INSERT INTO `notes` (`note_id`, `user`, `username`, `title`, `content`, `note_time`) VALUES 83 | (61, 6, 'faiyaz', 'Untitled note', 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry''s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.', '1497022376821'), 84 | (70, 6, 'faiyaz', 'Miracle', 'For you!! and me', '1497043496669'), 85 | (73, 8, 'coldplay', 'coldplay', '.....', '1497279792645'), 86 | (74, 8, 'coldplay', 'k', 'k', '1497279939388'), 87 | (75, 7, 'ghalib', 'Note...', '???????', '1497357617034'), 88 | (100, 5, 'takkar', 'one', 'j', '1502732101700'), 89 | (101, 5, 'takkar', 'two', 'jj', '1502732106616'), 90 | (102, 5, 'takkar', 'three', 'jjkmkm', '1502732115644'), 91 | (103, 5, 'takkar', 'fourrrr', 'g', '1502732123371'), 92 | (132, 5, 'takkar', 'five', 'hj', '1502879932911'), 93 | (141, 5, 'takkar', 'six', 'h', '1502882974135'); 94 | 95 | -- -------------------------------------------------------- 96 | 97 | -- 98 | -- Table structure for table `profile_views` 99 | -- 100 | 101 | CREATE TABLE `profile_views` ( 102 | `view_id` int(11) NOT NULL, 103 | `view_by` int(11) NOT NULL, 104 | `view_by_username` varchar(32) NOT NULL, 105 | `view_to` int(11) NOT NULL, 106 | `view_time` varchar(100) NOT NULL 107 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 108 | 109 | -- 110 | -- Dumping data for table `profile_views` 111 | -- 112 | 113 | INSERT INTO `profile_views` (`view_id`, `view_by`, `view_by_username`, `view_to`, `view_time`) VALUES 114 | (32, 5, 'takkar', 6, '1500921835026'), 115 | (33, 5, 'takkar', 6, '1500922249480'), 116 | (34, 5, 'takkar', 7, '1500924838877'), 117 | (35, 5, 'takkar', 8, '1500924855217'), 118 | (36, 5, 'takkar', 6, '1500992772869'), 119 | (37, 5, 'takkar', 6, '1500992897841'), 120 | (38, 5, 'takkar', 6, '1500993317753'), 121 | (39, 5, 'takkar', 6, '1500993507892'), 122 | (40, 5, 'takkar', 6, '1500993649374'), 123 | (41, 5, 'takkar', 6, '1500993826237'), 124 | (42, 5, 'takkar', 6, '1500994322811'), 125 | (43, 5, 'takkar', 6, '1500994543604'), 126 | (44, 5, 'takkar', 6, '1500995117840'), 127 | (45, 5, 'takkar', 6, '1500995653209'), 128 | (46, 5, 'takkar', 6, '1500995874191'), 129 | (47, 5, 'takkar', 6, '1500996103844'), 130 | (48, 5, 'takkar', 6, '1500996236677'), 131 | (49, 5, 'takkar', 6, '1500996654489'), 132 | (50, 5, 'takkar', 6, '1500996960369'), 133 | (51, 5, 'takkar', 6, '1500997489661'), 134 | (52, 5, 'takkar', 6, '1500998036025'), 135 | (53, 5, 'takkar', 6, '1500998158919'), 136 | (54, 5, 'takkar', 6, '1500998352473'), 137 | (55, 5, 'takkar', 6, '1500998472529'), 138 | (56, 5, 'takkar', 6, '1500998757389'), 139 | (57, 5, 'takkar', 6, '1500999316680'), 140 | (58, 5, 'takkar', 6, '1500999632354'), 141 | (59, 5, 'takkar', 6, '1501000899577'), 142 | (60, 5, 'takkar', 6, '1501001027676'), 143 | (61, 5, 'takkar', 6, '1501001186939'), 144 | (62, 5, 'takkar', 6, '1501001347797'), 145 | (63, 5, 'takkar', 6, '1501002114586'), 146 | (64, 5, 'takkar', 6, '1501002397194'), 147 | (65, 5, 'takkar', 6, '1501002586114'), 148 | (66, 5, 'takkar', 6, '1501002963656'), 149 | (67, 5, 'takkar', 6, '1501003095544'), 150 | (68, 5, 'takkar', 6, '1501003321896'), 151 | (69, 5, 'takkar', 6, '1501003622145'), 152 | (70, 5, 'takkar', 6, '1501003988175'), 153 | (71, 5, 'takkar', 6, '1501004137852'), 154 | (72, 5, 'takkar', 6, '1501004427165'), 155 | (73, 5, 'takkar', 6, '1501004628136'), 156 | (74, 5, 'takkar', 6, '1501004997477'), 157 | (75, 5, 'takkar', 6, '1501005242285'), 158 | (76, 5, 'takkar', 6, '1501009977584'), 159 | (77, 8, 'faiyaz', 6, '1501010011470'), 160 | (78, 8, 'faiyaz', 6, '1501010143417'), 161 | (79, 8, 'faiyaz', 6, '1501010373553'), 162 | (80, 8, 'faiyaz', 6, '1501010572762'), 163 | (81, 8, 'faiyaz', 6, '1501010818531'), 164 | (82, 8, 'takkar', 5, '1501010836673'), 165 | (83, 8, 'faiyaz', 6, '1501010987216'), 166 | (84, 8, 'faiyaz', 6, '1501011297474'), 167 | (85, 8, 'faiyaz', 6, '1501011632772'), 168 | (86, 8, 'takkar', 5, '1501011651818'), 169 | (87, 8, 'faiyaz', 6, '1501011948005'), 170 | (88, 5, 'takkar', 6, '1501060667169'), 171 | (89, 5, 'takkar', 8, '1501062832435'), 172 | (90, 5, 'takkar', 7, '1501063708764'), 173 | (91, 5, 'takkar', 6, '1501078211644'), 174 | (92, 5, 'takkar', 7, '1501078221725'), 175 | (93, 5, 'takkar', 8, '1501078229858'), 176 | (94, 5, 'takkar', 8, '1501078712816'), 177 | (95, 5, 'takkar', 6, '1501078887815'), 178 | (96, 5, 'takkar', 7, '1501078891603'), 179 | (97, 5, 'takkar', 8, '1501078894745'), 180 | (98, 9, 'ghalib', 7, '1501230682653'), 181 | (99, 5, 'takkar', 7, '1501230762107'), 182 | (100, 5, 'takkar', 8, '1501253259906'), 183 | (101, 5, 'takkar', 7, '1501254287942'), 184 | (102, 5, 'takkar', 7, '1501254506340'), 185 | (103, 5, 'faiyaz', 6, '1501268109623'), 186 | (104, 5, 'faiyaz', 6, '1501268215897'), 187 | (105, 7, 'takkar', 5, '1501513737354'), 188 | (106, 5, 'ghalib', 7, '1501515252540'), 189 | (107, 5, 'faiyaz', 6, '1502451381587'), 190 | (108, 5, 'faiyaz', 6, '1502451550839'), 191 | (109, 5, 'faiyaz', 6, '1502451732859'), 192 | (110, 5, 'ghalib', 7, '1502800636947'), 193 | (111, 5, 'faiyaz', 6, '1502819156273'), 194 | (112, 5, 'faiyaz', 6, '1502880013682'); 195 | 196 | -- -------------------------------------------------------- 197 | 198 | -- 199 | -- Table structure for table `users` 200 | -- 201 | 202 | CREATE TABLE `users` ( 203 | `id` int(11) NOT NULL, 204 | `username` varchar(32) NOT NULL, 205 | `email` varchar(500) NOT NULL, 206 | `password` varchar(500) NOT NULL, 207 | `bio` varchar(500) NOT NULL, 208 | `email_verified` enum('yes','no') NOT NULL DEFAULT 'no', 209 | `joined` varchar(100) NOT NULL 210 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 211 | 212 | -- 213 | -- Dumping data for table `users` 214 | -- 215 | 216 | INSERT INTO `users` (`id`, `username`, `email`, `password`, `bio`, `email_verified`, `joined`) VALUES 217 | (5, 'takkar', 'www.shtakkar@gmail.com', '$2a$10$sdy1uFQqpBtk8QbAQ61YqehQ73gksod9G8XbA3fqv7lcPEkffgDla', 'Developer of Instagram.', 'yes', '1497128558168'), 218 | (6, 'faiyaz', 'faiyaz@gmail.com', '$2a$10$HK.w9QLoBkkp2Tfx12FxKuaJWj2BkBPCR17xZKfk3sJFIVWfj7hma', 'Hello world!!', 'yes', '1497128554668'), 219 | (7, 'ghalib', 'ghalib@gmail.com', '$2a$10$S22pBWFlb1t1ZZnNr1pAFOVYBmbo7t.dNdD9JfB0sPsec87sEVsi.', '', 'yes', '1497128558668'), 220 | (8, 'coldplay', 'coldplay@gmail.com', '$2a$10$vSjEiBgSckcyBjZCV1AdV.x7e4n5jMte3wBlUWAH1GIEtVMfWlGdW', '', 'yes', '1497279682801'); 221 | 222 | -- 223 | -- Indexes for dumped tables 224 | -- 225 | 226 | -- 227 | -- Indexes for table `follow_system` 228 | -- 229 | ALTER TABLE `follow_system` 230 | ADD PRIMARY KEY (`follow_id`); 231 | 232 | -- 233 | -- Indexes for table `likes` 234 | -- 235 | ALTER TABLE `likes` 236 | ADD PRIMARY KEY (`like_id`); 237 | 238 | -- 239 | -- Indexes for table `notes` 240 | -- 241 | ALTER TABLE `notes` 242 | ADD PRIMARY KEY (`note_id`); 243 | 244 | -- 245 | -- Indexes for table `profile_views` 246 | -- 247 | ALTER TABLE `profile_views` 248 | ADD PRIMARY KEY (`view_id`); 249 | 250 | -- 251 | -- Indexes for table `users` 252 | -- 253 | ALTER TABLE `users` 254 | ADD PRIMARY KEY (`id`); 255 | 256 | -- 257 | -- AUTO_INCREMENT for dumped tables 258 | -- 259 | 260 | -- 261 | -- AUTO_INCREMENT for table `follow_system` 262 | -- 263 | ALTER TABLE `follow_system` 264 | MODIFY `follow_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=44; 265 | -- 266 | -- AUTO_INCREMENT for table `likes` 267 | -- 268 | ALTER TABLE `likes` 269 | MODIFY `like_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=105; 270 | -- 271 | -- AUTO_INCREMENT for table `notes` 272 | -- 273 | ALTER TABLE `notes` 274 | MODIFY `note_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=147; 275 | -- 276 | -- AUTO_INCREMENT for table `profile_views` 277 | -- 278 | ALTER TABLE `profile_views` 279 | MODIFY `view_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=113; 280 | -- 281 | -- AUTO_INCREMENT for table `users` 282 | -- 283 | ALTER TABLE `users` 284 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9; 285 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 286 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 287 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 288 | -------------------------------------------------------------------------------- /public/js/src/functions/functions.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import { post } from 'axios' 3 | import Notify from 'handy-notification' 4 | import P from 'bluebird' 5 | 6 | import * as notes_action from '../actions/notes-action' 7 | import * as user_action from '../actions/user-action' 8 | import * as follow_action from '../actions/follow-action' 9 | import * as note_int_action from '../actions/note-int-action' 10 | 11 | // FUNCTION FOR SHORTENING 12 | const shortener = (elem, length) => { 13 | let 14 | parse = parseInt(length), 15 | len = elem.length 16 | if (!parse) { return; } 17 | return (len >= parse) ? `${elem.substr(0, length-2)}..` : (len < parse) ? elem : null 18 | } 19 | 20 | // FUNCTION TO TOGGLE 21 | const toggle = el => { 22 | let style = el.style.display 23 | style === "none" ? el.style.display = "block" : el.style.display = "none" 24 | } 25 | 26 | // FUNCTION FOR COMMON LOGIN 27 | const commonLogin = options => { 28 | let 29 | { data, btn, url, redirect, defBtnValue } = options, 30 | overlay2 = $('.overlay-2') 31 | 32 | btn 33 | .attr('value', 'Please wait..') 34 | .addClass('a_disabled') 35 | overlay2.show() 36 | 37 | post(url, data) 38 | .then(s => { 39 | let { data: { mssg, success } } = s 40 | if (success) { 41 | Notify({ 42 | value: mssg, 43 | done: () => location.href = redirect 44 | }) 45 | btn.attr('value', 'Redirecting..') 46 | overlay2.show() 47 | } else { 48 | Notify({ value: mssg }) 49 | btn 50 | .attr('value', defBtnValue) 51 | .removeClass('a_disabled') 52 | overlay2.hide() 53 | } 54 | btn.blur() 55 | }) 56 | .catch(e => console.log(e)) 57 | 58 | } 59 | 60 | // FUNCTION TO CAPITALIZE FIRST LETTER OF A WORD 61 | const c_first = str => 62 | str.charAt(0).toUpperCase()+str.substr(1) 63 | 64 | // FUNCTION TO CHECK WHETHER ITS ME OR NOT 65 | const Me = user => 66 | user == $('#data').data('session') ? true : false 67 | 68 | // FUNCTION TO CHECK WHETHER EMAIL IS ACTIVATED ON NOT 69 | const e_v = () => { 70 | let ea = $('.data').data('email-verified') 71 | return ea == "yes" ? true : false 72 | } 73 | 74 | // TO REMOVE LINE OF LAST ELEMENT 75 | const last_line_remover = () => { 76 | let 77 | f = $('.modal_main').children(), 78 | s = $('.display_content').children().length - 1 79 | f.eq(s).find('hr').remove() 80 | } 81 | 82 | // FUNCTION FOR PROFILE DATA UPDATING 83 | const forProfile = obj => { 84 | P.coroutine(function *(){ 85 | let 86 | { dispatch, username, invalidUser } = obj, 87 | valid = yield post('/api/is-user-valid', { username }), 88 | s_username = $('.data').data('username') 89 | 90 | if(!valid.data){ 91 | invalidUser() 92 | } else { 93 | 94 | if (username != s_username) { 95 | post('/api/view-profile', { username }) 96 | dispatch(follow_action.is_following(username)) 97 | } 98 | 99 | dispatch(user_action.user_details(username)) 100 | dispatch(notes_action.getNotes(username)) 101 | dispatch(follow_action.get_followers(username)) 102 | dispatch(follow_action.get_followings(username)) 103 | dispatch(follow_action.get_profile_views(username)) 104 | 105 | } 106 | 107 | })() 108 | } 109 | 110 | // FUNCTION FOR GOING BACK 111 | const back = (e, history) => { 112 | e.preventDefault() 113 | history.goBack() 114 | } 115 | 116 | // FUNCTION FOR EDTITNG PROFILE 117 | const edit_profile = options => { 118 | P.coroutine(function *(){ 119 | 120 | let 121 | { susername, semail, username, email, bio } = options, 122 | button = $('.e_done'), 123 | { data: uCount} = yield post('/api/what-exists', { what: "username", value: username }), 124 | { data: eCount } = yield post('/api/what-exists', { what: "email", value: email }) 125 | 126 | button. 127 | addClass('a_disabled') 128 | .text('Processing..') 129 | .blur() 130 | 131 | if(!username){ 132 | Notify({ value: "Username must not be empty!!" }) 133 | } else if(!email){ 134 | Notify({ value: "Email must not be empty!!" }) 135 | } else if(uCount == 1 && username != susername){ 136 | Notify({ value: "Username already exists!!" }) 137 | } else if(eCount == 1 && email != semail){ 138 | Notify({ value: "Email already exists!!" }) 139 | } else { 140 | 141 | let { data: { mssg, success } } = yield post('/api/edit-profile', { username, email, bio }) 142 | 143 | Notify({ 144 | value: mssg, 145 | done: () => success ? location.reload() : null 146 | }) 147 | 148 | } 149 | 150 | button 151 | .removeClass('a_disabled') 152 | .text('Done Editing') 153 | .blur() 154 | 155 | })().catch(e => console.log(e.stack) ) 156 | 157 | } 158 | 159 | // FUNCTION FOR CHANGING AVATAR 160 | const change_avatar = options => { 161 | let 162 | { file } = options, 163 | form = new FormData() 164 | 165 | $('.overlay-2').show() 166 | $('.avatar_span') 167 | .text('Changing avatar..') 168 | .addClass('sec_btn_disabled') 169 | 170 | form.append('avatar', file) 171 | 172 | $.ajax({ 173 | url: "/api/change-avatar", 174 | method: "POST", 175 | processData: false, 176 | contentType: false, 177 | data: form, 178 | dataType: "JSON", 179 | success: data => { 180 | Notify({ 181 | value: data.mssg, 182 | done: () => location.reload() 183 | }) 184 | } 185 | }) 186 | 187 | } 188 | 189 | // FUNCTION FOR RE-SENDING EMAIL VERIFICATION LINK 190 | const resend_vl = () => { 191 | let 192 | vl = $('.resend_vl'), 193 | o = $('.overlay-2') 194 | 195 | vl 196 | .addClass('a_disabled') 197 | .text('Sending verification link..') 198 | 199 | o.show() 200 | 201 | post('/api/resend_vl') 202 | .then(s => { 203 | let { mssg } = s.data 204 | Notify({ value: mssg }) 205 | vl 206 | .removeClass('a_disabled') 207 | .text('Send verification link') 208 | .blur() 209 | o.hide() 210 | }) 211 | 212 | } 213 | 214 | // FUNCTION TO DEACTIVATE ACCOUNT 215 | const deactivate = () => { 216 | let 217 | btn = $('.prompt-done'), 218 | o = $('.overlay') 219 | 220 | btn 221 | .addClass('a_disabled') 222 | .text('Deactivating..') 223 | 224 | o.show() 225 | 226 | post('/api/deactivate') 227 | .then(d => { 228 | btn 229 | .removeClass('a_disabled') 230 | .text('Deactivated') 231 | Notify({ 232 | value: "Deactivated", 233 | done: () => location.href = "/login" 234 | }) 235 | }) 236 | } 237 | 238 | // FUNCTION FOR CREATING A NOTE 239 | const createNote = options => { 240 | let { title, content, dispatch, history } = options 241 | 242 | if(!title || !content){ 243 | Notify({ value: "Values are missing!!" }) 244 | } else { 245 | 246 | post('/api/create-note', { title, content }) 247 | .then(s => { 248 | dispatch(notes_action.updateNote(s.data)) 249 | history.goBack() 250 | Notify({ value: 'Note Created!!' }) 251 | }) 252 | .catch(e => console.log(e) ) 253 | 254 | } 255 | } 256 | 257 | // FUNCTION FOR DELETING NOTE 258 | const deleteNote = options => { 259 | let { note, dispatch, history } = options 260 | post('/api/delete-note', { note }) 261 | .then(s => { 262 | dispatch(notes_action.deleteNote(note)) 263 | history.goBack() 264 | Notify({ value: s.data.mssg }) 265 | }) 266 | .catch(e => console.log(e) ) 267 | } 268 | 269 | // FUNCTION FOR EDITING THE NOTE 270 | const editNote = options => { 271 | let { title, content, note_id, setState, dispatch } = options 272 | 273 | if(title == "" || content == "" ){ 274 | Notify({ value: "Fields are empty!!" }) 275 | setState({ editing: true }) 276 | } else { 277 | 278 | post('/api/edit-note', { title, content, note_id }) 279 | .then(s => { 280 | Notify({ value: s.data.mssg }) 281 | dispatch(notes_action.editNote({ note_id, title, content })) 282 | }) 283 | .catch(e => console.log(e)) 284 | 285 | } 286 | 287 | } 288 | 289 | // FUNCTION TO FOLLOW 290 | const follow = options => { 291 | let 292 | defaults = { 293 | user: null, 294 | username: null, 295 | dispatch: () => { return null }, 296 | update_followers: false, 297 | update_followings: false, 298 | done: () => { return null } 299 | }, 300 | obj = { ...defaults, ...options }, 301 | { user, username, dispatch, update_followers, update_followings, done } = obj 302 | 303 | post('/api/follow', { user, username }) 304 | .then(s => { 305 | 306 | let 307 | { follow_id, follow_time } = s.data, 308 | fwing = { 309 | follow_id: follow_id, 310 | follow_by: $('.data').data('session'), 311 | follow_by_username: $('.data').data('username'), 312 | follow_time: follow_time, 313 | follow_to: user, 314 | follow_to_username: username 315 | } 316 | 317 | update_followers ? dispatch(follow_action.follower(s.data)) : null 318 | update_followings ? dispatch(follow_action.following(fwing)) : null 319 | 320 | Notify({ value: "Followed" }) 321 | done() 322 | 323 | }) 324 | .catch(e => console.log(e) ) 325 | 326 | } 327 | 328 | const unfollow = options => { 329 | let 330 | defaults = { 331 | user: null, 332 | dispatch: () => { return null }, 333 | update_followers: false, 334 | update_followings: false, 335 | done: () => { return null } 336 | }, 337 | obj = { ...defaults, ...options }, 338 | { user, dispatch, update_followers, update_followings, done } = obj 339 | 340 | post('/api/unfollow', { user }) 341 | .then(s => { 342 | update_followers ? dispatch(follow_action.unfollower($('.data').data('session'))) : null 343 | update_followings ? dispatch(follow_action.unfollowing(user)) : null 344 | 345 | Notify({ value: "Unfollowed" }) 346 | done() 347 | }) 348 | .catch(e => console.log(e) ) 349 | 350 | } 351 | 352 | // TO LIKE THE NOTE 353 | const like = options => { 354 | let { note, dispatch, done } = options 355 | 356 | post('/api/like', { note }) 357 | .then(s => { 358 | Notify({ value: "Liked" }) 359 | dispatch(note_int_action.liked(s.data)) 360 | done() 361 | }) 362 | 363 | } 364 | 365 | // TO UNLIKE THE NOTE 366 | const unlike = options => { 367 | let { note, dispatch, done } = options 368 | 369 | post('/api/unlike', { note }) 370 | .then(u => { 371 | Notify({ value: "Unliked" }) 372 | dispatch(note_int_action.unliked(note)) 373 | done() 374 | }) 375 | 376 | } 377 | 378 | module.exports = { 379 | shortener, 380 | toggle, 381 | commonLogin, 382 | c_first, 383 | Me, 384 | e_v, 385 | last_line_remover, 386 | forProfile, 387 | back, 388 | edit_profile, 389 | change_avatar, 390 | resend_vl, 391 | deactivate, 392 | createNote, 393 | deleteNote, 394 | editNote, 395 | follow, 396 | unfollow, 397 | like, 398 | unlike 399 | } 400 | -------------------------------------------------------------------------------- /public/styles/src/styles.less: -------------------------------------------------------------------------------- 1 | @import 'defaults.less'; 2 | 3 | .notes_wrapper{ 4 | margin-top: 70px; 5 | } 6 | 7 | .index_header{ 8 | width: 100%; 9 | height: 50px; 10 | position: fixed; 11 | z-index: 1; 12 | top: 0px; 13 | 14 | & .right{ 15 | float: right; 16 | position: relative; 17 | display: inline-block; 18 | right: 70px; 19 | top: 21px; 20 | 21 | & a{ 22 | border: 1px solid @ee; 23 | outline: none; 24 | padding: 4px 16px; 25 | border-radius: 3px; 26 | font-size: 14px; 27 | margin-right: 5px; 28 | color: #3d464d; 29 | background: @ff; 30 | 31 | & a[href='help']{ margin-right: 0px; } 32 | 33 | & .index_header > .right > a[href="about"]{ margin-right: 5px; } 34 | 35 | &:hover{ background: @second_border; } 36 | 37 | &:focus{ background: @second_border; } 38 | 39 | } 40 | 41 | } 42 | 43 | } 44 | 45 | .header_loggedin{ 46 | background: @ff; 47 | box-shadow: 0 0 5px rgba(0,0,0,0.1); 48 | position: fixed; 49 | width: 100%; 50 | height: 45px; 51 | background: white; 52 | z-index: 2; 53 | top: 0px; 54 | 55 | & a{ 56 | font-size: 14px; 57 | color: @dark; 58 | margin-left: 7px; 59 | padding: 5px 12px; 60 | border-radius: 3px; 61 | display: inline-block; 62 | 63 | &:hover{ background: @second_border; } 64 | 65 | &:focus{ background: @second_border; } 66 | 67 | } 68 | 69 | & .left{ 70 | display: inline-block; 71 | position: relative; 72 | top: 8px; 73 | left: 25px; 74 | } 75 | 76 | & .right { 77 | display: inline-block; 78 | position: absolute; 79 | top: 8px; 80 | right: 3%; 81 | } 82 | 83 | } 84 | 85 | .display_text { 86 | text-align: center; 87 | margin-bottom: 30px; 88 | 89 | & > span{ 90 | font-size: 23px; 91 | color: #0b867a; 92 | } 93 | 94 | } 95 | 96 | .log_sign{ 97 | position: absolute; 98 | right: 50px; 99 | top: 88px; 100 | 101 | & > a{ 102 | padding: 5px 18px; 103 | border-radius: 4px; 104 | font-size: 14px; 105 | } 106 | 107 | } 108 | 109 | // for active links 110 | .ha_active{ background: @second_border; } 111 | 112 | // REGISTER PAGE 113 | 114 | .cua{ 115 | width: 300px; 116 | .center(); 117 | 118 | & form > *{ 119 | display: block; 120 | margin-bottom: 10px; 121 | width: 92%; 122 | padding: 8px 10px; 123 | font-size: 14px; 124 | } 125 | 126 | & input[type='submit']{ 127 | width: 99%; 128 | } 129 | 130 | } 131 | 132 | 133 | // REGISTERED PAGE 134 | .registered{ 135 | position: absolute; 136 | background: @ff; 137 | padding: 20px; 138 | width: 300px; 139 | top: 50%; 140 | left: 50%; 141 | transform: translate(-50%, -50%); 142 | font-size: 15px; 143 | border-radius: 5px; 144 | box-shadow: 0px 0px 15px 0px rgba(0,0,0,.1); 145 | } 146 | 147 | // PROFILE PAGE 148 | .aligner{ 149 | position: relative; 150 | width: 614px; 151 | left: 367px; 152 | 153 | .user_banner{ 154 | text-align: center; 155 | position: relative; 156 | background: white; 157 | padding: 15px; 158 | border: 1px solid @header_border; 159 | border-radius: 4px; 160 | 161 | .user_buttons{ 162 | position: absolute; 163 | right: 15px; 164 | top: 15px; 165 | 166 | &>a{ 167 | padding: 5px 15px; 168 | font-size: 14px; 169 | display: inline-block; 170 | margin-right: 5px; 171 | 172 | } 173 | 174 | } 175 | 176 | & .profile_img_div{ 177 | margin-top: 10px; 178 | & img{ 179 | border-radius: 50%; 180 | height: 150px; 181 | width: 150px; 182 | } 183 | } 184 | 185 | & .user_info{ 186 | margin-top: 10px; 187 | 188 | & a.user_main_link{ 189 | font-weight: 600; 190 | color: @dark; 191 | display: block; 192 | font-size: 16px; 193 | } 194 | 195 | & .user_no_notes{ 196 | color: @d_light; 197 | font-size: 13px; 198 | } 199 | 200 | & .user_bio{ 201 | text-align: center; 202 | font-size: 14px; 203 | margin-top: 7px; 204 | } 205 | 206 | & .no_bio{ 207 | font-style: italic; 208 | color: @d_light; 209 | } 210 | 211 | & hr{ 212 | margin: 25px 0px 10px 0px; 213 | border-top: 1px solid #f5eded; 214 | } 215 | 216 | & .user_stats{ 217 | text-align: center; 218 | 219 | &>div, &>a{ 220 | display: inline-block; 221 | padding: 7px 40px; 222 | text-align: center; 223 | position: relative; 224 | cursor: pointer; 225 | border-radius: 3px; 226 | color: inherit; 227 | 228 | &:hover{ 229 | background: @second_border; 230 | } 231 | 232 | & .stat_hg{ 233 | display: block; 234 | font-size: 15px; 235 | font-weight: 600; 236 | } 237 | 238 | } 239 | 240 | & .stat_disabled{ 241 | cursor: text !important; 242 | &:hover{ background: @ff; } 243 | } 244 | 245 | } 246 | 247 | } 248 | 249 | } 250 | 251 | .filter_notes{ 252 | position: relative; 253 | margin-top: 15px; 254 | 255 | & > input[type="text"]{ 256 | font-size: 14px; 257 | padding: 8px; 258 | width: 97%; 259 | } 260 | 261 | } 262 | 263 | .notes{ 264 | margin-top: 15px; 265 | margin-bottom: 50px; 266 | } 267 | 268 | } 269 | 270 | // FOR NOTE 271 | .note{ 272 | background: @ff; 273 | padding: 10px; 274 | display: block; 275 | font-size: 14px; 276 | border: 1px solid @header_border; 277 | border-radius: 3px; 278 | margin-bottom: 10px; 279 | cursor: pointer; 280 | 281 | &:hover{ 282 | border-color: @ee; 283 | } 284 | 285 | & .note_header{ 286 | margin-bottom: 7px; 287 | 288 | & img{ 289 | height: 31px; 290 | width: 31px; 291 | border-radius: 50%; 292 | display: inline-block; 293 | } 294 | 295 | & .note_h_left{ 296 | display: inline-block; 297 | margin-left: 5px; 298 | width: 90%; 299 | 300 | & .note_username{ 301 | display: block; 302 | color: @dark; 303 | font-weight: 600; 304 | position: relative; 305 | top: 2px; 306 | } 307 | 308 | & .note_time{ 309 | font-size: 13px; 310 | color: @d_light; 311 | position: relative; 312 | top: -1px; 313 | } 314 | 315 | } 316 | 317 | } 318 | 319 | & .note_title, & .note_content{ 320 | color: @dark; 321 | } 322 | 323 | & .note_title{ 324 | margin-bottom: 4px; 325 | font-weight: 600; 326 | } 327 | 328 | } 329 | 330 | .create_note{ 331 | width: 380px; 332 | 333 | & input[type='text'], & textarea{ 334 | padding: 7px !important; 335 | width: 94%; 336 | font-size: 14px; 337 | } 338 | 339 | & input[type="text"]{ 340 | margin-bottom: 9px; 341 | } 342 | 343 | & textarea{ 344 | height: 250px; 345 | } 346 | 347 | & .c_n_bottom{ 348 | margin-top: 1px; 349 | 350 | & input[type='submit']{ 351 | margin-left: 5px; 352 | padding: 5px 10px; 353 | margin-right: 6px; 354 | font-size: 14px; 355 | } 356 | } 357 | 358 | } 359 | 360 | // WELCOME AND ERROR PAGE 361 | .welcome_div{ 362 | .center(); 363 | & img{ 364 | height: 300px; 365 | } 366 | } 367 | 368 | .error_div{ 369 | width: 436px; 370 | 371 | .error_info{ 372 | margin-bottom: 10px; 373 | width: 100%; 374 | & span{ 375 | font-size: 20px; 376 | } 377 | } 378 | 379 | .error_bottom { 380 | text-align: right; 381 | margin-top: 3px; 382 | 383 | & a{ 384 | display: inline-block; 385 | padding: 4px 15px; 386 | font-weight: 600; 387 | font-size: 14px; 388 | 389 | &:last-of-type{ 390 | margin-left: 5px; 391 | } 392 | 393 | } 394 | 395 | } 396 | 397 | 398 | } 399 | // view note 400 | .view_note{ 401 | width: 600px; 402 | 403 | & .v_n_middle{ 404 | 405 | & .v_n_info{ 406 | margin-bottom: 5px; 407 | 408 | & img{ 409 | width: 40px; 410 | height: 40px; 411 | border-radius: 50%; 412 | display: inline-block; 413 | } 414 | 415 | & .v_n_left{ 416 | display: inline-block; 417 | margin-left: 5px; 418 | position: relative; 419 | top: -5px; 420 | 421 | & .v_n_username{ 422 | display: block; 423 | font-weight: 600; 424 | font-size: 15px; 425 | color: inherit; 426 | 427 | &:hover, &:focus{ text-decoration: underline; } 428 | 429 | } 430 | 431 | & .v_n_time{ 432 | font-size: 13px; 433 | color: @d_light; 434 | position: relative; 435 | top: -2px; 436 | } 437 | 438 | } 439 | 440 | } 441 | 442 | & .v_n_title{ 443 | font-weight: 600; 444 | display: block; 445 | margin-bottom: 3px; 446 | font-size: 15px; 447 | outline: none; 448 | } 449 | 450 | & .v_n_content{ 451 | display: block; 452 | outline: none; 453 | } 454 | 455 | & .v_n_title[contenteditable='true'], 456 | & .v_n_content[contenteditable='true']{ 457 | border: 1px solid #54BBFF; 458 | padding: 5px 5px !important; 459 | border-radius: 2px; 460 | } 461 | 462 | } 463 | 464 | & .v_n_bottom { 465 | 466 | a{ margin-left: 5px; } 467 | 468 | & .v_n_cancel{ margin-right: 5px; } 469 | 470 | & .v_n_int{ 471 | position: absolute; 472 | display: flex; 473 | color: @d_light; 474 | bottom: 10px; 475 | left: 10px; 476 | 477 | span.like_unlike{ 478 | display: inline-block; 479 | height: 24px; 480 | cursor: pointer; 481 | transition: all .1s ease-in-out; 482 | 483 | &:hover{ 484 | color: @dark; 485 | transform: scale(1.1); 486 | } 487 | 488 | } 489 | 490 | & .like_unlike_disabled{ 491 | cursor: not-allowed !important; 492 | pointer-events: none !important; 493 | color: #d8c3c3 !important; 494 | } 495 | 496 | } 497 | 498 | } 499 | 500 | } 501 | 502 | .content_editor{ 503 | margin-top: 5px; 504 | } 505 | 506 | // EDIT 507 | .edit{ 508 | position: relative; 509 | width: 400px; 510 | left: 490px; 511 | font-size: 14px; 512 | 513 | & .edit_animation > *{ 514 | display: block; 515 | width: 100%; 516 | margin-top: 15px; 517 | } 518 | 519 | & .eb_btns{ 520 | text-align: right; 521 | 522 | & a{ 523 | display: inline-block; 524 | margin-right: 23px; 525 | padding: 5px 20px; 526 | } 527 | 528 | & form{ 529 | display: inline-block; 530 | & .avatar_span{ 531 | display: inline-block; 532 | padding: 5px 10px 6px 10px; 533 | margin-right: 7px; 534 | } 535 | } 536 | 537 | } 538 | 539 | & .edit_span{ 540 | color: @d_light; 541 | display: block; 542 | position: relative; 543 | font-size: 14px; 544 | margin-bottom: 5px; 545 | } 546 | 547 | & .edit_info{ 548 | 549 | & img{ 550 | width: 35px; 551 | height: 35px; 552 | border-radius: 50%; 553 | position: relative; 554 | display: inline-block; 555 | } 556 | 557 | & span { 558 | position: relative; 559 | top: -12px; 560 | left: 8px; 561 | font-size: 18px; 562 | font-weight: 600; 563 | } 564 | 565 | } 566 | 567 | & .e_joined{ 568 | text-align: right; 569 | width: 94%; 570 | } 571 | 572 | & input[type='text'], 573 | & input[type='email'], 574 | textarea{ 575 | width: 90%; 576 | padding: 8px; 577 | font-size: 14px; 578 | } 579 | 580 | & textarea{ 581 | height: 100px; 582 | } 583 | 584 | & .resend_vl_div{ 585 | text-align: right; 586 | 587 | & .resend_vl{ 588 | display: inline-block; 589 | padding: 5px 15px; 590 | margin-right: 21px; 591 | } 592 | 593 | } 594 | 595 | } 596 | 597 | // HOME 598 | .home{ 599 | position: relative; 600 | width: 607px; 601 | margin-left: 365px; 602 | margin-bottom: 60px; 603 | 604 | & .home_info{ 605 | margin-bottom: 10px; 606 | background: @ff; 607 | padding: 14px 10px; 608 | border: 1px solid @header_border; 609 | border-radius: 3px; 610 | font-size: 15px; 611 | 612 | & a{ 613 | position: absolute; 614 | right: 10px; 615 | padding: 4px 12px; 616 | top: 10px; 617 | } 618 | 619 | } 620 | 621 | } 622 | 623 | // PAGE END 624 | .page_end{ 625 | padding: 10px; 626 | text-align: center; 627 | font-size: 14px; 628 | margin-top: 10px; 629 | color: @d_light; 630 | cursor: pointer; 631 | border-radius: 3px; 632 | border: 1px solid @fb; 633 | 634 | &:hover{ border: 1px solid @ee; } 635 | 636 | } 637 | 638 | // EXPLORE PAGE 639 | .explore{ 640 | 641 | & .explores{ 642 | display: inline-block; 643 | width: 615px; 644 | margin-left: 365px; 645 | 646 | & div.explores_list{ 647 | position: relative; 648 | background: @ff; 649 | // width: 100%; 650 | padding: 10px; 651 | border-radius: 3px; 652 | border: 1px solid @header_border; 653 | margin-bottom: 10px; 654 | 655 | &:hover{ border-color: @ee; } 656 | 657 | & .exl_main{ 658 | 659 | & img{ 660 | width: 45px; 661 | height: 45px; 662 | border-radius: 50%; 663 | } 664 | 665 | & .exl_content{ 666 | display: inline-block; 667 | position: relative; 668 | top: -8px; 669 | margin-left: 7px; 670 | 671 | & a.exl_username{ 672 | color: @dark; 673 | font-size: 14px; 674 | font-weight: 600; 675 | } 676 | 677 | & div.exl_desc{ 678 | color: @d_light; 679 | 680 | & span.exl_desc_sep{ 681 | margin-left: 5px; 682 | margin-right: 5px; 683 | } 684 | 685 | } 686 | 687 | } 688 | 689 | } 690 | 691 | & .exl_ff{ 692 | position: absolute; 693 | right: 15px; 694 | top: 25px; 695 | 696 | & a{ 697 | padding: 5px 20px; 698 | font-size: 14px; 699 | } 700 | 701 | } 702 | 703 | } 704 | 705 | } 706 | 707 | } 708 | 709 | // PROMPT MODAL 710 | .prompt{ 711 | position: fixed; 712 | top: 50%; 713 | left: 50%; 714 | transform: translate(-50%, -50%); 715 | z-index: 2; 716 | background: @ff; 717 | width: 400px; 718 | border-radius: 3px; 719 | .modal-shadow(); 720 | 721 | & .prompt-top{ 722 | padding: 10px 10px; 723 | background: @third-border; 724 | border-top-left-radius: 3px; 725 | border-top-right-radius: 3px; 726 | border-bottom: 1px solid @ee; 727 | 728 | & .prompt-title{ 729 | font-size: 15px; 730 | font-weight: 600; 731 | } 732 | 733 | & span:last-of-type{ 734 | display: inline-block; 735 | position: absolute; 736 | right: 7px; 737 | top: 7px; 738 | padding: 2px; 739 | color: @d_light; 740 | height: 20px; 741 | cursor: pointer; 742 | border: 1px solid @third-border; 743 | border-radius: 3px; 744 | 745 | &:hover{ 746 | color: #1b2733; 747 | border: 1px solid #e2dbdb; 748 | } 749 | 750 | & i{ 751 | font-size: 20px; 752 | position: relative; 753 | left: 1px; 754 | } 755 | 756 | } 757 | 758 | } 759 | 760 | & .prompt-middle{ 761 | padding: 4px 10px; 762 | font-size: 14px; 763 | margin-top: 11px; 764 | margin-bottom: 10px; 765 | position: relative; 766 | 767 | & span{ 768 | display: inline-block; 769 | position: relative; 770 | } 771 | 772 | } 773 | 774 | & .prompt-bottom{ 775 | padding: 8px 10px 8px 10px; 776 | border-top: 1px solid @ee; 777 | text-align: right; 778 | 779 | & a{ 780 | display: inline-block; 781 | padding: 4px 12px; 782 | font-weight: 600; 783 | 784 | &:last-of-type{ 785 | margin-left: 5px; 786 | } 787 | 788 | } 789 | 790 | } 791 | 792 | } 793 | 794 | // GOTO COMPONENT 795 | .goto{ 796 | position: absolute; 797 | right: 7px; 798 | top: 6px; 799 | height: 28px; 800 | 801 | & .goto_link{ 802 | 803 | & .goto_label{ 804 | position: relative; 805 | top: -7px; 806 | right: 2px; 807 | font-weight: 600; 808 | } 809 | 810 | & span.show_more{ 811 | display: inline-block; 812 | height: 24px; 813 | border: 1px solid @header; 814 | border-radius: 2px; 815 | cursor: pointer; 816 | color: @d_light; 817 | transition: all .1s ease-in-out; 818 | 819 | &:focus{ color: @dark; } 820 | 821 | } 822 | 823 | } 824 | 825 | & .goto_options{ 826 | right: -2px; 827 | top: 35px; 828 | 829 | &::before{ 830 | content: ""; 831 | background: white; 832 | width: 11px; 833 | height: 11px; 834 | top: -6px; 835 | position: absolute; 836 | border-top: 1px solid #DCDFE1; 837 | transform: rotate(45deg); 838 | border-left: 1px solid #DCDFE1; 839 | right: 9px; 840 | z-index: -1; 841 | } 842 | 843 | } 844 | 845 | } 846 | 847 | // DEACTIVATE 848 | .deactivate{ 849 | width: 500px; 850 | 851 | & .deactivate_title{ 852 | font-weight: 600; 853 | display: block; 854 | margin-bottom: 5px; 855 | } 856 | 857 | & .deactivate_btn{ 858 | display: block; 859 | margin-top: 11px; 860 | text-align: right; 861 | 862 | & a{ 863 | display: inline-block; 864 | padding: 4px 10px; 865 | } 866 | 867 | } 868 | 869 | } 870 | -------------------------------------------------------------------------------- /public/styles/src/defaults.less: -------------------------------------------------------------------------------- 1 | @a: #2895F1; 2 | @family: 'Open Sans', 'Roboto', Tahoma, arial, sans-serif; 3 | @dark: #1b2733; 4 | @fb: #fbfbfb; 5 | @ff: #fff; 6 | @ee: #eee; 7 | @pric: #1b9be9; 8 | @d_light: #66757f; 9 | @header: #f9f9f9; 10 | @header_border: #f7f5f5; 11 | @second_border: #f7f9fa; 12 | @third-border: #f6f7f9; 13 | 14 | .center(){ 15 | position: absolute; 16 | top: 50%; 17 | left: 50%; 18 | transform: translate(-50%, -50%); 19 | } 20 | 21 | .modal-shadow(){ 22 | box-shadow: 0 0 18px rgba(27,31,35,0.4); 23 | -webkit-box-shadow: 0 0 18px rgba(27,31,35,0.4); 24 | -moz-box-shadow: 0 0 18px rgba(27,31,35,0.4); 25 | } 26 | 27 | .overlay_defaults(){ 28 | position: fixed; 29 | width: 100%; 30 | height: 100%; 31 | top: 0px; 32 | left: 0px; 33 | right: 0px; 34 | bottom: 0px; 35 | z-index: 2; 36 | } 37 | 38 | .modal-shadow{ 39 | box-shadow: 0 0 18px rgba(27,31,35,0.4); 40 | -webkit-box-shadow: 0 0 18px rgba(27,31,35,0.4); 41 | -moz-box-shadow: 0 0 18px rgba(27,31,35,0.4); 42 | } 43 | 44 | *{ 45 | padding: 0px; 46 | margin: 0px; 47 | } 48 | 49 | body{ 50 | font-family: @family; 51 | background: @fb; 52 | font-size: 13px; 53 | color: #3d464d; 54 | font-weight: normal; 55 | } 56 | 57 | a{ 58 | text-decoration: none; 59 | color: @a; 60 | outline: none; 61 | } 62 | 63 | .a_disabled{ 64 | cursor: not-allowed !important; 65 | pointer-events: none !important; 66 | background: #6ab9e8 !important; 67 | } 68 | 69 | .sec_btn_disabled{ 70 | cursor: not-allowed !important; 71 | pointer-events: none !important; 72 | color: #d8c3c3 !important; 73 | } 74 | 75 | input[type="range"]{ 76 | outline: none; 77 | } 78 | 79 | input[type=range]::-moz-focus-outer { 80 | border: 0; 81 | } 82 | 83 | i{ 84 | pointer-events: none; 85 | } 86 | 87 | hr{ 88 | border: 0; 89 | border-top: 1px solid #c1c7cd; 90 | } 91 | 92 | li{ 93 | list-style: none; 94 | } 95 | 96 | input[type="text"], 97 | input[type="email"], 98 | input[type="password"], 99 | textarea{ 100 | border: 1px solid @ee; 101 | border-radius: 4px; 102 | font-family: @family; 103 | color: #0b867a; 104 | outline: none; 105 | padding: 5px 5px; 106 | } 107 | 108 | input[type="submit"]::-moz-focus-inner, 109 | input[type="button"]::-moz-focus-inner{ 110 | padding: 0 !important; 111 | border: 0 none !important; 112 | } 113 | 114 | textarea{ 115 | word-break: break-all; 116 | font-size: 13px; 117 | } 118 | 119 | input[type="text"]:focus, 120 | input[type="password"]:focus, 121 | input[type="email"]:focus, 122 | textarea:focus{ 123 | border: 1px solid #56b4ef !important; 124 | box-shadow: 0px 0px 5px 1px #c8def0; 125 | /*-webkit-box-shadow: 0px 0px 5px 0px #bed3dc; 126 | -moz-box-shadow: 0px 0px 5px 0px #bed3dc;*/ 127 | } 128 | 129 | input[type="submit"], 130 | input[type="button"], 131 | .pri_btn{ 132 | font-weight: 600; 133 | background: @pric; 134 | border: 1px solid @pric; /*#3e91b3*/ 135 | color: @ff;; 136 | border-radius: 3px; 137 | cursor: pointer; 138 | outline: none; 139 | 140 | &:hover{ opacity: .9; } 141 | 142 | } 143 | 144 | input[type="submit"]:focus, 145 | input[type="button"]:focus, 146 | .pri_btn:focus{ 147 | background: #198bd0; 148 | } 149 | 150 | input[type="submit"]:disabled, 151 | input[type="button"]:disabled{ 152 | background: #6ab9e8 !important; 153 | cursor: auto !important; 154 | 155 | &:hover{ opacity: 1 !important; } 156 | 157 | } 158 | 159 | .sec_btn{ 160 | border: 1px solid @ee; 161 | background: @fb; 162 | color: @dark; 163 | border-radius: 3px; 164 | cursor: pointer; 165 | font-weight: 600; 166 | outline: none; 167 | 168 | &:hover{ 169 | color: @dark; 170 | background-color: #fff7f7; 171 | } 172 | 173 | &:focus{ 174 | color: @dark; 175 | background: #f7ebeb; 176 | } 177 | 178 | } 179 | 180 | .tir_btn{ 181 | border: 1px solid @ee; 182 | background: @ff; 183 | color: @dark; 184 | border-radius: 3px; 185 | cursor: pointer; 186 | outline: none; 187 | 188 | &:hover{ 189 | color: @dark; 190 | background-color: #ebf4fd; 191 | } 192 | 193 | &:focus{ 194 | color: @dark; 195 | background-color: #ebf4fd; 196 | } 197 | 198 | } 199 | 200 | .a_pri{ color: @a; } 201 | 202 | .options{ 203 | position: absolute; 204 | padding: 7px 6px; 205 | background: @ff; 206 | box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08); 207 | -webkit-box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08); 208 | -moz-box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08); 209 | border-radius: 3px; 210 | z-index: 1; 211 | 212 | & ul{ 213 | background: @ff; 214 | 215 | & li{ 216 | background: @ff; 217 | 218 | & a, 219 | & label{ 220 | background: @ff; 221 | font-size: 14px; 222 | color: @d_light; 223 | padding: 4px 12px 4px 8px; 224 | width: inherit; 225 | display: block; 226 | border-radius: 2px; 227 | 228 | &:hover{ 229 | /*background: @second_border;*/ 230 | background: #54BBFF; 231 | /*background: #f4f4f4;*/ 232 | /*color: #1b2733;*/ 233 | color: @ff; 234 | } 235 | 236 | } 237 | 238 | &.o_divider{ 239 | margin: 5px 0px 5px 0px; 240 | } 241 | 242 | &.menu_divider{ 243 | border-top-color: #e6e8eb; 244 | } 245 | 246 | } 247 | 248 | } 249 | 250 | } 251 | 252 | input[type="range"]{ 253 | -webkit-appearance: none; 254 | width: 100%; 255 | border-radius: 3px; 256 | cursor: pointer; 257 | 258 | &:focus{ 259 | outline: none; 260 | 261 | &::-ms-thumb{ 262 | transform: scale(1.2); 263 | -webkit-transform: scale(1.2); 264 | -moz-transform: scale(1.2); 265 | } 266 | 267 | } 268 | 269 | &::-webkit-slider-runnable-track{ 270 | width: 100%; 271 | background: rgba(255,255,255,0.5); 272 | background: @ff; 273 | /*background: lightskyblue;*/ 274 | cursor: pointer; 275 | border-radius: 3px; 276 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,.1); 277 | height: 6px; 278 | } 279 | 280 | &::-webkit-slider-thumb{ 281 | width: 15px; 282 | height: 15px; 283 | border-radius: 50%; 284 | background: #4080ff; 285 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,.1); 286 | margin-top: -5px; 287 | margin-left: px; 288 | cursor: pointer; 289 | -webkit-appearance: none; 290 | border: 1px solid #4080ff; 291 | } 292 | 293 | &:hover::-webkit-slider-thumb{ 294 | transform: scale(1.2); 295 | -webkit-transform: scale(1.2); 296 | } 297 | 298 | &::-moz-range-track{ 299 | width: 100%; 300 | background: #fff; 301 | /*background: lightskyblue;*/ 302 | cursor: pointer; 303 | border-radius: 3px; 304 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,.1); 305 | height: 6px; 306 | } 307 | 308 | &::-moz-range-thumb{ 309 | width: 14px; 310 | height: 14px; 311 | border-radius: 50%; 312 | border: 1px solid #4080ff; 313 | background: #4080ff; 314 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,.1); 315 | cursor: pointer; 316 | } 317 | 318 | &:hover::-moz-range-thumb{ 319 | transform: scale(1.2); 320 | -moz-transform: scale(1.2); 321 | } 322 | 323 | &::-ms-track{ 324 | width: 100%; 325 | background: transparent; 326 | border-color: transparent; 327 | /*border-width: 10px 0px 10px 0px;*/ 328 | color: transparent; 329 | height: 6px; 330 | border-radius: 3px; 331 | z-index: 2; 332 | position: relative; 333 | border: 1px solid; 334 | /*border: 1px solid #ecf0f1;*/ 335 | /*margin-top: -10px;*/ 336 | } 337 | 338 | &::-ms-fill-lower{ 339 | background: lightskyblue; 340 | border-radius: 3px; 341 | } 342 | 343 | &::-ms-fill-upper{ 344 | background: #ecf0f1; 345 | border-color: #ecf0f1 !important; 346 | border-radius: 3px; 347 | } 348 | 349 | &::-ms-thumb{ 350 | width: 12px; 351 | height: 12px; 352 | border-radius: 50%; 353 | background: #4080ff; 354 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,.1); 355 | margin-top: 0px; 356 | cursor: pointer; 357 | border: 1px solid #4080ff; 358 | } 359 | 360 | } 361 | 362 | input[type="file"]{ 363 | width: 0.1px; 364 | height: 0.1px; 365 | opacity: 0; 366 | overflow: hidden; 367 | position: absolute; 368 | z-index: -2; 369 | } 370 | 371 | .overlay{ 372 | .overlay_defaults(); 373 | background: #000; 374 | opacity: 0.5 !important; 375 | } 376 | 377 | .hidden_overlay{ 378 | .overlay_defaults(); 379 | background: transparent !important; 380 | } 381 | 382 | .colored_overlay{ 383 | .overlay_defaults(); 384 | background: rgb(238, 238, 238) !important; 385 | } 386 | 387 | .overlay-2{ 388 | .overlay_defaults(); 389 | background: transparent; 390 | z-index: 20; 391 | display: none; 392 | } 393 | 394 | select{ 395 | font-size: 14px; 396 | font-family: @family; 397 | border-radius: 3px; 398 | border: 1px solid @ee; 399 | cursor: pointer; 400 | outline: none; 401 | /*-moz-appearance: none;*/ 402 | /*-webkit-appearance: none;*/ 403 | 404 | &:focus{ 405 | border-color: #56b4ef; 406 | box-shadow: 0px 0px 5px 1px #c8def0; 407 | } 408 | 409 | } 410 | 411 | .spinner{ 412 | width: 0px; 413 | height: 0px; 414 | background: #eee; 415 | border-radius: 50%; 416 | position: absolute; 417 | display: inline-block; 418 | left: 177px; 419 | top: 20px; 420 | 421 | & span{ 422 | display: block; 423 | height: 10px; 424 | width: 10px; 425 | background: #ddd; 426 | border-radius: 50%; 427 | position: absolute; 428 | top: 0px; 429 | transition: all .1s ease-in-out; 430 | 431 | &:nth-child(1){ 432 | left: -18px; 433 | animation: bounce 1s ease-in-out infinite; 434 | } 435 | 436 | &:nth-child(2){ 437 | animation: bounce 1s ease-in-out 0.33s infinite; 438 | } 439 | 440 | &:nth-child(3){ 441 | left: 18px; 442 | animation: bounce 1s ease-in-out 0.66s infinite; 443 | } 444 | 445 | } 446 | 447 | } 448 | 449 | @keyframes bounce { 450 | 0%, 75%, 100%{ 451 | transform: translateY(0px); 452 | } 453 | 25%{ 454 | transform: translateY(-10px) 455 | } 456 | } 457 | 458 | .inst_checkbox{ 459 | display: none; 460 | 461 | & + label:before{ 462 | content: "\f096"; 463 | font-family: "FontAwesome"; 464 | speak: none; 465 | font-weight: normal; 466 | font-style: normal; 467 | font-variant: normal; 468 | text-transform: normal; 469 | line-height: 1; 470 | -webkit-font-smoothing: antialiased; 471 | } 472 | 473 | &:checked + label::before{ 474 | content: "\f14a"; 475 | color: #06a3e9; 476 | animation: tick 1s ease-in; 477 | } 478 | 479 | &:disabled + label{ 480 | color: #aaa; 481 | } 482 | 483 | &:disabled + label::before{ 484 | content: "\f0c8"; 485 | color: #ccc; 486 | } 487 | 488 | } 489 | 490 | #hoverdiv{ 491 | position: absolute; 492 | background: #333; 493 | padding: 4px 14px; 494 | color: white; 495 | font-size: 13px; 496 | border-radius: 3px; 497 | top: 0px; 498 | left: 0px; 499 | display: none; 500 | z-index: 4; 501 | } 502 | 503 | .handy-notify{ 504 | position: fixed; 505 | background: #333; 506 | left: 2%; 507 | /*transform: translate(-50%, -50%);*/ 508 | color: @ff; 509 | border-radius: 3px; 510 | padding: 12px 80px 12px 25px; 511 | /*padding: 10px 30px;*/ 512 | font-size: 15px; 513 | cursor: pointer; 514 | text-align: left; 515 | z-index: 3; 516 | top: 105%; 517 | } 518 | 519 | .handy-tooltip-after::after{ 520 | content: ""; 521 | position: absolute; 522 | width: 8px; 523 | height: 8px; 524 | background: #333; 525 | transform: rotate(45deg); 526 | -webkit-transform: rotate(45deg); 527 | -moz-transform: rotate(45deg); 528 | bottom: -3px; 529 | left: 45%; 530 | z-index: -1; 531 | } 532 | 533 | .handy-tooltip-before::before{ 534 | content: ""; 535 | position: absolute; 536 | width: 8px; 537 | height: 8px; 538 | background: #333; 539 | transform: rotate(45deg); 540 | -webkit-transform: rotate(45deg); 541 | -moz-transform: rotate(45deg); 542 | top: -3px; 543 | left: 45%; 544 | z-index: -1; 545 | } 546 | 547 | .pro_ava_active{ 548 | box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08) !important; 549 | -webkit-box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08) !important; 550 | -moz-box-shadow: 0 0 0 1px rgba(99,114,130,0.16), 0 8px 16px rgba(27,39,51,0.08) !important; 551 | border: 3px solid #54BBFF !important; 552 | animation: pro_active 0.1s ease-in-out; 553 | -webkit-animation: pro_active 0.1s ease-in-out; 554 | -moz-animation: pro_active 0.1s ease-in-out; 555 | } 556 | 557 | @keyframes pro_active { 558 | 0%{ 559 | transform: scale(1); 560 | } 561 | 50%{ 562 | transform: scale(0.95); 563 | } 564 | 100%{ 565 | transform: scale(1); 566 | } 567 | } 568 | 569 | .follow{ 570 | background: @pric !important; 571 | border: 1px solid @pric !important; 572 | } 573 | 574 | .unfollow{ 575 | background: #6ae03b !important; 576 | color: white !important; 577 | border: 1px solid #6ae03b !important; 578 | 579 | &:focus{ background: #fbf2f2; } 580 | 581 | } 582 | 583 | .overlay_cursor{ 584 | cursor: -webkit-zoom-out; 585 | cursor: -moz-zoom-out; 586 | cursor: -ms-zoom-out; 587 | } 588 | 589 | .overlay_toggle{ 590 | opacity: 0.8 !important; 591 | } 592 | 593 | // NOTHING COMPONENT 594 | .home_last_mssg{ 595 | position: relative; 596 | border-radius: 4px; 597 | background:@ff; 598 | border: 1px solid @header_border; 599 | font-size: 14px; 600 | text-align: center; 601 | padding: 10px; 602 | cursor: default; 603 | 604 | & img{ 605 | position: relative; 606 | text-align: center; 607 | width: 150px; 608 | /*margin-bottom: 10px;*/ 609 | display: block; 610 | left: 48%; 611 | transform: translate(-50%); 612 | } 613 | 614 | & span{ 615 | position: relative; 616 | text-align: center; 617 | display: block; 618 | left: -5px; 619 | /*top: -5px;*/ 620 | } 621 | 622 | } 623 | 624 | .modal{ 625 | .center(); 626 | position: fixed !important; 627 | .modal-shadow(); 628 | width: 300px; 629 | border-radius: 3px; 630 | z-index: 2; 631 | background: @ff; 632 | font-size: 14px; 633 | 634 | & .modal_no{ 635 | text-align: center; 636 | position: relative; 637 | top: 30px; 638 | & img{ width: 150px; } 639 | } 640 | 641 | & .modal_header{ 642 | background: @header; 643 | border-bottom: 1px solid @header_border; 644 | padding: 10px; 645 | border-top-left-radius: 3px; 646 | border-top-right-radius: 3px; 647 | // font-weight: 600; 648 | 649 | & span.title{ 650 | font-weight: 600; 651 | } 652 | 653 | } 654 | 655 | & .modal_middle{ 656 | position: relative; 657 | padding: 10px; 658 | 659 | } 660 | 661 | & .modal_bottom{ 662 | display: block; 663 | text-align: right; 664 | padding: 9px 5px 12px 5px; 665 | border-top: 1px solid @ee; 666 | margin-top: 0px; 667 | 668 | & a, & input[type='submit']{ 669 | display: inline-block; 670 | padding: 4px 10px; 671 | font-weight: 600; 672 | } 673 | 674 | } 675 | 676 | } 677 | 678 | .modal_big{ 679 | width: 400px; 680 | 681 | & .modal_middle{ 682 | height: 450px; 683 | width: inherit; 684 | overflow: auto; 685 | padding: 0px !important; 686 | 687 | & .modal_main{ 688 | position: relative; 689 | padding: 5px 0px; 690 | 691 | & .modal_items{ 692 | margin-bottom: 5px; 693 | position: relative; 694 | padding: 0px 5px 5px 5px; 695 | 696 | & .modal_it_img{ 697 | position: relative; 698 | display: inline-block; 699 | height: 45px; 700 | margin-left: 5px; 701 | 702 | & img{ 703 | width: 45px; 704 | height: 45px; 705 | border-radius: 3px; 706 | } 707 | 708 | } 709 | 710 | & .modal_it_content{ 711 | display: inline-block; 712 | position: relative; 713 | width: 83%; 714 | margin-left: 8px; 715 | top: -10px; 716 | 717 | & .modal_it_info{ 718 | display: inline-block; 719 | width: 100%; 720 | 721 | & a.modal_it_username{ 722 | font-size: 14px; 723 | color: @dark; 724 | font-weight: 600; 725 | display: block; 726 | outline: none; 727 | 728 | &:hover,&:focus{text-decoration: underline;} 729 | 730 | } 731 | 732 | & span.modal_it_light{ 733 | color: @d_light; 734 | font-size: 13px; 735 | } 736 | 737 | } 738 | 739 | & .modal_ff{ 740 | display: inline-block; 741 | position: absolute; 742 | right: 5px; 743 | top: 5px; 744 | text-align: right; 745 | 746 | & a{ 747 | display: inline-block; 748 | padding: 5px 15px; 749 | font-weight: 600; 750 | } 751 | 752 | } 753 | 754 | } 755 | 756 | & hr{ 757 | border-top-color: @ee; 758 | margin-top: 3px; 759 | } 760 | 761 | } 762 | 763 | } 764 | 765 | } 766 | 767 | & .modal_bottom{ 768 | margin-top: 1px; 769 | 770 | & a:last-of-type{ 771 | margin-right: 10px; 772 | margin-left: 5px; 773 | } 774 | 775 | } 776 | 777 | } 778 | -------------------------------------------------------------------------------- /public/styles/dist/styles.css: -------------------------------------------------------------------------------- 1 | .modal-shadow { 2 | box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 3 | -webkit-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 4 | -moz-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 5 | } 6 | * { 7 | padding: 0px; 8 | margin: 0px; 9 | } 10 | body { 11 | font-family: 'Open Sans', 'Roboto', Tahoma, arial, sans-serif; 12 | background: #fbfbfb; 13 | font-size: 13px; 14 | color: #3d464d; 15 | font-weight: normal; 16 | } 17 | a { 18 | text-decoration: none; 19 | color: #2895F1; 20 | outline: none; 21 | } 22 | .a_disabled { 23 | cursor: not-allowed !important; 24 | pointer-events: none !important; 25 | background: #6ab9e8 !important; 26 | } 27 | .sec_btn_disabled { 28 | cursor: not-allowed !important; 29 | pointer-events: none !important; 30 | color: #d8c3c3 !important; 31 | } 32 | input[type="range"] { 33 | outline: none; 34 | } 35 | input[type=range]::-moz-focus-outer { 36 | border: 0; 37 | } 38 | i { 39 | pointer-events: none; 40 | } 41 | hr { 42 | border: 0; 43 | border-top: 1px solid #c1c7cd; 44 | } 45 | li { 46 | list-style: none; 47 | } 48 | input[type="text"], 49 | input[type="email"], 50 | input[type="password"], 51 | textarea { 52 | border: 1px solid #eee; 53 | border-radius: 4px; 54 | font-family: 'Open Sans', 'Roboto', Tahoma, arial, sans-serif; 55 | color: #0b867a; 56 | outline: none; 57 | padding: 5px 5px; 58 | } 59 | input[type="submit"]::-moz-focus-inner, 60 | input[type="button"]::-moz-focus-inner { 61 | padding: 0 !important; 62 | border: 0 none !important; 63 | } 64 | textarea { 65 | word-break: break-all; 66 | font-size: 13px; 67 | } 68 | input[type="text"]:focus, 69 | input[type="password"]:focus, 70 | input[type="email"]:focus, 71 | textarea:focus { 72 | border: 1px solid #56b4ef !important; 73 | box-shadow: 0px 0px 5px 1px #c8def0; 74 | /*-webkit-box-shadow: 0px 0px 5px 0px #bed3dc; 75 | -moz-box-shadow: 0px 0px 5px 0px #bed3dc;*/ 76 | } 77 | input[type="submit"], 78 | input[type="button"], 79 | .pri_btn { 80 | font-weight: 600; 81 | background: #1b9be9; 82 | border: 1px solid #1b9be9; 83 | /*#3e91b3*/ 84 | color: #fff; 85 | border-radius: 3px; 86 | cursor: pointer; 87 | outline: none; 88 | } 89 | input[type="submit"]:hover, 90 | input[type="button"]:hover, 91 | .pri_btn:hover { 92 | opacity: .9; 93 | } 94 | input[type="submit"]:focus, 95 | input[type="button"]:focus, 96 | .pri_btn:focus { 97 | background: #198bd0; 98 | } 99 | input[type="submit"]:disabled, 100 | input[type="button"]:disabled { 101 | background: #6ab9e8 !important; 102 | cursor: auto !important; 103 | } 104 | input[type="submit"]:disabled:hover, 105 | input[type="button"]:disabled:hover { 106 | opacity: 1 !important; 107 | } 108 | .sec_btn { 109 | border: 1px solid #eee; 110 | background: #fbfbfb; 111 | color: #1b2733; 112 | border-radius: 3px; 113 | cursor: pointer; 114 | font-weight: 600; 115 | outline: none; 116 | } 117 | .sec_btn:hover { 118 | color: #1b2733; 119 | background-color: #fff7f7; 120 | } 121 | .sec_btn:focus { 122 | color: #1b2733; 123 | background: #f7ebeb; 124 | } 125 | .tir_btn { 126 | border: 1px solid #eee; 127 | background: #fff; 128 | color: #1b2733; 129 | border-radius: 3px; 130 | cursor: pointer; 131 | outline: none; 132 | } 133 | .tir_btn:hover { 134 | color: #1b2733; 135 | background-color: #ebf4fd; 136 | } 137 | .tir_btn:focus { 138 | color: #1b2733; 139 | background-color: #ebf4fd; 140 | } 141 | .a_pri { 142 | color: #2895F1; 143 | } 144 | .options { 145 | position: absolute; 146 | padding: 7px 6px; 147 | background: #fff; 148 | box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08); 149 | -webkit-box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08); 150 | -moz-box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08); 151 | border-radius: 3px; 152 | z-index: 1; 153 | } 154 | .options ul { 155 | background: #fff; 156 | } 157 | .options ul li { 158 | background: #fff; 159 | } 160 | .options ul li a, 161 | .options ul li label { 162 | background: #fff; 163 | font-size: 14px; 164 | color: #66757f; 165 | padding: 4px 12px 4px 8px; 166 | width: inherit; 167 | display: block; 168 | border-radius: 2px; 169 | } 170 | .options ul li a:hover, 171 | .options ul li label:hover { 172 | /*background: @second_border;*/ 173 | background: #54BBFF; 174 | /*background: #f4f4f4;*/ 175 | /*color: #1b2733;*/ 176 | color: #fff; 177 | } 178 | .options ul li.o_divider { 179 | margin: 5px 0px 5px 0px; 180 | } 181 | .options ul li.menu_divider { 182 | border-top-color: #e6e8eb; 183 | } 184 | input[type="range"] { 185 | -webkit-appearance: none; 186 | width: 100%; 187 | border-radius: 3px; 188 | cursor: pointer; 189 | } 190 | input[type="range"]:focus { 191 | outline: none; 192 | } 193 | input[type="range"]:focus::-ms-thumb { 194 | transform: scale(1.2); 195 | -webkit-transform: scale(1.2); 196 | -moz-transform: scale(1.2); 197 | } 198 | input[type="range"]::-webkit-slider-runnable-track { 199 | width: 100%; 200 | background: rgba(255, 255, 255, 0.5); 201 | background: #fff; 202 | /*background: lightskyblue;*/ 203 | cursor: pointer; 204 | border-radius: 3px; 205 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1); 206 | height: 6px; 207 | } 208 | input[type="range"]::-webkit-slider-thumb { 209 | width: 15px; 210 | height: 15px; 211 | border-radius: 50%; 212 | background: #4080ff; 213 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1); 214 | margin-top: -5px; 215 | margin-left: px; 216 | cursor: pointer; 217 | -webkit-appearance: none; 218 | border: 1px solid #4080ff; 219 | } 220 | input[type="range"]:hover::-webkit-slider-thumb { 221 | transform: scale(1.2); 222 | -webkit-transform: scale(1.2); 223 | } 224 | input[type="range"]::-moz-range-track { 225 | width: 100%; 226 | background: #fff; 227 | /*background: lightskyblue;*/ 228 | cursor: pointer; 229 | border-radius: 3px; 230 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1); 231 | height: 6px; 232 | } 233 | input[type="range"]::-moz-range-thumb { 234 | width: 14px; 235 | height: 14px; 236 | border-radius: 50%; 237 | border: 1px solid #4080ff; 238 | background: #4080ff; 239 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1); 240 | cursor: pointer; 241 | } 242 | input[type="range"]:hover::-moz-range-thumb { 243 | transform: scale(1.2); 244 | -moz-transform: scale(1.2); 245 | } 246 | input[type="range"]::-ms-track { 247 | width: 100%; 248 | background: transparent; 249 | border-color: transparent; 250 | /*border-width: 10px 0px 10px 0px;*/ 251 | color: transparent; 252 | height: 6px; 253 | border-radius: 3px; 254 | z-index: 2; 255 | position: relative; 256 | border: 1px solid; 257 | /*border: 1px solid #ecf0f1;*/ 258 | /*margin-top: -10px;*/ 259 | } 260 | input[type="range"]::-ms-fill-lower { 261 | background: lightskyblue; 262 | border-radius: 3px; 263 | } 264 | input[type="range"]::-ms-fill-upper { 265 | background: #ecf0f1; 266 | border-color: #ecf0f1 !important; 267 | border-radius: 3px; 268 | } 269 | input[type="range"]::-ms-thumb { 270 | width: 12px; 271 | height: 12px; 272 | border-radius: 50%; 273 | background: #4080ff; 274 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1); 275 | margin-top: 0px; 276 | cursor: pointer; 277 | border: 1px solid #4080ff; 278 | } 279 | input[type="file"] { 280 | width: 0.1px; 281 | height: 0.1px; 282 | opacity: 0; 283 | overflow: hidden; 284 | position: absolute; 285 | z-index: -2; 286 | } 287 | .overlay { 288 | position: fixed; 289 | width: 100%; 290 | height: 100%; 291 | top: 0px; 292 | left: 0px; 293 | right: 0px; 294 | bottom: 0px; 295 | z-index: 2; 296 | background: #000; 297 | opacity: 0.5 !important; 298 | } 299 | .hidden_overlay { 300 | position: fixed; 301 | width: 100%; 302 | height: 100%; 303 | top: 0px; 304 | left: 0px; 305 | right: 0px; 306 | bottom: 0px; 307 | z-index: 2; 308 | background: transparent !important; 309 | } 310 | .colored_overlay { 311 | position: fixed; 312 | width: 100%; 313 | height: 100%; 314 | top: 0px; 315 | left: 0px; 316 | right: 0px; 317 | bottom: 0px; 318 | z-index: 2; 319 | background: #eeeeee !important; 320 | } 321 | .overlay-2 { 322 | position: fixed; 323 | width: 100%; 324 | height: 100%; 325 | top: 0px; 326 | left: 0px; 327 | right: 0px; 328 | bottom: 0px; 329 | z-index: 2; 330 | background: transparent; 331 | z-index: 20; 332 | display: none; 333 | } 334 | select { 335 | font-size: 14px; 336 | font-family: 'Open Sans', 'Roboto', Tahoma, arial, sans-serif; 337 | border-radius: 3px; 338 | border: 1px solid #eee; 339 | cursor: pointer; 340 | outline: none; 341 | /*-moz-appearance: none;*/ 342 | /*-webkit-appearance: none;*/ 343 | } 344 | select:focus { 345 | border-color: #56b4ef; 346 | box-shadow: 0px 0px 5px 1px #c8def0; 347 | } 348 | .spinner { 349 | width: 0px; 350 | height: 0px; 351 | background: #eee; 352 | border-radius: 50%; 353 | position: absolute; 354 | display: inline-block; 355 | left: 177px; 356 | top: 20px; 357 | } 358 | .spinner span { 359 | display: block; 360 | height: 10px; 361 | width: 10px; 362 | background: #ddd; 363 | border-radius: 50%; 364 | position: absolute; 365 | top: 0px; 366 | transition: all 0.1s ease-in-out; 367 | } 368 | .spinner span:nth-child(1) { 369 | left: -18px; 370 | animation: bounce 1s ease-in-out infinite; 371 | } 372 | .spinner span:nth-child(2) { 373 | animation: bounce 1s ease-in-out 0.33s infinite; 374 | } 375 | .spinner span:nth-child(3) { 376 | left: 18px; 377 | animation: bounce 1s ease-in-out 0.66s infinite; 378 | } 379 | @keyframes bounce { 380 | 0%, 381 | 75%, 382 | 100% { 383 | transform: translateY(0px); 384 | } 385 | 25% { 386 | transform: translateY(-10px); 387 | } 388 | } 389 | .inst_checkbox { 390 | display: none; 391 | } 392 | .inst_checkbox + label:before { 393 | content: "\f096"; 394 | font-family: "FontAwesome"; 395 | speak: none; 396 | font-weight: normal; 397 | font-style: normal; 398 | font-variant: normal; 399 | text-transform: normal; 400 | line-height: 1; 401 | -webkit-font-smoothing: antialiased; 402 | } 403 | .inst_checkbox:checked + label::before { 404 | content: "\f14a"; 405 | color: #06a3e9; 406 | animation: tick 1s ease-in; 407 | } 408 | .inst_checkbox:disabled + label { 409 | color: #aaa; 410 | } 411 | .inst_checkbox:disabled + label::before { 412 | content: "\f0c8"; 413 | color: #ccc; 414 | } 415 | #hoverdiv { 416 | position: absolute; 417 | background: #333; 418 | padding: 4px 14px; 419 | color: white; 420 | font-size: 13px; 421 | border-radius: 3px; 422 | top: 0px; 423 | left: 0px; 424 | display: none; 425 | z-index: 4; 426 | } 427 | .handy-notify { 428 | position: fixed; 429 | background: #333; 430 | left: 2%; 431 | /*transform: translate(-50%, -50%);*/ 432 | color: #fff; 433 | border-radius: 3px; 434 | padding: 12px 80px 12px 25px; 435 | /*padding: 10px 30px;*/ 436 | font-size: 15px; 437 | cursor: pointer; 438 | text-align: left; 439 | z-index: 3; 440 | top: 105%; 441 | } 442 | .handy-tooltip-after::after { 443 | content: ""; 444 | position: absolute; 445 | width: 8px; 446 | height: 8px; 447 | background: #333; 448 | transform: rotate(45deg); 449 | -webkit-transform: rotate(45deg); 450 | -moz-transform: rotate(45deg); 451 | bottom: -3px; 452 | left: 45%; 453 | z-index: -1; 454 | } 455 | .handy-tooltip-before::before { 456 | content: ""; 457 | position: absolute; 458 | width: 8px; 459 | height: 8px; 460 | background: #333; 461 | transform: rotate(45deg); 462 | -webkit-transform: rotate(45deg); 463 | -moz-transform: rotate(45deg); 464 | top: -3px; 465 | left: 45%; 466 | z-index: -1; 467 | } 468 | .pro_ava_active { 469 | box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08) !important; 470 | -webkit-box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08) !important; 471 | -moz-box-shadow: 0 0 0 1px rgba(99, 114, 130, 0.16), 0 8px 16px rgba(27, 39, 51, 0.08) !important; 472 | border: 3px solid #54BBFF !important; 473 | animation: pro_active 0.1s ease-in-out; 474 | -webkit-animation: pro_active 0.1s ease-in-out; 475 | -moz-animation: pro_active 0.1s ease-in-out; 476 | } 477 | @keyframes pro_active { 478 | 0% { 479 | transform: scale(1); 480 | } 481 | 50% { 482 | transform: scale(0.95); 483 | } 484 | 100% { 485 | transform: scale(1); 486 | } 487 | } 488 | .follow { 489 | background: #1b9be9 !important; 490 | border: 1px solid #1b9be9 !important; 491 | } 492 | .unfollow { 493 | background: #6ae03b !important; 494 | color: white !important; 495 | border: 1px solid #6ae03b !important; 496 | } 497 | .unfollow:focus { 498 | background: #fbf2f2; 499 | } 500 | .overlay_cursor { 501 | cursor: -webkit-zoom-out; 502 | cursor: -moz-zoom-out; 503 | cursor: -ms-zoom-out; 504 | } 505 | .overlay_toggle { 506 | opacity: 0.8 !important; 507 | } 508 | .home_last_mssg { 509 | position: relative; 510 | border-radius: 4px; 511 | background: #fff; 512 | border: 1px solid #f7f5f5; 513 | font-size: 14px; 514 | text-align: center; 515 | padding: 10px; 516 | cursor: default; 517 | } 518 | .home_last_mssg img { 519 | position: relative; 520 | text-align: center; 521 | width: 150px; 522 | /*margin-bottom: 10px;*/ 523 | display: block; 524 | left: 48%; 525 | transform: translate(-50%); 526 | } 527 | .home_last_mssg span { 528 | position: relative; 529 | text-align: center; 530 | display: block; 531 | left: -5px; 532 | /*top: -5px;*/ 533 | } 534 | .modal { 535 | position: absolute; 536 | top: 50%; 537 | left: 50%; 538 | transform: translate(-50%, -50%); 539 | position: fixed !important; 540 | box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 541 | -webkit-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 542 | -moz-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 543 | width: 300px; 544 | border-radius: 3px; 545 | z-index: 2; 546 | background: #fff; 547 | font-size: 14px; 548 | } 549 | .modal .modal_no { 550 | text-align: center; 551 | position: relative; 552 | top: 30px; 553 | } 554 | .modal .modal_no img { 555 | width: 150px; 556 | } 557 | .modal .modal_header { 558 | background: #f9f9f9; 559 | border-bottom: 1px solid #f7f5f5; 560 | padding: 10px; 561 | border-top-left-radius: 3px; 562 | border-top-right-radius: 3px; 563 | } 564 | .modal .modal_header span.title { 565 | font-weight: 600; 566 | } 567 | .modal .modal_middle { 568 | position: relative; 569 | padding: 10px; 570 | } 571 | .modal .modal_bottom { 572 | display: block; 573 | text-align: right; 574 | padding: 9px 5px 12px 5px; 575 | border-top: 1px solid #eee; 576 | margin-top: 0px; 577 | } 578 | .modal .modal_bottom a, 579 | .modal .modal_bottom input[type='submit'] { 580 | display: inline-block; 581 | padding: 4px 10px; 582 | font-weight: 600; 583 | } 584 | .modal_big { 585 | width: 400px; 586 | } 587 | .modal_big .modal_middle { 588 | height: 450px; 589 | width: inherit; 590 | overflow: auto; 591 | padding: 0px !important; 592 | } 593 | .modal_big .modal_middle .modal_main { 594 | position: relative; 595 | padding: 5px 0px; 596 | } 597 | .modal_big .modal_middle .modal_main .modal_items { 598 | margin-bottom: 5px; 599 | position: relative; 600 | padding: 0px 5px 5px 5px; 601 | } 602 | .modal_big .modal_middle .modal_main .modal_items .modal_it_img { 603 | position: relative; 604 | display: inline-block; 605 | height: 45px; 606 | margin-left: 5px; 607 | } 608 | .modal_big .modal_middle .modal_main .modal_items .modal_it_img img { 609 | width: 45px; 610 | height: 45px; 611 | border-radius: 3px; 612 | } 613 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content { 614 | display: inline-block; 615 | position: relative; 616 | width: 83%; 617 | margin-left: 8px; 618 | top: -10px; 619 | } 620 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_it_info { 621 | display: inline-block; 622 | width: 100%; 623 | } 624 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_it_info a.modal_it_username { 625 | font-size: 14px; 626 | color: #1b2733; 627 | font-weight: 600; 628 | display: block; 629 | outline: none; 630 | } 631 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_it_info a.modal_it_username:hover, 632 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_it_info a.modal_it_username:focus { 633 | text-decoration: underline; 634 | } 635 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_it_info span.modal_it_light { 636 | color: #66757f; 637 | font-size: 13px; 638 | } 639 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_ff { 640 | display: inline-block; 641 | position: absolute; 642 | right: 5px; 643 | top: 5px; 644 | text-align: right; 645 | } 646 | .modal_big .modal_middle .modal_main .modal_items .modal_it_content .modal_ff a { 647 | display: inline-block; 648 | padding: 5px 15px; 649 | font-weight: 600; 650 | } 651 | .modal_big .modal_middle .modal_main .modal_items hr { 652 | border-top-color: #eee; 653 | margin-top: 3px; 654 | } 655 | .modal_big .modal_bottom { 656 | margin-top: 1px; 657 | } 658 | .modal_big .modal_bottom a:last-of-type { 659 | margin-right: 10px; 660 | margin-left: 5px; 661 | } 662 | .notes_wrapper { 663 | margin-top: 70px; 664 | } 665 | .index_header { 666 | width: 100%; 667 | height: 50px; 668 | position: fixed; 669 | z-index: 1; 670 | top: 0px; 671 | } 672 | .index_header .right { 673 | float: right; 674 | position: relative; 675 | display: inline-block; 676 | right: 70px; 677 | top: 21px; 678 | } 679 | .index_header .right a { 680 | border: 1px solid #eee; 681 | outline: none; 682 | padding: 4px 16px; 683 | border-radius: 3px; 684 | font-size: 14px; 685 | margin-right: 5px; 686 | color: #3d464d; 687 | background: #fff; 688 | } 689 | .index_header .right a a[href='help'] { 690 | margin-right: 0px; 691 | } 692 | .index_header .right a .index_header > .right > a[href="about"] { 693 | margin-right: 5px; 694 | } 695 | .index_header .right a:hover { 696 | background: #f7f9fa; 697 | } 698 | .index_header .right a:focus { 699 | background: #f7f9fa; 700 | } 701 | .header_loggedin { 702 | background: #fff; 703 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); 704 | position: fixed; 705 | width: 100%; 706 | height: 45px; 707 | background: white; 708 | z-index: 2; 709 | top: 0px; 710 | } 711 | .header_loggedin a { 712 | font-size: 14px; 713 | color: #1b2733; 714 | margin-left: 7px; 715 | padding: 5px 12px; 716 | border-radius: 3px; 717 | display: inline-block; 718 | } 719 | .header_loggedin a:hover { 720 | background: #f7f9fa; 721 | } 722 | .header_loggedin a:focus { 723 | background: #f7f9fa; 724 | } 725 | .header_loggedin .left { 726 | display: inline-block; 727 | position: relative; 728 | top: 8px; 729 | left: 25px; 730 | } 731 | .header_loggedin .right { 732 | display: inline-block; 733 | position: absolute; 734 | top: 8px; 735 | right: 3%; 736 | } 737 | .display_text { 738 | text-align: center; 739 | margin-bottom: 30px; 740 | } 741 | .display_text > span { 742 | font-size: 23px; 743 | color: #0b867a; 744 | } 745 | .log_sign { 746 | position: absolute; 747 | right: 50px; 748 | top: 88px; 749 | } 750 | .log_sign > a { 751 | padding: 5px 18px; 752 | border-radius: 4px; 753 | font-size: 14px; 754 | } 755 | .ha_active { 756 | background: #f7f9fa; 757 | } 758 | .cua { 759 | width: 300px; 760 | position: absolute; 761 | top: 50%; 762 | left: 50%; 763 | transform: translate(-50%, -50%); 764 | } 765 | .cua form > * { 766 | display: block; 767 | margin-bottom: 10px; 768 | width: 92%; 769 | padding: 8px 10px; 770 | font-size: 14px; 771 | } 772 | .cua input[type='submit'] { 773 | width: 99%; 774 | } 775 | .registered { 776 | position: absolute; 777 | background: #fff; 778 | padding: 20px; 779 | width: 300px; 780 | top: 50%; 781 | left: 50%; 782 | transform: translate(-50%, -50%); 783 | font-size: 15px; 784 | border-radius: 5px; 785 | box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.1); 786 | } 787 | .aligner { 788 | position: relative; 789 | width: 614px; 790 | left: 367px; 791 | } 792 | .aligner .user_banner { 793 | text-align: center; 794 | position: relative; 795 | background: white; 796 | padding: 15px; 797 | border: 1px solid #f7f5f5; 798 | border-radius: 4px; 799 | } 800 | .aligner .user_banner .user_buttons { 801 | position: absolute; 802 | right: 15px; 803 | top: 15px; 804 | } 805 | .aligner .user_banner .user_buttons > a { 806 | padding: 5px 15px; 807 | font-size: 14px; 808 | display: inline-block; 809 | margin-right: 5px; 810 | } 811 | .aligner .user_banner .profile_img_div { 812 | margin-top: 10px; 813 | } 814 | .aligner .user_banner .profile_img_div img { 815 | border-radius: 50%; 816 | height: 150px; 817 | width: 150px; 818 | } 819 | .aligner .user_banner .user_info { 820 | margin-top: 10px; 821 | } 822 | .aligner .user_banner .user_info a.user_main_link { 823 | font-weight: 600; 824 | color: #1b2733; 825 | display: block; 826 | font-size: 16px; 827 | } 828 | .aligner .user_banner .user_info .user_no_notes { 829 | color: #66757f; 830 | font-size: 13px; 831 | } 832 | .aligner .user_banner .user_info .user_bio { 833 | text-align: center; 834 | font-size: 14px; 835 | margin-top: 7px; 836 | } 837 | .aligner .user_banner .user_info .no_bio { 838 | font-style: italic; 839 | color: #66757f; 840 | } 841 | .aligner .user_banner .user_info hr { 842 | margin: 25px 0px 10px 0px; 843 | border-top: 1px solid #f5eded; 844 | } 845 | .aligner .user_banner .user_info .user_stats { 846 | text-align: center; 847 | } 848 | .aligner .user_banner .user_info .user_stats > div, 849 | .aligner .user_banner .user_info .user_stats > a { 850 | display: inline-block; 851 | padding: 7px 40px; 852 | text-align: center; 853 | position: relative; 854 | cursor: pointer; 855 | border-radius: 3px; 856 | color: inherit; 857 | } 858 | .aligner .user_banner .user_info .user_stats > div:hover, 859 | .aligner .user_banner .user_info .user_stats > a:hover { 860 | background: #f7f9fa; 861 | } 862 | .aligner .user_banner .user_info .user_stats > div .stat_hg, 863 | .aligner .user_banner .user_info .user_stats > a .stat_hg { 864 | display: block; 865 | font-size: 15px; 866 | font-weight: 600; 867 | } 868 | .aligner .user_banner .user_info .user_stats .stat_disabled { 869 | cursor: text !important; 870 | } 871 | .aligner .user_banner .user_info .user_stats .stat_disabled:hover { 872 | background: #fff; 873 | } 874 | .aligner .filter_notes { 875 | position: relative; 876 | margin-top: 15px; 877 | } 878 | .aligner .filter_notes > input[type="text"] { 879 | font-size: 14px; 880 | padding: 8px; 881 | width: 97%; 882 | } 883 | .aligner .notes { 884 | margin-top: 15px; 885 | margin-bottom: 50px; 886 | } 887 | .note { 888 | background: #fff; 889 | padding: 10px; 890 | display: block; 891 | font-size: 14px; 892 | border: 1px solid #f7f5f5; 893 | border-radius: 3px; 894 | margin-bottom: 10px; 895 | cursor: pointer; 896 | } 897 | .note:hover { 898 | border-color: #eee; 899 | } 900 | .note .note_header { 901 | margin-bottom: 7px; 902 | } 903 | .note .note_header img { 904 | height: 31px; 905 | width: 31px; 906 | border-radius: 50%; 907 | display: inline-block; 908 | } 909 | .note .note_header .note_h_left { 910 | display: inline-block; 911 | margin-left: 5px; 912 | width: 90%; 913 | } 914 | .note .note_header .note_h_left .note_username { 915 | display: block; 916 | color: #1b2733; 917 | font-weight: 600; 918 | position: relative; 919 | top: 2px; 920 | } 921 | .note .note_header .note_h_left .note_time { 922 | font-size: 13px; 923 | color: #66757f; 924 | position: relative; 925 | top: -1px; 926 | } 927 | .note .note_title, 928 | .note .note_content { 929 | color: #1b2733; 930 | } 931 | .note .note_title { 932 | margin-bottom: 4px; 933 | font-weight: 600; 934 | } 935 | .create_note { 936 | width: 380px; 937 | } 938 | .create_note input[type='text'], 939 | .create_note textarea { 940 | padding: 7px !important; 941 | width: 94%; 942 | font-size: 14px; 943 | } 944 | .create_note input[type="text"] { 945 | margin-bottom: 9px; 946 | } 947 | .create_note textarea { 948 | height: 250px; 949 | } 950 | .create_note .c_n_bottom { 951 | margin-top: 1px; 952 | } 953 | .create_note .c_n_bottom input[type='submit'] { 954 | margin-left: 5px; 955 | padding: 5px 10px; 956 | margin-right: 6px; 957 | font-size: 14px; 958 | } 959 | .welcome_div { 960 | position: absolute; 961 | top: 50%; 962 | left: 50%; 963 | transform: translate(-50%, -50%); 964 | } 965 | .welcome_div img { 966 | height: 300px; 967 | } 968 | .error_div { 969 | width: 436px; 970 | } 971 | .error_div .error_info { 972 | margin-bottom: 10px; 973 | width: 100%; 974 | } 975 | .error_div .error_info span { 976 | font-size: 20px; 977 | } 978 | .error_div .error_bottom { 979 | text-align: right; 980 | margin-top: 3px; 981 | } 982 | .error_div .error_bottom a { 983 | display: inline-block; 984 | padding: 4px 15px; 985 | font-weight: 600; 986 | font-size: 14px; 987 | } 988 | .error_div .error_bottom a:last-of-type { 989 | margin-left: 5px; 990 | } 991 | .view_note { 992 | width: 600px; 993 | } 994 | .view_note .v_n_middle .v_n_info { 995 | margin-bottom: 5px; 996 | } 997 | .view_note .v_n_middle .v_n_info img { 998 | width: 40px; 999 | height: 40px; 1000 | border-radius: 50%; 1001 | display: inline-block; 1002 | } 1003 | .view_note .v_n_middle .v_n_info .v_n_left { 1004 | display: inline-block; 1005 | margin-left: 5px; 1006 | position: relative; 1007 | top: -5px; 1008 | } 1009 | .view_note .v_n_middle .v_n_info .v_n_left .v_n_username { 1010 | display: block; 1011 | font-weight: 600; 1012 | font-size: 15px; 1013 | color: inherit; 1014 | } 1015 | .view_note .v_n_middle .v_n_info .v_n_left .v_n_username:hover, 1016 | .view_note .v_n_middle .v_n_info .v_n_left .v_n_username:focus { 1017 | text-decoration: underline; 1018 | } 1019 | .view_note .v_n_middle .v_n_info .v_n_left .v_n_time { 1020 | font-size: 13px; 1021 | color: #66757f; 1022 | position: relative; 1023 | top: -2px; 1024 | } 1025 | .view_note .v_n_middle .v_n_title { 1026 | font-weight: 600; 1027 | display: block; 1028 | margin-bottom: 3px; 1029 | font-size: 15px; 1030 | outline: none; 1031 | } 1032 | .view_note .v_n_middle .v_n_content { 1033 | display: block; 1034 | outline: none; 1035 | } 1036 | .view_note .v_n_middle .v_n_title[contenteditable='true'], 1037 | .view_note .v_n_middle .v_n_content[contenteditable='true'] { 1038 | border: 1px solid #54BBFF; 1039 | padding: 5px 5px !important; 1040 | border-radius: 2px; 1041 | } 1042 | .view_note .v_n_bottom a { 1043 | margin-left: 5px; 1044 | } 1045 | .view_note .v_n_bottom .v_n_cancel { 1046 | margin-right: 5px; 1047 | } 1048 | .view_note .v_n_bottom .v_n_int { 1049 | position: absolute; 1050 | display: flex; 1051 | color: #66757f; 1052 | bottom: 10px; 1053 | left: 10px; 1054 | } 1055 | .view_note .v_n_bottom .v_n_int span.like_unlike { 1056 | display: inline-block; 1057 | height: 24px; 1058 | cursor: pointer; 1059 | transition: all 0.1s ease-in-out; 1060 | } 1061 | .view_note .v_n_bottom .v_n_int span.like_unlike:hover { 1062 | color: #1b2733; 1063 | transform: scale(1.1); 1064 | } 1065 | .view_note .v_n_bottom .v_n_int .like_unlike_disabled { 1066 | cursor: not-allowed !important; 1067 | pointer-events: none !important; 1068 | color: #d8c3c3 !important; 1069 | } 1070 | .content_editor { 1071 | margin-top: 5px; 1072 | } 1073 | .edit { 1074 | position: relative; 1075 | width: 400px; 1076 | left: 490px; 1077 | font-size: 14px; 1078 | } 1079 | .edit .edit_animation > * { 1080 | display: block; 1081 | width: 100%; 1082 | margin-top: 15px; 1083 | } 1084 | .edit .eb_btns { 1085 | text-align: right; 1086 | } 1087 | .edit .eb_btns a { 1088 | display: inline-block; 1089 | margin-right: 23px; 1090 | padding: 5px 20px; 1091 | } 1092 | .edit .eb_btns form { 1093 | display: inline-block; 1094 | } 1095 | .edit .eb_btns form .avatar_span { 1096 | display: inline-block; 1097 | padding: 5px 10px 6px 10px; 1098 | margin-right: 7px; 1099 | } 1100 | .edit .edit_span { 1101 | color: #66757f; 1102 | display: block; 1103 | position: relative; 1104 | font-size: 14px; 1105 | margin-bottom: 5px; 1106 | } 1107 | .edit .edit_info img { 1108 | width: 35px; 1109 | height: 35px; 1110 | border-radius: 50%; 1111 | position: relative; 1112 | display: inline-block; 1113 | } 1114 | .edit .edit_info span { 1115 | position: relative; 1116 | top: -12px; 1117 | left: 8px; 1118 | font-size: 18px; 1119 | font-weight: 600; 1120 | } 1121 | .edit .e_joined { 1122 | text-align: right; 1123 | width: 94%; 1124 | } 1125 | .edit input[type='text'], 1126 | .edit input[type='email'], 1127 | .edit textarea { 1128 | width: 90%; 1129 | padding: 8px; 1130 | font-size: 14px; 1131 | } 1132 | .edit textarea { 1133 | height: 100px; 1134 | } 1135 | .edit .resend_vl_div { 1136 | text-align: right; 1137 | } 1138 | .edit .resend_vl_div .resend_vl { 1139 | display: inline-block; 1140 | padding: 5px 15px; 1141 | margin-right: 21px; 1142 | } 1143 | .home { 1144 | position: relative; 1145 | width: 607px; 1146 | margin-left: 365px; 1147 | margin-bottom: 60px; 1148 | } 1149 | .home .home_info { 1150 | margin-bottom: 10px; 1151 | background: #fff; 1152 | padding: 14px 10px; 1153 | border: 1px solid #f7f5f5; 1154 | border-radius: 3px; 1155 | font-size: 15px; 1156 | } 1157 | .home .home_info a { 1158 | position: absolute; 1159 | right: 10px; 1160 | padding: 4px 12px; 1161 | top: 10px; 1162 | } 1163 | .page_end { 1164 | padding: 10px; 1165 | text-align: center; 1166 | font-size: 14px; 1167 | margin-top: 10px; 1168 | color: #66757f; 1169 | cursor: pointer; 1170 | border-radius: 3px; 1171 | border: 1px solid #fbfbfb; 1172 | } 1173 | .page_end:hover { 1174 | border: 1px solid #eee; 1175 | } 1176 | .explore .explores { 1177 | display: inline-block; 1178 | width: 615px; 1179 | margin-left: 365px; 1180 | } 1181 | .explore .explores div.explores_list { 1182 | position: relative; 1183 | background: #fff; 1184 | padding: 10px; 1185 | border-radius: 3px; 1186 | border: 1px solid #f7f5f5; 1187 | margin-bottom: 10px; 1188 | } 1189 | .explore .explores div.explores_list:hover { 1190 | border-color: #eee; 1191 | } 1192 | .explore .explores div.explores_list .exl_main img { 1193 | width: 45px; 1194 | height: 45px; 1195 | border-radius: 50%; 1196 | } 1197 | .explore .explores div.explores_list .exl_main .exl_content { 1198 | display: inline-block; 1199 | position: relative; 1200 | top: -8px; 1201 | margin-left: 7px; 1202 | } 1203 | .explore .explores div.explores_list .exl_main .exl_content a.exl_username { 1204 | color: #1b2733; 1205 | font-size: 14px; 1206 | font-weight: 600; 1207 | } 1208 | .explore .explores div.explores_list .exl_main .exl_content div.exl_desc { 1209 | color: #66757f; 1210 | } 1211 | .explore .explores div.explores_list .exl_main .exl_content div.exl_desc span.exl_desc_sep { 1212 | margin-left: 5px; 1213 | margin-right: 5px; 1214 | } 1215 | .explore .explores div.explores_list .exl_ff { 1216 | position: absolute; 1217 | right: 15px; 1218 | top: 25px; 1219 | } 1220 | .explore .explores div.explores_list .exl_ff a { 1221 | padding: 5px 20px; 1222 | font-size: 14px; 1223 | } 1224 | .prompt { 1225 | position: fixed; 1226 | top: 50%; 1227 | left: 50%; 1228 | transform: translate(-50%, -50%); 1229 | z-index: 2; 1230 | background: #fff; 1231 | width: 400px; 1232 | border-radius: 3px; 1233 | box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 1234 | -webkit-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 1235 | -moz-box-shadow: 0 0 18px rgba(27, 31, 35, 0.4); 1236 | } 1237 | .prompt .prompt-top { 1238 | padding: 10px 10px; 1239 | background: #f6f7f9; 1240 | border-top-left-radius: 3px; 1241 | border-top-right-radius: 3px; 1242 | border-bottom: 1px solid #eee; 1243 | } 1244 | .prompt .prompt-top .prompt-title { 1245 | font-size: 15px; 1246 | font-weight: 600; 1247 | } 1248 | .prompt .prompt-top span:last-of-type { 1249 | display: inline-block; 1250 | position: absolute; 1251 | right: 7px; 1252 | top: 7px; 1253 | padding: 2px; 1254 | color: #66757f; 1255 | height: 20px; 1256 | cursor: pointer; 1257 | border: 1px solid #f6f7f9; 1258 | border-radius: 3px; 1259 | } 1260 | .prompt .prompt-top span:last-of-type:hover { 1261 | color: #1b2733; 1262 | border: 1px solid #e2dbdb; 1263 | } 1264 | .prompt .prompt-top span:last-of-type i { 1265 | font-size: 20px; 1266 | position: relative; 1267 | left: 1px; 1268 | } 1269 | .prompt .prompt-middle { 1270 | padding: 4px 10px; 1271 | font-size: 14px; 1272 | margin-top: 11px; 1273 | margin-bottom: 10px; 1274 | position: relative; 1275 | } 1276 | .prompt .prompt-middle span { 1277 | display: inline-block; 1278 | position: relative; 1279 | } 1280 | .prompt .prompt-bottom { 1281 | padding: 8px 10px 8px 10px; 1282 | border-top: 1px solid #eee; 1283 | text-align: right; 1284 | } 1285 | .prompt .prompt-bottom a { 1286 | display: inline-block; 1287 | padding: 4px 12px; 1288 | font-weight: 600; 1289 | } 1290 | .prompt .prompt-bottom a:last-of-type { 1291 | margin-left: 5px; 1292 | } 1293 | .goto { 1294 | position: absolute; 1295 | right: 7px; 1296 | top: 6px; 1297 | height: 28px; 1298 | } 1299 | .goto .goto_link .goto_label { 1300 | position: relative; 1301 | top: -7px; 1302 | right: 2px; 1303 | font-weight: 600; 1304 | } 1305 | .goto .goto_link span.show_more { 1306 | display: inline-block; 1307 | height: 24px; 1308 | border: 1px solid #f9f9f9; 1309 | border-radius: 2px; 1310 | cursor: pointer; 1311 | color: #66757f; 1312 | transition: all 0.1s ease-in-out; 1313 | } 1314 | .goto .goto_link span.show_more:focus { 1315 | color: #1b2733; 1316 | } 1317 | .goto .goto_options { 1318 | right: -2px; 1319 | top: 35px; 1320 | } 1321 | .goto .goto_options::before { 1322 | content: ""; 1323 | background: white; 1324 | width: 11px; 1325 | height: 11px; 1326 | top: -6px; 1327 | position: absolute; 1328 | border-top: 1px solid #DCDFE1; 1329 | transform: rotate(45deg); 1330 | border-left: 1px solid #DCDFE1; 1331 | right: 9px; 1332 | z-index: -1; 1333 | } 1334 | .deactivate { 1335 | width: 500px; 1336 | } 1337 | .deactivate .deactivate_title { 1338 | font-weight: 600; 1339 | display: block; 1340 | margin-bottom: 5px; 1341 | } 1342 | .deactivate .deactivate_btn { 1343 | display: block; 1344 | margin-top: 11px; 1345 | text-align: right; 1346 | } 1347 | .deactivate .deactivate_btn a { 1348 | display: inline-block; 1349 | padding: 4px 10px; 1350 | } 1351 | --------------------------------------------------------------------------------