mirror of
https://github.com/anatolykopyl/variants.git
synced 2026-03-26 12:54:36 +00:00
Initial commit
This commit is contained in:
5
src/client.js
Normal file
5
src/client.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import * as sapper from '@sapper/app';
|
||||
|
||||
sapper.start({
|
||||
target: document.querySelector('#sapper')
|
||||
});
|
||||
40
src/routes/_error.svelte
Normal file
40
src/routes/_error.svelte
Normal file
@@ -0,0 +1,40 @@
|
||||
<script>
|
||||
export let status;
|
||||
export let error;
|
||||
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1, p {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.8em;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
h1 {
|
||||
font-size: 4em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>{status}</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>{status}</h1>
|
||||
|
||||
<p>{error.message}</p>
|
||||
|
||||
{#if dev && error.stack}
|
||||
<pre>{error.stack}</pre>
|
||||
{/if}
|
||||
3
src/routes/_layout.svelte
Normal file
3
src/routes/_layout.svelte
Normal file
@@ -0,0 +1,3 @@
|
||||
<main>
|
||||
<slot></slot>
|
||||
</main>
|
||||
164
src/routes/index.svelte
Normal file
164
src/routes/index.svelte
Normal file
@@ -0,0 +1,164 @@
|
||||
<script>
|
||||
let groupTask = false;
|
||||
let name = "";
|
||||
let memberCount = 10;
|
||||
|
||||
function createRoom() {
|
||||
const url=`http://localhost:3000/api/create/?g=${groupTask}&n=${name}&c=${memberCount}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Varian.cc</title>
|
||||
</svelte:head>
|
||||
|
||||
<greeting>
|
||||
<h1>varian.cc</h1>
|
||||
<div class="startbtns">
|
||||
<div>
|
||||
<h2>Создать работу</h2>
|
||||
<div class="inputs">
|
||||
<div class="toggle">
|
||||
По вариантам
|
||||
<label class="switch">
|
||||
<input type="checkbox" bind:checked={groupTask}>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
По бригадам
|
||||
</div>
|
||||
<div>
|
||||
Количество {#if groupTask}бригад{:else}вариантов{/if}:
|
||||
<input type="number" id="varCount" name="варианты" min="2" max="50" bind:value={memberCount}>
|
||||
</div>
|
||||
<div>
|
||||
Название работы:<br>
|
||||
<input type="text" size="45" placeholder="Лабораторная работа по физике №3" bind:value={name}>
|
||||
</div>
|
||||
<input type="button" value="Создать" on:click={createRoom}>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Присоедениться к работе</h2>
|
||||
<div class="inputs">
|
||||
<div>
|
||||
Код подключения:<br>
|
||||
<input type="text" size="45" placeholder="43d0505c-d695-4323-9140-5d7744ec95e7">
|
||||
</div>
|
||||
<input type="button" value="Подключиться" on:click={connect}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</greeting>
|
||||
|
||||
<style>
|
||||
greeting {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.startbtns {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.startbtns > div {
|
||||
border-radius: 8px;
|
||||
box-shadow: lightgrey 0px 5px 20px;
|
||||
width: 30vw;
|
||||
height: 30vw;
|
||||
padding: 2vw;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: xx-large;
|
||||
font-weight: 100;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.inputs {
|
||||
display: flex;
|
||||
height: 75%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.switch {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #2196F3;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #f57323;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #f57323;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
100
src/routes/room.svelte
Normal file
100
src/routes/room.svelte
Normal file
@@ -0,0 +1,100 @@
|
||||
<script>
|
||||
import queryString from "query-string";
|
||||
|
||||
let parsed = {};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
parsed = queryString.parse(window.location.search);
|
||||
}
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let room;
|
||||
|
||||
onMount(async () => {
|
||||
var url = `http://localhost:3000/api/join/?id=${parsed.id}`;
|
||||
const res = await fetch(url);
|
||||
room = await res.json();
|
||||
console.log(room);
|
||||
});
|
||||
|
||||
function selectVar(selected) {
|
||||
if (!room.teams[selected]) {
|
||||
room.teams[selected] = [];
|
||||
}
|
||||
room.teams[selected][room.teams[selected].length] = "Толя";
|
||||
console.log(room.teams[selected]);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title></title>
|
||||
</svelte:head>
|
||||
|
||||
<list>
|
||||
{#if room}
|
||||
<h1>{room.name}</h1>
|
||||
<ol>
|
||||
{#each room.teams as _, i}
|
||||
<li class={room.group==="true" ? "group" : "individual"} on:click={() => selectVar(i)}>
|
||||
{#each room.teams[i] as name}
|
||||
<span class="name">{name}</span>
|
||||
{/each}
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
{:else}
|
||||
Loading...
|
||||
{/if}
|
||||
</list>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
list {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ol {
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
list-style: none;
|
||||
counter-reset: item;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
li {
|
||||
text-align: left;
|
||||
border-bottom: solid lightblue 1px;
|
||||
counter-increment: item;
|
||||
padding: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
background-color: lightcoral;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
li:before {
|
||||
margin-right: 10px;
|
||||
content: counter(item);
|
||||
border-radius: 100%;
|
||||
color: white;
|
||||
width: 1.2em;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.individual:before {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
.group:before {
|
||||
background-color: #f57323;
|
||||
}
|
||||
</style>
|
||||
68
src/server.js
Normal file
68
src/server.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import compression from 'compression';
|
||||
const dotenv = require('dotenv');
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import * as sapper from '@sapper/server';
|
||||
const MongoClient = require('mongodb');
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
|
||||
dotenv.config();
|
||||
const app = polka();
|
||||
|
||||
MongoClient.connect(process.env.DB_CONNECTION, { useUnifiedTopology: true })
|
||||
.then(client => {
|
||||
console.log('Connected to Database')
|
||||
const db = client.db('variants');
|
||||
const rooms = db.collection('rooms');
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
res.redirect = location => {
|
||||
let str = `Redirecting to ${location}`;
|
||||
res.writeHead(302, {
|
||||
Location: location,
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': str.length,
|
||||
});
|
||||
res.end(str);
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/api/create/', (req, res) => {
|
||||
console.log(req.query);
|
||||
const id = uuidv4();
|
||||
let teams = [];
|
||||
for (let i = 0; i < req.query.c; i++) {
|
||||
teams[i] = [];
|
||||
}
|
||||
rooms.insertOne({
|
||||
"id": id,
|
||||
"name": req.query.n,
|
||||
"group": req.query.g,
|
||||
"memberCount": req.query.c,
|
||||
"teams": teams
|
||||
});
|
||||
|
||||
res.redirect(`http://localhost:3000/room/?id=${id}`);
|
||||
});
|
||||
|
||||
app.get('/api/join/', (req, res) => {
|
||||
rooms.findOne({ "id": req.query.id }).then(room => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.write(JSON.stringify(room));
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
|
||||
app.use(
|
||||
compression({ threshold: 0 }),
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
});
|
||||
82
src/service-worker.js
Normal file
82
src/service-worker.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { timestamp, files, shell, routes } from '@sapper/service-worker';
|
||||
|
||||
const ASSETS = `cache${timestamp}`;
|
||||
|
||||
// `shell` is an array of all the files generated by the bundler,
|
||||
// `files` is an array of everything in the `static` directory
|
||||
const to_cache = shell.concat(files);
|
||||
const cached = new Set(to_cache);
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.open(ASSETS)
|
||||
.then(cache => cache.addAll(to_cache))
|
||||
.then(() => {
|
||||
self.skipWaiting();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(
|
||||
caches.keys().then(async keys => {
|
||||
// delete old caches
|
||||
for (const key of keys) {
|
||||
if (key !== ASSETS) await caches.delete(key);
|
||||
}
|
||||
|
||||
self.clients.claim();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
|
||||
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
// don't try to handle e.g. data: URIs
|
||||
if (!url.protocol.startsWith('http')) return;
|
||||
|
||||
// ignore dev server requests
|
||||
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
|
||||
|
||||
// always serve static files and bundler-generated assets from cache
|
||||
if (url.host === self.location.host && cached.has(url.pathname)) {
|
||||
event.respondWith(caches.match(event.request));
|
||||
return;
|
||||
}
|
||||
|
||||
// for pages, you might want to serve a shell `service-worker-index.html` file,
|
||||
// which Sapper has generated for you. It's not right for every
|
||||
// app, but if it's right for yours then uncomment this section
|
||||
/*
|
||||
if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
|
||||
event.respondWith(caches.match('/service-worker-index.html'));
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (event.request.cache === 'only-if-cached') return;
|
||||
|
||||
// for everything else, try the network first, falling back to
|
||||
// cache if the user is offline. (If the pages never change, you
|
||||
// might prefer a cache-first approach to a network-first one.)
|
||||
event.respondWith(
|
||||
caches
|
||||
.open(`offline${timestamp}`)
|
||||
.then(async cache => {
|
||||
try {
|
||||
const response = await fetch(event.request);
|
||||
cache.put(event.request, response.clone());
|
||||
return response;
|
||||
} catch(err) {
|
||||
const response = await cache.match(event.request);
|
||||
if (response) return response;
|
||||
|
||||
throw err;
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
33
src/template.html
Normal file
33
src/template.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<meta name="theme-color" content="#333333">
|
||||
|
||||
%sapper.base%
|
||||
|
||||
<link rel="stylesheet" href="global.css">
|
||||
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
|
||||
<!-- Sapper creates a <script> tag containing `src/client.js`
|
||||
and anything else it needs to hydrate the app and
|
||||
initialise the router -->
|
||||
%sapper.scripts%
|
||||
|
||||
<!-- Sapper generates a <style> tag containing critical CSS
|
||||
for the current page. CSS for the rest of the app is
|
||||
lazily loaded when it precaches secondary pages -->
|
||||
%sapper.styles%
|
||||
|
||||
<!-- This contains the contents of the <svelte:head> component, if
|
||||
the current page has one -->
|
||||
%sapper.head%
|
||||
</head>
|
||||
<body>
|
||||
<!-- The application will be rendered inside this element,
|
||||
because `src/client.js` references it -->
|
||||
<div id="sapper">%sapper.html%</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user