1
0
mirror of https://github.com/mgerb/mywebsite synced 2026-01-11 02:12:53 +00:00

too many changes - load posts now

This commit is contained in:
2016-08-24 00:29:00 -05:00
parent 24ef17ac5b
commit 7b48c79180
26 changed files with 354 additions and 272 deletions

View File

@@ -0,0 +1 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#282a36}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-section,.hljs-link{color:#8be9fd}.hljs-function .hljs-keyword{color:#ff79c6}.hljs,.hljs-subst{color:#f8f8f2}.hljs-string,.hljs-title,.hljs-name,.hljs-type,.hljs-attribute,.hljs-symbol,.hljs-bullet,.hljs-addition,.hljs-variable,.hljs-template-tag,.hljs-template-variable{color:#f1fa8c}.hljs-comment,.hljs-quote,.hljs-deletion,.hljs-meta{color:#6272a4}.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-title,.hljs-section,.hljs-doctag,.hljs-type,.hljs-name,.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}

View File

@@ -1,10 +1,10 @@
.Footer{ .Footer {
height: 5em; height: 5em;
line-height: 5em; line-height: 5em;
background-color: #F1F1F1; background-color: #F1F1F1;
border-top: solid; border-top: solid;
border-width: 1px; border-width: 1px;
border-color: #DADADA; border-color: #DADADA;
font-weight: 300; font-weight: 300;
margin-top: 1em; margin-top: 1em;
} }

View File

@@ -1,22 +1,16 @@
.Preview{ .Preview {
flex: 1; .post + .post {
-webkit-flex: 1; margin-top: 2em;
}
.post + .post{ .date {
margin-top: 2em; margin: 0;
} opacity: 0.7;
font-weight: 300;
.date{ }
margin: 0; .intro {
opacity: 0.7; margin-bottom: 0.5em;
font-weight: 300; }
} p {
font-weight: 300;
.intro{ }
margin-bottom: 0.5em;
}
p{
font-weight: 300;
}
} }

View File

@@ -1,75 +1,64 @@
$transitionDuration: .4s; $transitionDuration: 0.4s;
.Sidebar {
.Sidebar{ background: #fff;
background: #fff; width: 300px;
width: 300px; padding-right: 1em;
padding-right: 1em; padding-left: 1em;
padding-left: 1em; img {
width: 100%;
img{ height: auto;
width: 100%; border-radius: 0.5em;
height: auto;
border-radius: 0.5em;
}
h2{
text-align: center;
margin-bottom: 0.5em;
}
a.toggler{
display: none;
}
@media (max-width: 768px) {
position: absolute;
top: 0;
right: 0;
min-height: 100%;
padding-top: 1em;
transform: translateX(300px);
-webkit-transform: translateX(300px);
transition: transform $transitionDuration ease-out;
-webkit-transition: transform $transitionDuration ease-out;
&.open {
transform: translateX(0px);
-webkit-transform: translateX(0px);
border-left: 1px solid #EAEAEA;
&:before {
content: " ";
display: block;
background: rgba(0,0,0,0.4);
position: absolute;
height: 100%;
width: 100vw;
top: 0;
right: 100%;
}
a.toggler{
transform: rotate(450deg);
}
} }
h2 {
a.toggler{ text-align: center;
display: block; margin-bottom: 0.5em;
width: 2em; }
height: 2em; a.toggler {
display: none;
}
@media (max-width: 768px) {
position: absolute; position: absolute;
left: -3em; top: 0;
top: .5em; right: 0;
cursor: pointer; min-height: 100%;
color: white; padding-top: 1em;
transform: rotate(0deg); transform: translateX(300px);
-webkit-transform: translateX(300px);
transition: transform $transitionDuration ease-out; transition: transform $transitionDuration ease-out;
-webkit-transition: transform $transitionDuration ease-out; -webkit-transition: transform $transitionDuration ease-out;
&.open {
&:hover{ transform: translateX(0px);
color: #3598db; -webkit-transform: translateX(0px);
border-left: 1px solid #EAEAEA;
&:before {
content: " ";
display: block;
background: rgba(0,0,0,0.4);
position: absolute;
height: 100%;
width: 100vw;
top: 0;
right: 100%;
}
a.toggler {
transform: rotate(450deg);
}
}
a.toggler {
display: block;
width: 2em;
height: 2em;
position: absolute;
left: -3em;
top: 0.5em;
cursor: pointer;
color: white;
transform: rotate(0deg);
transition: transform $transitionDuration ease-out;
-webkit-transition: transform $transitionDuration ease-out;
&:hover {
color: #3598db;
}
} }
} }
}
} }

View File

@@ -1,69 +1,82 @@
@import './utils.scss'; @import './utils.scss';
html {
html{
font-family: 'Roboto Slab', serif; font-family: 'Roboto Slab', serif;
} }
body {
body{ max-width: 100%;
position: relative; overflow-x: hidden;
-webkit-font-smoothing: subpixel-antialiased; position: relative;
-webkit-font-smoothing: subpixel-antialiased;
} }
h1,
h1, h2, h3, h4, h5, h6{ h2,
margin: 0; h3,
font-weight: 500; h4,
h5,
h6 {
margin: 0;
font-weight: 500;
} }
p {
p{ font-weight: 300;
font-weight: 300;
} }
*, *,
*:before, *:after,
*:after { *:before {
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
-moz-box-sizing: border-box; -moz-box-sizing: border-box;
box-sizing: border-box; box-sizing: border-box;
} }
a {
a{ background-color: transparent;
background-color: transparent; color: #3598db;
color: #3598db; text-decoration: none;
text-decoration: none; &:hover {
color: #3598db;
&:hover{ }
color: #3598db; }
} code,
pre code{
white-space: pre-wrap;
position: relative;
} }
.Layout { .Layout {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.Footer,
.Header, .Main, .Footer { .Header,
display: flex; .Main {
padding-right: calc(50% - 997px / 2); display: flex;
padding-left: calc(50% - 997px / 2); padding-right: calc(50% - 997px / 2);
&:before, &:after { content: " "; width: 1em; } padding-left: calc(50% - 997px / 2);
} &:after,
&:before {
content: " ";
width: 1em;
}
}
} }
.Main {
.Main{ padding-top: 1em;
padding-top: 1em;
flex: 1;
} }
.Content {
flex: 1;
.Header{ img{
width: 100%; width: 100%;
background: url("../images/header.jpg"); height: auto;
background-size: cover; }
height: 30em; }
border-bottom: solid; .Header {
border-width: 1px; width: 100%;
border-color: #DADADA; background: url("../images/header.jpg");
background-size: cover;
h1{ height: 30em;
text-align: center; border-bottom: solid;
} border-width: 1px;
border-color: #DADADA;
h1 {
text-align: center;
}
} }

View File

@@ -1,8 +1,7 @@
/* Effect 1: Brackets */ /* Effect 1: Brackets */
.link{ .link {
position: relative; position: relative;
&:before {
&:before{
content: ""; content: "";
position: absolute; position: absolute;
width: 100%; width: 100%;
@@ -16,10 +15,9 @@
-webkit-transition: all 0.2s ease-in-out 0s; -webkit-transition: all 0.2s ease-in-out 0s;
transition: all 0.2s ease-in-out 0s; transition: all 0.2s ease-in-out 0s;
} }
&:hover:before {
&:hover:before{
visibility: visible; visibility: visible;
-webkit-transform: scaleX(1); -webkit-transform: scaleX(1);
transform: scaleX(1); transform: scaleX(1);
} }
} }

View File

@@ -16,7 +16,7 @@ import Index from './pages/Index';
class Main extends React.Component{ class Main extends React.Component{
render(){ render(){
return( return(
<div>{React.cloneElement(this.props.children, this.props)}</div> <div>{React.cloneElement(this.props.children, this.props)}</div>
); );
} }
} }
@@ -40,8 +40,8 @@ ReactDOM.render((
<Router history={history}> <Router history={history}>
<Route path="/" component={App}> <Route path="/" component={App}>
<IndexRoute component={Index}/> <IndexRoute component={Index}/>
<Route path="/:page" component={Index}/> <Route path="/:page(/:category)(/:post)" component={Index}/>
</Route> </Route>
</Router> </Router>
</Provider> </Provider>
),document.getElementById('app')); ),document.getElementById('app'));

View File

@@ -4,9 +4,11 @@ import '../../assets/scss/Footer.scss';
export default class Footer extends React.Component{ export default class Footer extends React.Component{
render(){ render(){
return( return(
<div class="Footer">Site created and maintained by Mitchell Gerber</div> <div class="Footer">
); Site created and maintained by Mitchell Gerber
} </div>
);
}
} }

View File

@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
export default class Header extends React.Component{ export default class Header extends React.Component{
render(){ render(){
return( return(
<header class="Header" /> <header class="Header" />
) )
} }
} }

View File

@@ -1,11 +1,20 @@
import React from 'react'; import React from 'react';
import marked from 'marked';
import highlight from 'highlight.js';
marked.setOptions({
header: true,
highlight: (code) => {
return highlight.highlightAuto(code).value;
}
});
export default class Post extends React.Component{ export default class Post extends React.Component{
render(){
render(){ return(
return( <div class="Preview" dangerouslySetInnerHTML={{__html : marked(this.props.content)}}>
<div></div> </div>
); );
} }
} }

View File

@@ -1,39 +1,39 @@
import React from 'react'; import React from 'react';
import Link from 'react-router'; import {Link} from 'react-router';
import '../../assets/scss/Preview.scss'; import '../../assets/scss/Preview.scss';
export default class Preview extends React.Component{ export default class Preview extends React.Component{
insertPosts(posts){ insertPosts(posts){
let elements = []; let elements = [];
for (let i in posts){ for (let i in posts){
elements.push( elements.push(
<div class="post" key={i}> <div class="post" key={i}>
<div class="date">{posts[i].date}</div> <div class="date">
<div class="intro" dangerouslySetInnerHTML={this.decodeHtml(posts[i].title.toString())}></div> {posts[i].date}
<div dangerouslySetInnerHTML={this.decodeHtml(posts[i].intro.toString())}></div> </div>
<p><a class="link" href="#">continue reading...</a></p> <h2 class="intro" >{posts[i].title.toString()}</h2>
</div> <p>{posts[i].intro.toString()}</p>
); <p>
} <Link class="link" to={`/post/${posts[i].category}/${posts[i].filename}`}>
continue reading...
return elements; </Link>
</p>
</div>
);
} }
decodeHtml(html) { return elements;
var txt = document.createElement("textarea"); }
txt.innerHTML = html;
return {__html : txt.value};
}
render(){ render(){
const posts = this.props.posts; const posts = this.props.posts;
return ( return (
<div class="Preview"> <div class="Preview">
{posts.length > 0 ? this.insertPosts(posts): ""} {posts.length > 0 ? this.insertPosts(posts): ""}
</div> </div>
); );
} }
} }

View File

@@ -6,39 +6,61 @@ import '../../assets/scss/Sidebar.scss';
export default class Sidebar extends React.Component{ export default class Sidebar extends React.Component{
constructor(){ constructor(){
super(); super();
this.state = { this.state = {
toggler: "" toggler: ""
}; };
this.onToggle = this.onToggle.bind(this); this.onToggle = this.onToggle.bind(this);
} }
onToggle(){ onToggle(){
let temp = this.state.toggler; let temp = this.state.toggler;
temp = temp === "open" ? "" : "open"; temp = temp === "open" ? "" : "open";
this.setState({ this.setState({
toggler: temp toggler: temp
}); });
} }
render(){ render(){
return( return(
<div class={"Sidebar " + this.state.toggler}> <div class={"Sidebar " + this.state.toggler}>
<a onClick={this.onToggle} class="toggler"><i class="fa fa-2x fa-navicon" aria-hidden="true" /></a> <a onClick={this.onToggle} class="toggler">
<h2>About Me</h2> <i
<img src={me}/> class="fa fa-2x fa-navicon"
<p>My name is Mitchell and I have a passion for software development. I am currently a software engineer and enjoy working on personal projects in my free time.</p> aria-hidden="true" />
</a>
<h2>
About Me
</h2>
<img src={me}/>
<p>
My name is Mitchell and I have a passion for software development. I am currently a software engineer and enjoy working on personal projects in my free time.
</p>
<h3>Contact Me</h3> <h3>
<p><i class="fa fa-envelope" aria-hidden="true"></i><a class="link" href="mailto:mgerb42@gmail.com"> eMail</a></p> Contact Me
<p><i class="fa fa-linkedin-square" aria-hidden="true"></i><a class="link" href="https://www.linkedin.com/in/mitchell-gerber-125391b3" target="_blank"> LinkedIn</a></p> </h3>
<p><i class="fa fa-github" aria-hidden="true"></i><a class="link" href="https://github.com/mgerb" target="_blank"> GitHub</a></p> <p>
<p><i class="fa fa-wpforms" aria-hidden="true"></i><a href="/resume" class="link"> Resume</a></p> <i class="fa fa-envelope" aria-hidden="true"></i>
</div> <a class="link" href="mailto:mgerb42@gmail.com"> eMail</a>
); </p>
} <p>
<i class="fa fa-linkedin-square" aria-hidden="true"></i>
<a class="link" href="https://www.linkedin.com/in/mitchell-gerber-125391b3" target="_blank"> LinkedIn</a>
</p>
<p>
<i class="fa fa-github" aria-hidden="true"></i>
<a class="link" href="https://github.com/mgerb" target="_blank"> GitHub</a>
</p>
<p>
<i class="fa fa-wpforms" aria-hidden="true"> </i>
<a href="/resume" class="link"> Resume</a>
</p>
</div>
);
}
} }

View File

@@ -5,27 +5,46 @@ import Header from '../components/Header';
import Preview from '../components/Preview'; import Preview from '../components/Preview';
import Footer from '../components/Footer'; import Footer from '../components/Footer';
import Sidebar from '../components/Sidebar'; import Sidebar from '../components/Sidebar';
import Post from '../components/Post';
//css //css
import '../../assets/css/normalize.css'; import '../../assets/css/normalize.css';
import '../../assets/scss/main.scss'; import '../../assets/scss/main.scss';
import 'font-awesome/css/font-awesome.min.css'; import 'font-awesome/css/font-awesome.min.css';
import '../../assets/css/dracula.css';
export default class Index extends React.Component{ export default class Index extends React.Component {
componentDidMount(){ componentDidMount() {
this.props.actions.fetchPreview(); this.props.actions.fetchPreview();
this.page = this.props.params.page;
this.page === 'post' ? this.props.actions.fetchPost(this.props.params.category, this.props.params.post) : "";
} }
render(){
return( componentWillReceiveProps(nextProps){
<div class="Layout"> if(this.props.params !== nextProps.params){
<Header/> const params = nextProps.params;
<div class="Main"> this.page = params.page;
<Preview posts={this.props.redux.preview.posts}/>
<Sidebar /> if(typeof params.post !== 'undefined' && typeof params.category !== 'undefined'){
</div> this.props.actions.fetchPost(params.category, params.post);
<Footer/> }
}
}
render() {
return (
<div class="Layout" >
<Header />
<div class="Main">
<div class="Content">
{typeof this.page === 'undefined' ? <Preview posts={this.props.redux.preview.posts} /> : ""}
{this.page === 'post' ? <Post content={this.props.redux.post}/> : ""}
</div> </div>
); <Sidebar />
</div>
<Footer />
</div>
);
} }
} }

View File

@@ -1,20 +1,41 @@
import * as types from "./constants"; import * as types from "./constants";
import marked from 'marked';
function initPreview(posts){ function initPreview(posts) {
return{ return {
type: types.INIT_PREVIEW, type: types.INIT_PREVIEW,
posts posts
} }
} }
function loadPost(post){
return {
type: types.LOAD_POST,
post
}
}
//using redux-thunk we can modify actions before they get called //using redux-thunk we can modify actions before they get called
//in this case we can send the http request here rather in the react component //in this case we can send the http request here rather in the react component
export function fetchPreview(){ export function fetchPreview() {
return (dispatch) => { return (dispatch) => {
return fetch('/public/metadata.json') return fetch('/public/metadata.json')
.then(response => response.json()) .then(response => response.json())
.then(json => { .then(json => {
dispatch(initPreview(json)); dispatch(initPreview(json));
})
.catch(error => {
console.log(error);
});
}
}
export function fetchPost(category, post) {
return (dispatch) => {
return fetch(`/public/posts/${category}/${post}.md`)
.then(response => response.text())
.then(response => {
dispatch(loadPost(response));
}) })
.catch(error => { .catch(error => {
console.log(error); console.log(error);

View File

@@ -1,3 +1,4 @@
//constants //constants
export const INIT_PREVIEW = 'INIT_PREVIEW'; export const INIT_PREVIEW = 'INIT_PREVIEW';
export const FILTER_PREVIEW = 'FILTER_PREVIEW'; export const FILTER_PREVIEW = 'FILTER_PREVIEW';
export const LOAD_POST = 'LOAD_POST';

View File

@@ -1,20 +1,29 @@
//just using one reducer - use combineReducers from redux to modularize things //just using one reducer - use combineReducers from redux to modularize things
import {combineReducers} from 'redux'; import {
import {routerReducer} from 'react-router-redux'; combineReducers
} from 'redux';
import {
routerReducer
} from 'react-router-redux';
//import typs //import typs
import * as types from './constants'; import * as types from './constants';
//defaults - //defaults -
const defaultState = { const defaultState = {
preview: {posts: []}, preview: {
filteredPreview: {posts: []} posts: []
},
filteredPreview: {
posts: []
},
post: ""
}; };
//default reducer //default reducer
function reducer(state = defaultState, action){ function reducer(state = defaultState, action) {
//every reducer gets called when an action is called - we check for the type to modify our state accordingly //every reducer gets called when an action is called - we check for the type to modify our state accordingly
switch (action.type){ switch (action.type) {
case types.INIT_PREVIEW: case types.INIT_PREVIEW:
return Object.assign({}, state, { return Object.assign({}, state, {
preview: Object.assign({}, state.preview, action.posts) preview: Object.assign({}, state.preview, action.posts)
@@ -22,9 +31,14 @@ function reducer(state = defaultState, action){
case types.FILTER_PREVIEW: case types.FILTER_PREVIEW:
return Object.assign({}, state, { return Object.assign({}, state, {
filteredPreview: Object.assign({}, state.filteredPreview, action.posts) filteredPreview: Object.assign({}, state.filteredPreview, action.posts)
});
case types.LOAD_POST:
return Object.assign({}, state, {
post: action.post
}) })
} }
//return present state if no actions get called //return present state if no actions get called
return state; return state;
} }
@@ -34,4 +48,4 @@ const allReducers = combineReducers({
routing: routerReducer routing: routerReducer
}); });
export default allReducers; export default allReducers;

View File

@@ -36,13 +36,12 @@ function parse_dir(dir, folder_name){
} else { } else {
const file = fs.readFileSync(dir+post, 'utf8'); const file = fs.readFileSync(dir+post, 'utf8');
const tokens = marked.lexer(file, null); const tokens = marked.lexer(file, null);
const path = dir + post;
const temp = { const temp = {
path: path.slice(1, path.length), filename: post.slice(0, post.length - 3),
category: folder_name, category: folder_name,
date: post.slice(0, 10), date: post.slice(0, 10),
title: marked('<h2>' + tokens[0].text + '</h2>'), title: tokens[0].text,
intro: marked(tokens[1].text) intro: tokens[1].text
} }
json.posts.push(temp); json.posts.push(temp);
} }

View File

@@ -42,4 +42,4 @@ Now as easy as that sounds, you have a Node web application running on your mach
Express organizes everything nicely in the [Model View Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) software pattern. Folders are split up into views, routes, and public files. If you are implementing a database, you would also create a "models" folder. There is also a folder which contains Node modules. When you use NPM to install new modules this is where they will be located. Express organizes everything nicely in the [Model View Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) software pattern. Folders are split up into views, routes, and public files. If you are implementing a database, you would also create a "models" folder. There is also a folder which contains Node modules. When you use NPM to install new modules this is where they will be located.
Node is a very powerful tool that I plan on exploring more in the future. As development of this site continues, I plan to submit more content similar to this. Node is a very powerful tool that I plan on exploring more in the future. As development of this site continues, I plan to submit more content similar to this.