Skip to content
Snippets Groups Projects
Verified Commit cd3c8a30 authored by insert's avatar insert
Browse files

First working draft.


Co-authored-by: default avatarrene.vinter@outlook.com <rene.vinter@outlook.com>
parent c24534b6
No related merge requests found
Showing
with 542 additions and 30 deletions
.container {
display: flex;
flex-direction: row;
align-items: center;
flex-shrink: 0;
padding: 0 25px 0;
height: 55px;
background: var(--theme-tertiary);
z-index: 1;
box-shadow: 0 1px 2px rgba(0,0,0,.15),0 0 2px rgba(0,0,0,.1);
.left {
flex-grow: 1;
.logo {
height: 34px;
vertical-align: middle;
filter: var(--logo);
}
}
}
import React, { useContext } from 'react';
import styles from './Navbar.module.scss';
import { ThemeContext } from '../../pages/_app';
export default () => {
const { theme, setTheme } = useContext(ThemeContext);
function toggleTheme() {
setTheme(theme === 'dark' ? 'light' : 'dark');
}
return (
<div className={styles.container}>
<div className={styles.left}>
<img className={styles.logo} src="/logo.svg" draggable={false}/>
</div>
<div className={styles.right}>
<button onClick={toggleTheme}>switch theme</button>
</div>
</div>
);
}
.button {
display: flex;
}
\ No newline at end of file
import React from 'react';
import styles from './Button.module.scss';
// To add:
// different color buttons, e.g. twitch - purple, donate - yellow, etc.
// Text as string
// Link to
// open in new tab, true or false
// with or without icons, also can we use boxicons?
export default () => {
return (
<a className={styles.button} href="#">
Follow
</a>
);
}
.chat {
display: flex;
flex-direction: row;
min-width: 340px; /* This will be the minimum width for resizing chat */
/*max-width: 500px;*/
background: var(--theme-secondary);
iframe {
flex-grow: 1;
}
.resize {
height: 100%;
width: 10px;
background: var(--theme-primary);
cursor: w-resize;
}
&[data-hidden="true"] {
display: none;
}
}
import React, { useContext } from 'react'
import styles from './Chat.module.scss';
import { ThemeContext } from '../../pages/_app';
import { connect } from 'react-redux';
import { TeamState } from './store';
import { Chat } from 'livestreams';
const mapStateToProps = (state: TeamState) => ({
hideChat: state.hideChat
});
export default connect(mapStateToProps, { })((props: { hideChat?: boolean }) => {
let { theme } = useContext(ThemeContext);
return (
<div className={styles.chat} data-hidden={props.hideChat}>
<div className={styles.resize} />
<Chat source="twitch" id="anomaly" darkMode={theme === 'dark'} />
</div>
);
})
import React, { Fragment } from 'react'
import { connect } from 'react-redux';
import Sidebar from './Sidebar';
import Stream from './Stream';
import Chat from './Chat';
import { TeamState, ViewState } from './store';
const mapStateToProps = (state: TeamState) => ({
view: state.view
});
export default connect(mapStateToProps, { })((props: { view: ViewState }) => {
let content;
switch (props.view.mode) {
case 'stream':
content = (
<Fragment>
<Stream />
<Chat />
</Fragment>
);
break;
case 'home':
default:
content = <div><h1>select a stream to get started</h1></div>;
break;
}
return (
<Fragment>
<Sidebar />
{content}
</Fragment>
);
})
.sidebar {
min-width: 305px;
background: var(--theme-secondary);
padding: 10px 15px;
.teamname {
color: #CCCCCC;
font-size: 14px;
font-weight: 600;
}
}
import React from 'react'
import styles from './Sidebar.module.scss'
import { useTeamState } from './store';
const Streamer = (props: { name: string }) => {
return <h1>{ props.name }</h1>
};
export default () => {
const state = useTeamState();
return (
<div className={styles.sidebar}>
<div className={styles.teamname}>
{ state.name }
</div>
<Streamer name="gamer" />
</div>
);
}
.stream {
flex-grow: 1;
display: flex;
flex-direction: column;
background: var(--theme-primary);
min-height: 0;
overflow-y: auto;
.header {
display: flex;
align-items: center;
flex-shrink: 0;
height: 50px;
padding: 0 15px 0;
.container {
display: flex;
flex-direction: row;
flex-grow: 1;
align-items: center;
font-weight: 600;
.clickable {
display: flex;
flex-direction: row;
align-items: center;
text-decoration: none;
color: var(--theme-text-color);
&:hover {
text-decoration: underline;
}
.avatar {
height: 30px;
width: 30px;
background: red;
border-radius: 50%;
margin-right: 8px;
}
.username {
font-size: 14px;
}
}
}
}
.player {
width: 100%;
min-height: 575px;
}
.rewards {
display: flex;
align-items: center;
flex-direction: row;
height: 60px;
flex-shrink: 0;
background: var(--theme-tertiary);
padding: 0 20px 0;
.icon {
fill: red;
margin-right: 10px;
vertical-align: middle;
height: 30px;
}
.description {
font-size: 14px;
color: #AEAEAE;
font-weight: 700;
flex-grow: 1;
}
}
.info {
padding: 15px;
.title {
font-weight: 700;
h1 {
font-size: 30px;
margin: 8px 0;
}
}
.stats {
font-size: 14px;
font-weight: 700;
color: #7C7C7C;
}
.description {
}
}
}
/* remove later */
.button {
display: flex;
padding: 0 10px;
flex-direction: row;
align-items: center;
height: 30px;
background: lightgray;
border-radius: 4px;
text-decoration: none;
color: black;
font-weight: 700;
font-size: 14px;
}
\ No newline at end of file
import React from 'react'
import styles from './Stream.module.scss';
import { Player } from 'livestreams';
import { connect } from 'react-redux';
import { TeamState, useDispatcher } from './store';
const mapStateToProps = (state: TeamState) => ({
view: state.view,
hideChat: state.hideChat,
streamers: state.streamers
});
export default connect(mapStateToProps, { })((props: Pick<TeamState, 'view' | 'hideChat' | 'streamers'>) => {
const dispatch = useDispatcher();
function toggleChat() {
dispatch({
type: 'TOGGLE_CHAT'
});
}
if (props.view.mode !== 'stream')
throw new Error("Unreachable code reached!");
const streamer = props.streamers[props.view.selectedId];
return (
<div className={styles.stream}>
<div className={styles.header}>
<div className={styles.container}>
<a className={styles.clickable} href="https://twitch.tv/test" target="_blank">
<div className={styles.avatar}/>
<div className={styles.username}>
{ streamer.name }
</div>
</a>
</div>
<div className={styles.action}>
<button>subscribe</button>
<button onClick={toggleChat}>{ props.hideChat ? 'show' : 'hide' } chat</button>
</div>
</div>
<Player className={styles.player} source="twitch" id="anomaly" />
<div className={styles.rewards}>
<img className={styles.icon} src="/rewards.svg" draggable={false}/>
<div className={styles.description}>
<span>You aren’t earning points from this channel.<br />To earn points, follow this channel on Twitch.tv </span>
</div>
<div className={styles.right}>
<a href="https://twitch.tv/" className={styles.button}>
Follow
</a>
</div>
</div>
<div className={styles.info}>
<div className={styles.title}>
<h1>I am gaming right now</h1>
</div>
<div className={styles.stats}>
<span>Stream started 1 hour ago • Live on Twitch.tv</span>
</div>
<div className={styles.description} style={{ minHeight: '1000px' }}>long ass description</div>
</div>
</div>
);
})
import { useStore, useDispatch } from "react-redux";
import update from 'immutability-helper';
export type ViewState =
{ mode: 'home' } |
{ mode: 'stream', selectedId: string } |
{ mode: 'multiview', selected: string[] };
export interface TeamState {
view: ViewState,
hideChat?: boolean,
id: string,
name: string,
streamers: {
[key: string]: {
name: string,
source: 'twitch' | 'mixer' | 'youtube',
source_id: string,
}
}
};
type TeamAction = (
{ type: 'TOGGLE_CHAT' }
);
export function teams(state: TeamState = {} as any, action: TeamAction) {
switch (action.type) {
case 'TOGGLE_CHAT':
let newState = update(state, {
hideChat: {
$set: state.hideChat ? false : true
}
});
console.log(state, newState);
return newState;
default:
break;
}
return state;
}
export function useTeamState() {
return useStore().getState() as TeamState;
}
export function useDispatcher() {
return useDispatch() as (action: TeamAction) => void;
}
import { AppProps } from 'next/app'
import { createContext, useState } from 'react';
import './global.scss';
import 'livestreams/dist/index.css';
function fetchFromStorage(key: string, defaultValue: string) {
if (typeof(localStorage) === 'undefined') {
return defaultValue;
} else {
return localStorage.getItem(key) || defaultValue;
}
}
interface ThemeConfig {
theme: string,
setTheme: (theme: string) => void,
}
export const ThemeContext = createContext<ThemeConfig>({} as any);
function App({ Component, pageProps }: AppProps) {
const [ theme, setThemeCurrent ] = useState(fetchFromStorage('theme', 'dark'));
function setTheme(theme: string) {
localStorage.setItem('theme', theme);
setThemeCurrent(theme);
}
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className={'theme-' + theme}>
<Component {...pageProps} />
</div>
</ThemeContext.Provider>
)
}
export default App
import React from 'react'
export default () => {
return (
<main>
<h1>About us</h1>
</main>
);
}
* { /* Font smoothening trick */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility !important;
}
html, body {
margin: 0;
font-family: Helvetica, sans-serif;
}
.theme-dark {
--theme-primary: #222222;
--theme-secondary: #1e1e1e;
--theme-tertiary: #303030;
--theme-text-color: whitesmoke;
--logo: invert(0);
}
.theme-light {
--theme-primary: white;
--theme-secondary: #efeff1;
--theme-tertiary: white;
--theme-text-color: #0e0e10;
--logo: invert(1);
}
import React from 'react'
import Link from 'next/link'
import styles from './test.module.scss';
export default () => {
return (
<main>
<h1 className={styles.header}>Hello from Preact + Typescript</h1>
<Link href="/about">
<a>About</a>
</Link>
</main>
);
}
main.parent {
display: flex;
flex-direction: column;
color: var(--theme-text-color);
width: 100vw;
height: 100vh;
}
.content {
flex-grow: 1;
display: flex;
flex-direction: row;
min-height: 0;
}
import React, { useState, Fragment } from 'react'
import * as Redux from 'react-redux';
import styles from './player.module.scss';
import Navbar from '../components/global/Navbar';
import Content from '../components/player/Content';
import { createStore } from 'redux';
import { teams } from '../components/player/store';
// ? FETCH FROM API
const SAMPLE_DATA = {
view: {
mode: 'stream',
selectedId: '01E7DC6HKP109Q08G1ZS4H3HKJ'
},
hideChat: false,
id: '01E7D9CK852YHZMY5KEPEVWGJH',
name: 'my team',
streamers: {
['01E7DC6HKP109Q08G1ZS4H3HKJ']: {
name: 'Anomaly',
source: 'twitch',
source_id: 'anomaly'
}
}
};
const store = createStore(teams, SAMPLE_DATA as any);
export default () => {
return (
<Redux.Provider store={store}>
<main className={styles.parent}>
<Navbar />
<div className={styles.content}>
<Content />
</div>
</main>
</Redux.Provider>
);
}
.header {
font-size: 3em;
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment