Initial commit - cloned the Svelte todo's app with google login enabled as a starting point. This system will initially be used to let the chrome extension for students report which computers are used by which students and when.
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
private/settings.json
|
||||||
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
13
.idea/AvusdMeteor.iml
generated
Normal file
13
.idea/AvusdMeteor.iml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.meteor/local" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<includedPredefinedLibrary name="Meteor project library" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/AvusdMeteor.iml" filepath="$PROJECT_DIR$/.idea/AvusdMeteor.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
19
.meteor/.finished-upgraders
Normal file
19
.meteor/.finished-upgraders
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# This file contains information which helps Meteor properly upgrade your
|
||||||
|
# app when you run 'meteor update'. You should check it into version control
|
||||||
|
# with your project.
|
||||||
|
|
||||||
|
notices-for-0.9.0
|
||||||
|
notices-for-0.9.1
|
||||||
|
0.9.4-platform-file
|
||||||
|
notices-for-facebook-graph-api-2
|
||||||
|
1.2.0-standard-minifiers-package
|
||||||
|
1.2.0-meteor-platform-split
|
||||||
|
1.2.0-cordova-changes
|
||||||
|
1.2.0-breaking-changes
|
||||||
|
1.3.0-split-minifiers-package
|
||||||
|
1.4.0-remove-old-dev-bundle-link
|
||||||
|
1.4.1-add-shell-server-package
|
||||||
|
1.4.3-split-account-service-packages
|
||||||
|
1.5-add-dynamic-import-package
|
||||||
|
1.7-split-underscore-from-meteor-base
|
||||||
|
1.8.3-split-jquery-from-blaze
|
||||||
1
.meteor/.gitignore
vendored
Normal file
1
.meteor/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
local
|
||||||
7
.meteor/.id
Normal file
7
.meteor/.id
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file contains a token that is unique to your project.
|
||||||
|
# Check it into your repository along with the rest of this directory.
|
||||||
|
# It can be used for purposes such as:
|
||||||
|
# - ensuring you don't accidentally deploy one app on top of another
|
||||||
|
# - providing package authors with aggregated statistics
|
||||||
|
|
||||||
|
ou78pym2e7kf.kuc1f56r19il
|
||||||
29
.meteor/packages
Normal file
29
.meteor/packages
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Meteor packages used by this project, one per line.
|
||||||
|
# Check this file (and the other files in this directory) into your repository.
|
||||||
|
#
|
||||||
|
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||||
|
# but you can also edit it by hand.
|
||||||
|
|
||||||
|
meteor-base@1.4.0 # Packages every Meteor app needs to have
|
||||||
|
mobile-experience@1.1.0 # Packages for a great mobile UX
|
||||||
|
mongo@1.10.1 # The database Meteor supports right now
|
||||||
|
jquery # Wrapper package for npm-installed jquery
|
||||||
|
reactive-var@1.0.11 # Reactive variable for tracker
|
||||||
|
tracker@1.2.0 # Meteor's client-side reactive programming library
|
||||||
|
|
||||||
|
standard-minifier-css@1.7.1 # CSS minifier run for production mode
|
||||||
|
standard-minifier-js@2.6.0 # JS minifier run for production mode
|
||||||
|
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
|
||||||
|
ecmascript@0.14.4 # Enable ECMAScript2015+ syntax in app code
|
||||||
|
typescript@4.1.2 # Enable TypeScript syntax in .ts and .tsx modules
|
||||||
|
shell-server@0.5.0 # Server-side component of the `meteor shell` command
|
||||||
|
|
||||||
|
svelte:compiler
|
||||||
|
rdb:svelte-meteor-data
|
||||||
|
static-html
|
||||||
|
accounts-ui
|
||||||
|
accounts-password@1.6.3
|
||||||
|
svelte:blaze-integration
|
||||||
|
meteortesting:mocha
|
||||||
|
accounts-google
|
||||||
|
service-configuration
|
||||||
2
.meteor/platforms
Normal file
2
.meteor/platforms
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
server
|
||||||
|
browser
|
||||||
1
.meteor/release
Normal file
1
.meteor/release
Normal file
@@ -0,0 +1 @@
|
|||||||
|
METEOR@1.12
|
||||||
104
.meteor/versions
Normal file
104
.meteor/versions
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
accounts-base@1.7.1
|
||||||
|
accounts-google@1.3.3
|
||||||
|
accounts-oauth@1.2.0
|
||||||
|
accounts-password@1.6.3
|
||||||
|
accounts-ui@1.3.2
|
||||||
|
accounts-ui-unstyled@1.4.3
|
||||||
|
allow-deny@1.1.0
|
||||||
|
autoupdate@1.6.0
|
||||||
|
babel-compiler@7.5.4
|
||||||
|
babel-runtime@1.5.0
|
||||||
|
base64@1.0.12
|
||||||
|
binary-heap@1.0.11
|
||||||
|
blaze@2.3.4
|
||||||
|
blaze-tools@1.0.10
|
||||||
|
boilerplate-generator@1.7.1
|
||||||
|
caching-compiler@1.2.2
|
||||||
|
caching-html-compiler@1.1.3
|
||||||
|
callback-hook@1.3.0
|
||||||
|
check@1.3.1
|
||||||
|
ddp@1.4.0
|
||||||
|
ddp-client@2.3.3
|
||||||
|
ddp-common@1.4.0
|
||||||
|
ddp-rate-limiter@1.0.9
|
||||||
|
ddp-server@2.3.2
|
||||||
|
deps@1.0.12
|
||||||
|
diff-sequence@1.1.1
|
||||||
|
dynamic-import@0.5.4
|
||||||
|
ecmascript@0.14.4
|
||||||
|
ecmascript-runtime@0.7.0
|
||||||
|
ecmascript-runtime-client@0.11.0
|
||||||
|
ecmascript-runtime-server@0.10.0
|
||||||
|
ejson@1.1.1
|
||||||
|
email@2.0.0
|
||||||
|
es5-shim@4.8.0
|
||||||
|
fetch@0.1.1
|
||||||
|
geojson-utils@1.0.10
|
||||||
|
google-oauth@1.3.0
|
||||||
|
hot-code-push@1.0.4
|
||||||
|
html-tools@1.0.11
|
||||||
|
htmljs@1.0.11
|
||||||
|
http@1.4.2
|
||||||
|
id-map@1.1.0
|
||||||
|
inter-process-messaging@0.1.1
|
||||||
|
jquery@3.0.0
|
||||||
|
launch-screen@1.2.0
|
||||||
|
less@3.0.1
|
||||||
|
livedata@1.0.18
|
||||||
|
localstorage@1.2.0
|
||||||
|
logging@1.1.20
|
||||||
|
meteor@1.9.3
|
||||||
|
meteor-base@1.4.0
|
||||||
|
meteortesting:browser-tests@1.3.4
|
||||||
|
meteortesting:mocha@2.0.1
|
||||||
|
meteortesting:mocha-core@8.1.2
|
||||||
|
minifier-css@1.5.3
|
||||||
|
minifier-js@2.6.0
|
||||||
|
minimongo@1.6.1
|
||||||
|
mobile-experience@1.1.0
|
||||||
|
mobile-status-bar@1.1.0
|
||||||
|
modern-browsers@0.1.5
|
||||||
|
modules@0.15.0
|
||||||
|
modules-runtime@0.12.0
|
||||||
|
mongo@1.10.1
|
||||||
|
mongo-decimal@0.1.2
|
||||||
|
mongo-dev-server@1.1.0
|
||||||
|
mongo-id@1.0.7
|
||||||
|
npm-bcrypt@0.9.3
|
||||||
|
npm-mongo@3.8.1
|
||||||
|
oauth@1.3.2
|
||||||
|
oauth2@1.3.0
|
||||||
|
observe-sequence@1.0.16
|
||||||
|
ordered-dict@1.1.0
|
||||||
|
promise@0.11.2
|
||||||
|
random@1.2.0
|
||||||
|
rate-limit@1.0.9
|
||||||
|
rdb:svelte-meteor-data@0.3.0
|
||||||
|
reactive-dict@1.3.0
|
||||||
|
reactive-var@1.0.11
|
||||||
|
reload@1.3.1
|
||||||
|
retry@1.1.0
|
||||||
|
routepolicy@1.1.0
|
||||||
|
service-configuration@1.0.11
|
||||||
|
session@1.2.0
|
||||||
|
sha@1.0.9
|
||||||
|
shell-server@0.5.0
|
||||||
|
socket-stream-client@0.3.1
|
||||||
|
spacebars@1.0.15
|
||||||
|
spacebars-compiler@1.1.3
|
||||||
|
srp@1.1.0
|
||||||
|
standard-minifier-css@1.7.1
|
||||||
|
standard-minifier-js@2.6.0
|
||||||
|
static-html@1.2.2
|
||||||
|
svelte:blaze-integration@0.4.0
|
||||||
|
svelte:compiler@3.27.0
|
||||||
|
templating@1.3.2
|
||||||
|
templating-compiler@1.3.3
|
||||||
|
templating-runtime@1.3.2
|
||||||
|
templating-tools@1.1.2
|
||||||
|
tracker@1.2.0
|
||||||
|
typescript@4.1.2
|
||||||
|
underscore@1.0.10
|
||||||
|
url@1.3.1
|
||||||
|
webapp@1.9.1
|
||||||
|
webapp-hashing@1.0.9
|
||||||
126
client/main.css
Normal file
126
client/main.css
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/* CSS declarations go here */
|
||||||
|
body {
|
||||||
|
padding: 10px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background-color: #315481;
|
||||||
|
background-image: linear-gradient(to bottom, #315481, #918e82 100%);
|
||||||
|
background-attachment: fixed;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
min-height: 100%;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background: #d2edf4;
|
||||||
|
background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
|
||||||
|
padding: 20px 15px 15px 15px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-buttons {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 80px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
background: none;
|
||||||
|
font-size: 1em;
|
||||||
|
border: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: #eee solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .text {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.checked {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.checked .text {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.private {
|
||||||
|
background: #eee;
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .hide-completed {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-private {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
li {
|
||||||
|
padding: 12px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
width: 150px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=main.css.map */
|
||||||
1
client/main.css.map
Normal file
1
client/main.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["main.sass"],"names":[],"mappings":"AAAA;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;IACC;;;EAED;IACC;IACA;;;EAED;IACC","file":"main.css"}
|
||||||
8
client/main.html
Normal file
8
client/main.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<head>
|
||||||
|
<title>Todo List</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
127
client/main.js
Normal file
127
client/main.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import {Meteor} from 'meteor/meteor';
|
||||||
|
import App from '../imports/ui/App.svelte';
|
||||||
|
import '../imports/startup/accounts-config.js';
|
||||||
|
import Phaser from 'phaser';
|
||||||
|
|
||||||
|
Meteor.startup(() => {
|
||||||
|
new App({
|
||||||
|
target: document.getElementById('app')
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class playGame extends Phaser.Scene {
|
||||||
|
constructor() {
|
||||||
|
super("PlayGame");
|
||||||
|
}
|
||||||
|
create() {
|
||||||
|
// graphic object used to draw walls
|
||||||
|
this.wallGraphics = this.add.graphics();
|
||||||
|
this.wallGraphics.lineStyle(1, 0x00ff00);
|
||||||
|
|
||||||
|
// graphic object used to draw rays of light
|
||||||
|
this.lightGraphics = this.add.graphics();
|
||||||
|
|
||||||
|
// array with all polygons in game
|
||||||
|
this.polygons = [];
|
||||||
|
|
||||||
|
// add random boxes
|
||||||
|
for(let i = 0; i < gameOptions.boxes; i ++){
|
||||||
|
this.addRandomBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
// walls around game perimeter
|
||||||
|
this.polygons.push([[-1, -1], [game.config.width + 1, -1], [game.config.width + 1, game.config.height+1], [-1, game.config.height + 1]]);
|
||||||
|
|
||||||
|
// listener for input movement
|
||||||
|
this.input.on("pointermove", this.renderLight, this);
|
||||||
|
}
|
||||||
|
addRandomBox() {
|
||||||
|
// use a do...while statement because there can't be intersecting polygons
|
||||||
|
do {
|
||||||
|
// random x and y coordinates, width and height
|
||||||
|
var startX = Phaser.Math.Between(10, game.config.width - 10 - gameOptions.sizeRange.max);
|
||||||
|
var startY = Phaser.Math.Between(10, game.config.height - 10 - gameOptions.sizeRange.max);
|
||||||
|
var width = Phaser.Math.Between(gameOptions.sizeRange.min, gameOptions.sizeRange.max);
|
||||||
|
var height = Phaser.Math.Between(gameOptions.sizeRange.min, gameOptions.sizeRange.max);
|
||||||
|
|
||||||
|
// check if current box intersects other boxes
|
||||||
|
} while(this.boxesIntersect(startX, startY, width, height));
|
||||||
|
|
||||||
|
// draw the box
|
||||||
|
this.wallGraphics.strokeRect(startX, startY, width, height);
|
||||||
|
|
||||||
|
// insert box vertices into polygons array
|
||||||
|
this.polygons.push([[startX, startY], [startX + width, startY], [startX + width, startY + height], [startX, startY + height]]);
|
||||||
|
}
|
||||||
|
// method to check if the box intersects other boxes
|
||||||
|
boxesIntersect(x, y, w, h) {
|
||||||
|
// loop through existing boxes
|
||||||
|
for(let i = 0; i < this.polygons.length; i ++) {
|
||||||
|
// if the box intersects the existing i-th box...
|
||||||
|
if(x < this.polygons[i][1][0] && x + w > this.polygons[i][0][0] && y < this.polygons[i][3][1] && y + h > this.polygons[i][0][1]){
|
||||||
|
// return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we reach the end of the loop, return false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// method to render the light
|
||||||
|
renderLight(pointer) {
|
||||||
|
// determine light polygon starting from pointer coordinates
|
||||||
|
let visibility = this.createLightPolygon(pointer.x, pointer.y);
|
||||||
|
|
||||||
|
// clear and prepare lightGraphics graphic object
|
||||||
|
this.lightGraphics.clear();
|
||||||
|
this.lightGraphics.lineStyle(2, 0xff8800);
|
||||||
|
this.lightGraphics.fillStyle(0xffff00);
|
||||||
|
|
||||||
|
// begin a drawing path
|
||||||
|
this.lightGraphics.beginPath();
|
||||||
|
|
||||||
|
// move the graphic pen to first vertex of light polygon
|
||||||
|
this.lightGraphics.moveTo(visibility[0][0], visibility[0][1]);
|
||||||
|
|
||||||
|
// loop through all light polygon vertices
|
||||||
|
for(let i = 1; i <= visibility.length; i ++) {
|
||||||
|
// draw a line to i-th light polygon vertex
|
||||||
|
this.lightGraphics.lineTo(visibility[i % visibility.length][0], visibility[ i %visibility.length][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// close, stroke and fill light polygon
|
||||||
|
this.lightGraphics.closePath();
|
||||||
|
this.lightGraphics.fillPath();
|
||||||
|
this.lightGraphics.strokePath();
|
||||||
|
}
|
||||||
|
// method to create light polygon using visibility_polygon.js
|
||||||
|
createLightPolygon(x, y) {
|
||||||
|
let segments = VisibilityPolygon.convertToSegments(this.polygons);
|
||||||
|
segments = VisibilityPolygon.breakIntersections(segments);
|
||||||
|
let position = [x, y];
|
||||||
|
if (VisibilityPolygon.inPolygon(position, this.polygons[this.polygons.length - 1])) {
|
||||||
|
return VisibilityPolygon.compute(position, segments);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
type: Phaser.AUTO,
|
||||||
|
parent: "game",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
physics: {
|
||||||
|
default: 'arcade',
|
||||||
|
arcade: {
|
||||||
|
gravity: {y: 200}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
scene: playGame
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let game = new Phaser.Game(config);
|
||||||
|
});
|
||||||
103
client/main.sass
Normal file
103
client/main.sass
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/* CSS declarations go here */
|
||||||
|
body
|
||||||
|
padding: 10px
|
||||||
|
font-family: sans-serif
|
||||||
|
background-color: #315481
|
||||||
|
background-image: linear-gradient(to bottom, #315481, #918e82 100%)
|
||||||
|
background-attachment: fixed
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
font-size: 14px
|
||||||
|
|
||||||
|
.container
|
||||||
|
max-width: 600px
|
||||||
|
margin: 0 auto
|
||||||
|
min-height: 100%
|
||||||
|
background: white
|
||||||
|
|
||||||
|
header
|
||||||
|
background: #d2edf4
|
||||||
|
background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%)
|
||||||
|
padding: 20px 15px 15px 15px
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
#login-buttons
|
||||||
|
display: block
|
||||||
|
|
||||||
|
h1
|
||||||
|
font-size: 1.5em
|
||||||
|
margin: 0
|
||||||
|
margin-bottom: 10px
|
||||||
|
display: inline-block
|
||||||
|
margin-right: 1em
|
||||||
|
|
||||||
|
form
|
||||||
|
margin-top: 10px
|
||||||
|
margin-bottom: -10px
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.new-task input
|
||||||
|
box-sizing: border-box
|
||||||
|
padding: 10px 0
|
||||||
|
background: transparent
|
||||||
|
border: none
|
||||||
|
width: 100%
|
||||||
|
padding-right: 80px
|
||||||
|
font-size: 1em
|
||||||
|
|
||||||
|
.new-task input:focus
|
||||||
|
outline: 0
|
||||||
|
|
||||||
|
ul
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
background: white
|
||||||
|
|
||||||
|
.delete
|
||||||
|
float: right
|
||||||
|
font-weight: bold
|
||||||
|
background: none
|
||||||
|
font-size: 1em
|
||||||
|
border: none
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
li
|
||||||
|
position: relative
|
||||||
|
list-style: none
|
||||||
|
padding: 15px
|
||||||
|
border-bottom: #eee solid 1px
|
||||||
|
|
||||||
|
li .text
|
||||||
|
margin-left: 10px
|
||||||
|
|
||||||
|
li.checked
|
||||||
|
color: #888
|
||||||
|
|
||||||
|
li.checked .text
|
||||||
|
text-decoration: line-through
|
||||||
|
|
||||||
|
li.private
|
||||||
|
background: #eee
|
||||||
|
border-color: #ddd
|
||||||
|
|
||||||
|
header .hide-completed
|
||||||
|
float: right
|
||||||
|
|
||||||
|
.toggle-private
|
||||||
|
margin-left: 5px
|
||||||
|
|
||||||
|
@media (max-width: 600px)
|
||||||
|
li
|
||||||
|
padding: 12px 15px
|
||||||
|
|
||||||
|
.search
|
||||||
|
width: 150px
|
||||||
|
clear: both
|
||||||
|
|
||||||
|
.new-task input
|
||||||
|
padding-bottom: 5px
|
||||||
2
imports/api/index.js
Normal file
2
imports/api/index.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import "./tasks.js";
|
||||||
|
import "./users.js";
|
||||||
71
imports/api/tasks.js
Normal file
71
imports/api/tasks.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
|
export const Tasks = new Mongo.Collection('tasks');
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
// This code only runs on the server
|
||||||
|
Meteor.publish('tasks', function tasksPublication() {
|
||||||
|
return Tasks.find({
|
||||||
|
$or: [
|
||||||
|
{ private: { $ne: true } },
|
||||||
|
{ owner: this.userId },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'tasks.insert'(text) {
|
||||||
|
check(text, String);
|
||||||
|
|
||||||
|
// Make sure the user is logged in before inserting a task
|
||||||
|
if (!this.userId) {
|
||||||
|
throw new Meteor.Error('not-authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
Tasks.insert({
|
||||||
|
text,
|
||||||
|
createdAt: new Date(),
|
||||||
|
owner: this.userId,
|
||||||
|
username: Meteor.users.findOne(this.userId).username,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'tasks.remove'(taskId) {
|
||||||
|
check(taskId, String);
|
||||||
|
|
||||||
|
const task = Tasks.findOne(taskId);
|
||||||
|
if (task.private && task.owner !== this.userId) {
|
||||||
|
// If the task is private, make sure only the owner can delete it
|
||||||
|
throw new Meteor.Error('not-authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
Tasks.remove(taskId);
|
||||||
|
},
|
||||||
|
'tasks.setChecked'(taskId, setChecked) {
|
||||||
|
check(taskId, String);
|
||||||
|
check(setChecked, Boolean);
|
||||||
|
|
||||||
|
const task = Tasks.findOne(taskId);
|
||||||
|
if (task.private && task.owner !== this.userId) {
|
||||||
|
// If the task is private, make sure only the owner can check it off
|
||||||
|
throw new Meteor.Error('not-authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
Tasks.update(taskId, { $set: { checked: setChecked } });
|
||||||
|
},
|
||||||
|
'tasks.setPrivate'(taskId, setToPrivate) {
|
||||||
|
check(taskId, String);
|
||||||
|
check(setToPrivate, Boolean);
|
||||||
|
|
||||||
|
const task = Tasks.findOne(taskId);
|
||||||
|
|
||||||
|
// Make sure only the task owner can make a task private
|
||||||
|
if (task.owner !== this.userId) {
|
||||||
|
throw new Meteor.Error('not-authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
Tasks.update(taskId, { $set: { private: setToPrivate } });
|
||||||
|
},
|
||||||
|
});
|
||||||
41
imports/api/tasks.tests.js
Normal file
41
imports/api/tasks.tests.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Random } from 'meteor/random';
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import { Tasks } from './tasks.js';
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
describe('Tasks', () => {
|
||||||
|
describe('methods', () => {
|
||||||
|
const userId = Random.id();
|
||||||
|
let taskId;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Tasks.remove({});
|
||||||
|
taskId = Tasks.insert({
|
||||||
|
text: 'test task',
|
||||||
|
createdAt: new Date(),
|
||||||
|
owner: userId,
|
||||||
|
username: 'tmeasday',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can delete owned task', () => {
|
||||||
|
// Find the internal implementation of the task method so we can
|
||||||
|
// test it in isolation
|
||||||
|
const deleteTask = Meteor.server.method_handlers['tasks.remove'];
|
||||||
|
|
||||||
|
// Set up a fake method invocation that looks like what the method expects
|
||||||
|
const invocation = { userId };
|
||||||
|
|
||||||
|
// Run the method with `this` set to the fake invocation
|
||||||
|
deleteTask.apply(invocation, [taskId]);
|
||||||
|
|
||||||
|
// Verify that the method does what we expected
|
||||||
|
assert.equal(Tasks.find().count(), 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
10
imports/api/users.js
Normal file
10
imports/api/users.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.methods({
|
||||||
|
'users.login'() {
|
||||||
|
console.log("Login Called.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
5
imports/startup/accounts-config.js
Normal file
5
imports/startup/accounts-config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Accounts } from 'meteor/accounts-base';
|
||||||
|
|
||||||
|
Accounts.ui.config({
|
||||||
|
passwordSignupFields: 'USERNAME_ONLY'
|
||||||
|
});
|
||||||
100
imports/ui/App.svelte
Normal file
100
imports/ui/App.svelte
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import {onMount} from 'svelte';
|
||||||
|
import {useTracker} from 'meteor/rdb:svelte-meteor-data';
|
||||||
|
import {BlazeTemplate} from 'meteor/svelte:blaze-integration';
|
||||||
|
import Task from './Task.svelte';
|
||||||
|
import {Tasks} from '../api/tasks.js'
|
||||||
|
|
||||||
|
let newTask = "";
|
||||||
|
let hideCompleted = false;
|
||||||
|
let tasks;
|
||||||
|
let currentUser;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
Meteor.subscribe('tasks');
|
||||||
|
});
|
||||||
|
|
||||||
|
$: incompleteCount = useTracker(() => Tasks.find({checked: {$ne: true}}).count());
|
||||||
|
|
||||||
|
$: currentUser = useTracker(() => Meteor.user());
|
||||||
|
|
||||||
|
const taskStore = Tasks.find({}, {sort: {createdAt: -1}});
|
||||||
|
$: {
|
||||||
|
tasks = $taskStore;
|
||||||
|
if (hideCompleted) {
|
||||||
|
tasks = tasks.filter(task => !task.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit(event) {
|
||||||
|
Meteor.call("tasks.insert", newTask);
|
||||||
|
// Clear form
|
||||||
|
newTask = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function performLogin() {
|
||||||
|
//console.log("In Perform Login");
|
||||||
|
//Meteor.call("users.login");
|
||||||
|
let config = ServiceConfiguration.configurations.findOne({service: 'google'});
|
||||||
|
|
||||||
|
console.log(config);
|
||||||
|
let scope = config.scope;
|
||||||
|
let loginStyle = "popup";
|
||||||
|
|
||||||
|
Meteor.loginWithGoogle({requestPermissions: scope, loginStyle, requestOfflineToken: true}, (err) => {
|
||||||
|
if(err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Logged in");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function performLogout() {
|
||||||
|
Meteor.logout();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<h1>Todo List ({ $incompleteCount })</h1>
|
||||||
|
<label className="hide-completed">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={hideCompleted}
|
||||||
|
/>
|
||||||
|
Hide Completed Tasks
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<BlazeTemplate template="loginButtons"/>
|
||||||
|
{#if !$currentUser}
|
||||||
|
<button type="button" on:click={performLogin}>Login</button>
|
||||||
|
{:else}
|
||||||
|
<button type="button" on:click={performLogout}>Logout</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if $currentUser}
|
||||||
|
<form class="new-task" on:submit|preventDefault={handleSubmit}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Type to add new tasks"
|
||||||
|
bind:value={newTask}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
</header>
|
||||||
|
<ul>
|
||||||
|
{#each tasks as task}
|
||||||
|
<Task
|
||||||
|
key={task._id}
|
||||||
|
task={task}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
<div id="game" style="height: 400px; width: 400px">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
56
imports/ui/Task.svelte
Normal file
56
imports/ui/Task.svelte
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script>
|
||||||
|
import { useTracker } from 'meteor/rdb:svelte-meteor-data';
|
||||||
|
import { Tasks } from "../api/tasks.js";
|
||||||
|
|
||||||
|
export let key;
|
||||||
|
export let task;
|
||||||
|
let showPrivateButton;
|
||||||
|
|
||||||
|
$: currentUser = useTracker(() => Meteor.user());
|
||||||
|
|
||||||
|
$: {
|
||||||
|
showPrivateButton = false;
|
||||||
|
if($currentUser){
|
||||||
|
showPrivateButton = task.owner === $currentUser._id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleChecked() {
|
||||||
|
// Set the checked property to the opposite of its current value
|
||||||
|
Meteor.call("tasks.setChecked", task._id, !task.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteThisTask() {
|
||||||
|
Meteor.call("tasks.remove", task._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePrivate() {
|
||||||
|
Meteor.call("tasks.setPrivate", task._id, !task.private);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li class:checked="{task.checked}"
|
||||||
|
class:private="{task.private}" >
|
||||||
|
<button class="delete" on:click={deleteThisTask}>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
readonly
|
||||||
|
checked={!!task.checked}
|
||||||
|
on:click={toggleChecked}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{#if showPrivateButton}
|
||||||
|
<button className="toggle-private" on:click="{togglePrivate}">
|
||||||
|
{ task.private ? "Private" : "Public" }
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<span class="text">
|
||||||
|
<strong>{ task.username }</strong>
|
||||||
|
: { task.text }
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
1542
package-lock.json
generated
Normal file
1542
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
package.json
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "simple-todos-svelte",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "meteor run",
|
||||||
|
"test": "meteor test --once --driver-package meteortesting:mocha",
|
||||||
|
"test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha",
|
||||||
|
"visualize": "meteor --production --extra-packages bundle-visualizer"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.3",
|
||||||
|
"connect-route": "^0.1.5",
|
||||||
|
"jquery": "^3.4.1",
|
||||||
|
"meteor-node-stubs": "^1.0.0",
|
||||||
|
"phaser": "3.55.2",
|
||||||
|
"svelte": "^3.27.0"
|
||||||
|
},
|
||||||
|
"meteor": {
|
||||||
|
"mainModule": {
|
||||||
|
"client": "client/main.js",
|
||||||
|
"server": "server/main.js"
|
||||||
|
},
|
||||||
|
"testModule": "tests/main.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
server/google-oauth.js
Normal file
27
server/google-oauth.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Google cloud platform: https://console.developers.google.com/apis/dashboard
|
||||||
|
* Setup a OAuth consent screen, then setup OAuth credentials.
|
||||||
|
* Loads the information from the /private/settings.json file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
try {
|
||||||
|
let settingsFile = process.env.SETTINGS;
|
||||||
|
let settings = JSON.parse(Assets.getText(settingsFile));
|
||||||
|
|
||||||
|
if (settings) {
|
||||||
|
ServiceConfiguration.configurations.upsert({
|
||||||
|
service: "google"
|
||||||
|
}, {
|
||||||
|
$set: {
|
||||||
|
loginStyle: settings.google.loginStyle,
|
||||||
|
clientId: settings.google.clientId,
|
||||||
|
secret: settings.google.secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log("Error: No settings file.");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
12
server/main.js
Normal file
12
server/main.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import '../imports/api/';
|
||||||
|
import './google-oauth.js';
|
||||||
|
import connectRoute from 'connect-route';
|
||||||
|
|
||||||
|
// Got the below code from:
|
||||||
|
// https://docs.meteor.com/packages/webapp.html
|
||||||
|
WebApp.connectHandlers.use(connectRoute((router) => {
|
||||||
|
router.get('/ping', (req, res, next) => {
|
||||||
|
//TODO: Change ping / pong to something appropriate for a browser plugin reporting user/machine data and requesting updates.
|
||||||
|
res.end("pong");
|
||||||
|
});
|
||||||
|
}));
|
||||||
21
tests/main.js
Normal file
21
tests/main.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import assert from "assert";
|
||||||
|
import "../imports/api/tasks.tests.js";
|
||||||
|
|
||||||
|
describe("simple-todos-svelte", function () {
|
||||||
|
it("package.json has correct name", async function () {
|
||||||
|
const { name } = await import("../package.json");
|
||||||
|
assert.strictEqual(name, "simple-todos-svelte");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Meteor.isClient) {
|
||||||
|
it("client is not server", function () {
|
||||||
|
assert.strictEqual(Meteor.isServer, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
it("server is not client", function () {
|
||||||
|
assert.strictEqual(Meteor.isClient, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user