build: initial commit with React
This commit is contained in:
parent
27ad8f9bb4
commit
ff0140134f
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -0,0 +1,18 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: 'airbnb',
|
||||
parser: 'babel-eslint',
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
globals: {
|
||||
shallow: false,
|
||||
render: false,
|
||||
mount: false
|
||||
},
|
||||
plugins: ['react', 'prefer-object-spread', 'compat'],
|
||||
rules: {
|
||||
'max-len': ['error', 120],
|
||||
'react/jsx-filename-extension': [1, { 'extensions': ['.js'] }]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
|
@ -0,0 +1,21 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.renderWhitespace": "boundary",
|
||||
"files.encoding": "utf8",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"editor.rulers": [
|
||||
80,
|
||||
120
|
||||
],
|
||||
"search.exclude": {
|
||||
"build/**": true,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
# tildetv-frontend
|
||||
|
||||
> A web player for all videos shared within tilde.town. Sit back and relax.
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
|
||||
|
||||
## Build Setup
|
||||
|
||||
A nodejs >= 8.0.0 setup with [yarn](https://yarnpkg.com/) is recommended.
|
||||
|
||||
``` bash
|
||||
# install dependencies
|
||||
yarn
|
||||
|
||||
# starts the development server
|
||||
yarn start
|
||||
|
||||
# bundles the app into static files for production
|
||||
yarn build
|
||||
|
||||
# starts the test runner
|
||||
yarn test
|
||||
```
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "tildetv-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://tilde.town/~resir014/tildetv",
|
||||
"dependencies": {
|
||||
"axios": "^0.17.1",
|
||||
"normalize.css": "^7.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-paginate": "^5.0.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-scripts": "1.0.17",
|
||||
"styled-components": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "react-scripts build",
|
||||
"commit": "git-cz",
|
||||
"eject": "react-scripts eject",
|
||||
"start": "react-scripts start",
|
||||
"test": "react-scripts test --env=jsdom"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"commitizen": "^2.9.6",
|
||||
"cz-conventional-changelog": "^2.1.0",
|
||||
"eslint": "^4.13.1",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-plugin-compat": "^2.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-prefer-object-spread": "^1.2.1",
|
||||
"eslint-plugin-react": "^7.5.1"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"short_name": "tildetv",
|
||||
"name": "tildetv",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
[
|
||||
{
|
||||
"contentWarnings": [
|
||||
"Trains"
|
||||
],
|
||||
"description": "One of my favourite videos, a relaxing minute by minute train journey from Bergen to Oslo",
|
||||
"title": "BergensBanen minutt for minutt HD (Full video)",
|
||||
"addedtime": 1492970809,
|
||||
"tags": [
|
||||
"SlowTv",
|
||||
"Train",
|
||||
"Norway"
|
||||
],
|
||||
"youtubelink": "https://www.youtube.com/watch?v=z7VYVjR_nwE",
|
||||
"id": {
|
||||
"v": [
|
||||
"z7VYVjR_nwE"
|
||||
]
|
||||
},
|
||||
"user": "karlen"
|
||||
},
|
||||
{
|
||||
"contentWarnings": [],
|
||||
"description": "A must watch even if you don't like sports. Jon Bois from SB Nation uses some neat statistics to figure out if baseballer Barry Bonds would still do well, even without carrying a bat.",
|
||||
"title": "What if Barry Bonds had played without a baseball bat? | Chart Party",
|
||||
"addedtime": 1492256083,
|
||||
"tags": [
|
||||
"sports",
|
||||
"statistics"
|
||||
],
|
||||
"youtubelink": "https://www.youtube.com/watch?v=JwMfT2cZGHg",
|
||||
"id": {
|
||||
"v": [
|
||||
"JwMfT2cZGHg"
|
||||
]
|
||||
},
|
||||
"user": "resir014"
|
||||
},
|
||||
{
|
||||
"contentWarnings": [],
|
||||
"description": "A parody talk researching the turing-completeness of PowerPoint.",
|
||||
"title": "On The Turing Completeness of PowerPoint (SIGBOVIK)",
|
||||
"addedtime": 14924141943,
|
||||
"tags": [],
|
||||
"youtubelink": "https://www.youtube.com/watch?v=uNjxe8ShM-8",
|
||||
"id": {
|
||||
"v": [
|
||||
"uNjxe8ShM-8"
|
||||
]
|
||||
},
|
||||
"user": "resir014"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import AppContainer from './containers/App';
|
||||
|
||||
import Home from './pages/Home';
|
||||
import Videos from './pages/Videos';
|
||||
import Howto from './pages/Howto';
|
||||
import Playground from './pages/Playground';
|
||||
|
||||
const Root = () => (
|
||||
<AppContainer>
|
||||
<Switch>
|
||||
<Route path="/" exact component={Home} />
|
||||
<Route path="/videos" component={Videos} />
|
||||
<Route path="/howto" component={Howto} />
|
||||
{process.env.NODE_ENV !== 'production' && <Route path="/playground" component={Playground} />}
|
||||
</Switch>
|
||||
</AppContainer>
|
||||
);
|
||||
|
||||
export default Root;
|
|
@ -0,0 +1,12 @@
|
|||
/* eslint-env node, jest */
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Root from './Root';
|
||||
|
||||
describe('Root', () => {
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Root />, div);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.section`
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-size: .8rem;
|
||||
line-height: 1.4;
|
||||
background-color: var(--code-background-color);
|
||||
border-radius: .25rem;
|
||||
|
||||
pre {
|
||||
margin-bottom: 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Wrap a code block inside a React component.
|
||||
*/
|
||||
const CodeBlock = ({ code }) => (
|
||||
<Wrapper>
|
||||
<pre>{code}</pre>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
CodeBlock.propTypes = {
|
||||
/** A template string containing the code we want to include. */
|
||||
code: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default CodeBlock;
|
|
@ -0,0 +1,31 @@
|
|||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
/**
|
||||
* Wrap content inside a container to wrap them inside a fixed-width or fluid container.
|
||||
*/
|
||||
const Container = ({ children, className }) => (
|
||||
<div className={className}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
Container.propTypes = {
|
||||
/** Child nodes of the container. */
|
||||
children: PropTypes.node.isRequired,
|
||||
/** Any additional classes to put inside the container. */
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
Container.defaultProps = {
|
||||
className: null,
|
||||
};
|
||||
|
||||
export default styled(Container)`
|
||||
max-width: ${props => (props.fluid ? '100%' : '48em')};
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
`;
|
|
@ -0,0 +1,7 @@
|
|||
import * as React from 'react';
|
||||
|
||||
const Footer = () => (
|
||||
<div>Footer</div>
|
||||
);
|
||||
|
||||
export default Footer;
|
|
@ -0,0 +1,72 @@
|
|||
import * as React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const SiteHeader = styled.header`
|
||||
padding: 1rem 0;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const SiteNavigation = styled.nav`
|
||||
margin: 1.5rem 0;
|
||||
|
||||
a,
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const SiteBrand = styled.h1`
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-weight: 300;
|
||||
font-size: 2.5rem;
|
||||
|
||||
span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const SiteTagline = styled.span`
|
||||
font-size: 1.25rem;
|
||||
font-weight: 300;
|
||||
`;
|
||||
|
||||
const SiteNavigationLink = styled(NavLink)`
|
||||
display: inline-block;
|
||||
margin: 0 1rem;
|
||||
|
||||
&.router-link--active {
|
||||
color: inherit;
|
||||
}
|
||||
`;
|
||||
|
||||
const Home = () => (
|
||||
<SiteHeader>
|
||||
<SiteBrand>tilde<span>tv</span></SiteBrand>
|
||||
<SiteTagline>sit back and relax</SiteTagline>
|
||||
<SiteNavigation>
|
||||
<SiteNavigationLink exact to="/" className="router-link" activeClassName="router-link--active">
|
||||
about
|
||||
</SiteNavigationLink>
|
||||
<SiteNavigationLink to="/videos" className="router-link" activeClassName="router-link--active">
|
||||
{'this week\'s playlist'}
|
||||
</SiteNavigationLink>
|
||||
<SiteNavigationLink to="/howto" className="router-link" activeClassName="router-link--active">
|
||||
add videos
|
||||
</SiteNavigationLink>
|
||||
{process.env.NODE_ENV !== 'production' &&
|
||||
<SiteNavigationLink to="/playground" className="router-link" activeClassName="router-link--active">
|
||||
component playground
|
||||
</SiteNavigationLink>}
|
||||
</SiteNavigation>
|
||||
</SiteHeader>
|
||||
);
|
||||
|
||||
export default Home;
|
|
@ -0,0 +1,21 @@
|
|||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
|
||||
const Video = ({ video }) => (
|
||||
<div>{JSON.stringify(video)}</div>
|
||||
);
|
||||
|
||||
Video.propTypes = {
|
||||
video: PropTypes.shape({
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
user: PropTypes.string,
|
||||
addedtime: PropTypes.number,
|
||||
tags: PropTypes.arrayOf(PropTypes.string),
|
||||
youtubelink: PropTypes.string,
|
||||
id: PropTypes.object,
|
||||
contentWarnings: PropTypes.arrayOf(PropTypes.string),
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default Video;
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Header from '../components/Header';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const AppContainer = ({ children }) => (
|
||||
<Wrapper>
|
||||
<Header />
|
||||
{children}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
AppContainer.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default AppContainer;
|
|
@ -0,0 +1,28 @@
|
|||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Container from '../components/Container';
|
||||
|
||||
const Wrapper = styled.main`
|
||||
margin: 2rem 0;
|
||||
`;
|
||||
|
||||
const PageContainer = ({ children, isFluid }) => (
|
||||
<Wrapper role="main">
|
||||
<Container fluid={isFluid}>
|
||||
<article>{children}</article>
|
||||
</Container>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
PageContainer.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
isFluid: PropTypes.bool,
|
||||
};
|
||||
|
||||
PageContainer.defaultProps = {
|
||||
isFluid: false,
|
||||
};
|
||||
|
||||
export default PageContainer;
|
|
@ -0,0 +1,70 @@
|
|||
import * as React from 'react';
|
||||
import * as axios from 'axios';
|
||||
// import styled from 'styled-components';
|
||||
|
||||
import Video from '../components/Video';
|
||||
|
||||
class VideosContainer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
fetched: false,
|
||||
videos: [],
|
||||
errors: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.fetchVideos();
|
||||
}
|
||||
|
||||
fetchVideos() {
|
||||
const baseUrl = process.env.NODE_ENV !== 'production'
|
||||
? '/sample-api'
|
||||
: '/~karlen/tv';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: baseUrl,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
});
|
||||
|
||||
api.get('/videos.json').then((res) => {
|
||||
this.setState({
|
||||
fetched: true,
|
||||
videos: res.data,
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.setState({
|
||||
fetched: true,
|
||||
errors: e,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderLoading = () => (
|
||||
<div>Loading...</div>
|
||||
);
|
||||
|
||||
renderVideos = videos => (
|
||||
<div>
|
||||
<h2>we have {videos.length} video(s) for you this week</h2>
|
||||
{videos.map(video => <Video key={video.id.v[0]} video={video} />)}
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
if (this.state.fetched) {
|
||||
return this.state.errors.length === 0
|
||||
? this.renderVideos(this.state.videos)
|
||||
: null;
|
||||
}
|
||||
return this.renderLoading();
|
||||
}
|
||||
}
|
||||
|
||||
export default VideosContainer;
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import 'normalize.css';
|
||||
import './styles/index.css';
|
||||
import Root from './Root';
|
||||
|
||||
const wrapped = (
|
||||
<BrowserRouter>
|
||||
<Root />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
ReactDOM.render(wrapped, document.getElementById('root'));
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
||||
<g fill="#61DAFB">
|
||||
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
||||
<circle cx="420.9" cy="296.5" r="45.7"/>
|
||||
<path d="M520.5 78.1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,32 @@
|
|||
import * as React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Page from '../containers/Page';
|
||||
|
||||
const Home = () => (
|
||||
<Page>
|
||||
<h1>welcome to tildetv</h1>
|
||||
<p>
|
||||
tildetv is an alternative way to enjoy visual media on the internet. it's a playlist of YouTube videos{' '}
|
||||
discovered and curated by{' '}
|
||||
<a href="https://tilde.town" target="_blank" rel="noopener noreferrer">
|
||||
tilde.town members
|
||||
</a>{' '}
|
||||
from a variety of different categories across the site, and are available here for everyone's viewing{' '}
|
||||
pleasure.
|
||||
</p>
|
||||
|
||||
<h2>features</h2>
|
||||
<ul>
|
||||
<li>simple, intuitive <Link href="/videos" to="/videos">web viewer</Link> (with tags & content warnings)</li>
|
||||
<li><Link href="/howto" to="/howto">add videos to the playlist</Link> using a simple command line</li>
|
||||
<li>
|
||||
<a href="https://tilde.town/~karlen/tv/videos.json" target="_blank" rel="noopener noreferrer">
|
||||
JSON datadump
|
||||
</a>, for ~town members who want to develop their own, in-console client
|
||||
</li>
|
||||
<li>videos are reset every week, so you're bound to see something fresh!</li>
|
||||
</ul>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default Home;
|
|
@ -0,0 +1,34 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import Container from '../components/Container';
|
||||
import CodeBlock from '../components/CodeBlock';
|
||||
|
||||
const cli = `$ ~karlen/tv/addtotv -h
|
||||
|
||||
Usage: addtotv [options] arg
|
||||
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
-l LINK, --link=LINK URL to youtube video quoted. e.g. -l
|
||||
"https://www.youtube.com/watch?v=z7VYVjR_nwE"
|
||||
-t TAGS, --tags=TAGS Tags, comma separated and quoted. e.g. -t "Train, Ice,
|
||||
cold"
|
||||
-c CW, --contentwarning=CW
|
||||
Content warning, comma separated and quoted. e.g. -c
|
||||
"Doggo, pupper, Joyce"
|
||||
-d DESC, --description=DESC
|
||||
Description of the video, quoted. e.g. "A real
|
||||
angerery pupper writing about pubs"`;
|
||||
|
||||
const Home = () => (
|
||||
<Container>
|
||||
<h1>adding stuff to tildetv</h1>
|
||||
<p>
|
||||
to add videos to tildetv you can use the command-line tool provided by{' '}
|
||||
<a href="https://tilde.town/~karlen">~karlen</a>.
|
||||
</p>
|
||||
<CodeBlock code={cli} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default Home;
|
|
@ -0,0 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import Page from '../containers/Page';
|
||||
|
||||
const Playground = () => (
|
||||
<Page>
|
||||
<h1>component playground</h1>
|
||||
<p>test your components here, y'all.</p>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default Playground;
|
|
@ -0,0 +1,11 @@
|
|||
import * as React from 'react';
|
||||
import Page from '../containers/Page';
|
||||
import VideosContainer from '../containers/Videos';
|
||||
|
||||
const Videos = () => (
|
||||
<Page>
|
||||
<VideosContainer />
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default Videos;
|
|
@ -0,0 +1,191 @@
|
|||
/* Variables
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/*
|
||||
* Colours based on the base16 styleguide
|
||||
* https://chriskempson.github.io/base16/
|
||||
*/
|
||||
--base00-background: #181818;
|
||||
--base01-background: #282828;
|
||||
--base02-background: #383838;
|
||||
--base03-background: #585858;
|
||||
--base04-background: #b8b8b8;
|
||||
--base05-background: #d8d8d8;
|
||||
--base06-background: #e8e8e8;
|
||||
--base07-background: #f8f8f8;
|
||||
--base08-background: #ab4642;
|
||||
--base09-background: #dc9656;
|
||||
--base0A-background: #f7ca88;
|
||||
--base0B-background: #a1b56c;
|
||||
--base0C-background: #86c1b9;
|
||||
--base0D-background: #7cafc2;
|
||||
--base0E-background: #ba8baf;
|
||||
--base0F-background: #a16946;
|
||||
|
||||
--base00: #181818;
|
||||
--base01: #282828;
|
||||
--base02: #383838;
|
||||
--base03: #585858;
|
||||
--base04: #b8b8b8;
|
||||
--base05: #d8d8d8;
|
||||
--base06: #e8e8e8;
|
||||
--base07: #f8f8f8;
|
||||
--base08: #ab4642;
|
||||
--base09: #dc9656;
|
||||
--base0A: #f7ca88;
|
||||
--base0B: #a1b56c;
|
||||
--base0C: #86c1b9;
|
||||
--base0D: #7cafc2;
|
||||
--base0E: #ba8baf;
|
||||
--base0F: #a16946;
|
||||
|
||||
/* Base variables */
|
||||
--root-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif;
|
||||
--root-font-size: 16px;
|
||||
--root-line-height: 1.35;
|
||||
|
||||
/* Typography */
|
||||
--body-color: var(--base05);
|
||||
--body-bg: var(--base00-background);
|
||||
--heading-color: var(--base07);
|
||||
--heading-line-height: 1.15;
|
||||
--link-color: var(--base0E);
|
||||
|
||||
/* Other components */
|
||||
--label-color: var(--base01-background);
|
||||
|
||||
/* Code */
|
||||
--code-font-family: Consolas, "Liberation Mono", Menlo, Monaco, Courier New, monospace;
|
||||
--code-color: #bf616a;
|
||||
--code-background-color: var(--base01-background);
|
||||
|
||||
/* Content warnings */
|
||||
--content-warning-label-background: var(--base08-background);
|
||||
--content-warning-label-color: var(--base05);
|
||||
}
|
||||
|
||||
/* Global resets
|
||||
========================================================================== */
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
font-family: var(--root-font-family);
|
||||
font-size: var(--root-font-size);
|
||||
line-height: var(--root-line-height);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--body-bg);
|
||||
color: var(--body-color);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
a strong {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
/* Typography
|
||||
========================================================================== */
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-bottom: .5rem;
|
||||
font-weight: 400;
|
||||
line-height: var(--heading-line-height);
|
||||
color: var(--base07);
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
margin-top: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: var(--base07);
|
||||
}
|
||||
|
||||
ul, ol, dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: var(--code-font-family);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: .25em .5em;
|
||||
font-size: 85%;
|
||||
color: var(--code-color);
|
||||
background-color: var(--code-background-color);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: 0;
|
||||
font-size: 100%;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/* React root entry point. */
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
const getBaseUrl = () => process.env.PUBLIC_URL || undefined;
|
||||
|
||||
export default getBaseUrl;
|
Loading…
Reference in New Issue