merge upstream/master
|
@ -1,4 +1,4 @@
|
|||
<a class="cell" data-id="{{id}}" href="/make/{{id}}/play">
|
||||
<a class="cell" data-id="{{id}}" href="/make/{{id}}/{{mode || 'play'}}">
|
||||
<div class="left">
|
||||
<span class="title">{{name}}</span>
|
||||
<span v-if="author" class="description" v-bind-html="'by _'" v-with="{name: author.username|| guestKey}"></span>
|
||||
|
|
|
@ -3,6 +3,7 @@ var i18n = require('../../lib/i18n');
|
|||
module.exports = {
|
||||
className: 'app-cell',
|
||||
template: require('./index.html'),
|
||||
paramAttributes: ['mode'],
|
||||
computed: {
|
||||
guestKey: function() {
|
||||
return i18n.get('Guest');
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.app-cell .cell {
|
||||
display: flex;
|
||||
.app-icon,
|
||||
.author-icon {
|
||||
width: 50px;
|
||||
|
@ -10,4 +11,14 @@
|
|||
.app-icon {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.title {
|
||||
.text-truncate;
|
||||
}
|
||||
.left, .right {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
.left {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<button class="back" v-if="back" v-on="click: goBack"><</button>
|
||||
<button v-if="back === true" class="back" v-on="click: goBack"><</button>
|
||||
<a v-if="typeof back === 'string'" class="back" href="{{back}}"><</a>
|
||||
|
||||
<button v-if="cancel === true" v-on="click: goBack">{{'Cancel' | i18n}}</button>
|
||||
<a v-if="typeof cancel === 'string'" href="{{cancel}}">{{'Cancel' | i18n}}</a>
|
||||
|
||||
<h1>{{title | i18n}}</h1>
|
||||
<button v-if="typeof onDone === 'function'" v-on="click: onDone">{{'Done' | i18n}}</button>
|
||||
<a v-if="typeof onDone === 'string'" href="{{onDone}}">{{'Done' | i18n}}</a>
|
||||
|
||||
<button v-if="typeof onDone === 'function'" v-on="click: onDone" v-class="disabled: doneDisabled">{{doneLabel || 'Done' | i18n}}
|
||||
</button>
|
||||
<a v-if="typeof onDone === 'string'" href="{{onDone}}" v-class="disabled: doneDisabled">{{doneLabel || 'Done' | i18n}}</a>
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
right: 0;
|
||||
}
|
||||
&.back {
|
||||
line-height: @header-height;
|
||||
padding-top: 0;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<p class="text-small">Make your own app at</p>
|
||||
<p><a href="http://webmaker-app.mofodev.net" class="btn btn-dark btn-block">Mozilla Webmaker</a></p>
|
||||
<!-- Can't show these until we have manifest + discover implemented -->
|
||||
<!-- <ul class="list-btns">
|
||||
<li><a class="btn" href="">Add to Homescreen</a></li>
|
||||
<li><a class="btn" href="">Learn more abut this app</a></li>
|
||||
<li><a class="btn" href="">Share App</a></li>
|
||||
</ul> -->
|
||||
<ul class="list-btns">
|
||||
<li v-if="showInstall"><a class="btn" href="#" v-on="click: install">Add to Homescreen</a></li>
|
||||
<!-- <li><a class="btn" href="">Learn more abut this app</a></li>
|
||||
<li><a class="btn" href="">Share App</a></li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,8 @@ module.exports = {
|
|||
id: 'publish-footer',
|
||||
template: require('./index.html'),
|
||||
data: {
|
||||
showFooter: false
|
||||
showFooter: false,
|
||||
showInstall: false
|
||||
},
|
||||
methods: {
|
||||
toggleShowFooter: function (e) {
|
||||
|
@ -24,6 +25,29 @@ module.exports = {
|
|||
self.toggleShowFooter();
|
||||
}, false);
|
||||
|
||||
if (navigator.mozApps) {
|
||||
var manifestUrl = location.href + 'manifest.webapp';
|
||||
self.$data.install = function install(e) {
|
||||
e.preventDefault();
|
||||
var installLocFind = navigator.mozApps.install(manifestUrl);
|
||||
installLocFind.onsuccess = function (data) {
|
||||
self.$data.showInstall = false;
|
||||
};
|
||||
installLocFind.onerror = function() {
|
||||
alert('Sorry, we could not install this app: ' + installLocFind.error.name);
|
||||
};
|
||||
};
|
||||
|
||||
var installCheck = navigator.mozApps.checkInstalled(manifestUrl);
|
||||
installCheck.onsuccess = function() {
|
||||
if (installCheck.result) {
|
||||
self.$data.showInstall = false;
|
||||
} else {
|
||||
self.$data.showInstall = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.toggleOverlay = function (show) {
|
||||
if (show) {
|
||||
overlay.classList.add('on');
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
margin-top: -25px;
|
||||
width: 65px;
|
||||
height: 65px;
|
||||
margin-right: 17px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
#tabBar li g {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a class="cell" data-id="{{id}}" href="/make/{{id}}/play">
|
||||
<a class="cell" data-id="{{id}}" href="/make/{{id}}/edit">
|
||||
<img class="app-icon" src="{{icon}}">
|
||||
<div class="name">{{name | i18n}}</div>
|
||||
</a>
|
||||
|
|
29
lib/app.js
|
@ -1,6 +1,9 @@
|
|||
var Blocks = require('./blocks');
|
||||
var utils = require('./utils');
|
||||
var uuid = require('./uuid');
|
||||
var clone = require('clone');
|
||||
var templates = require('./templates.json');
|
||||
var i18n = require('./i18n');
|
||||
|
||||
var model = require('./model')();
|
||||
var blocks = new Blocks();
|
||||
|
@ -18,6 +21,32 @@ function App (id) {
|
|||
self.data = model.data.apps[self.index];
|
||||
}
|
||||
|
||||
// Global Methods
|
||||
|
||||
// createApp(options)
|
||||
// - template: id of a template. Will overwrite options.data
|
||||
// - data: a set of app data to clone
|
||||
// - name: a name for the clone.
|
||||
App.createApp = function (options) {
|
||||
options = options || {};
|
||||
if (options.template) {
|
||||
options.data = templates[utils.findInArray(templates, 'id', options.template)];
|
||||
}
|
||||
if (!options.data) return;
|
||||
|
||||
var app = clone(options.data);
|
||||
|
||||
// Prepare the clone for use
|
||||
app.id = uuid();
|
||||
app.name = options.name || i18n.get('Untitled App');
|
||||
app.author = model.data.user;
|
||||
|
||||
// Add to model & redirect to editor
|
||||
model.data.apps.unshift(app);
|
||||
return new App(app.id);
|
||||
};
|
||||
|
||||
// Instance Methods
|
||||
App.prototype.insert = function (blockId) {
|
||||
var self = this;
|
||||
var block = newBlock(blockId);
|
||||
|
|
|
@ -13,6 +13,12 @@ auth.on('newuser', function (assertion, email) {
|
|||
auth._email = email;
|
||||
});
|
||||
|
||||
auth.on('logout', function () {
|
||||
auth._assertion = null;
|
||||
auth._email = null;
|
||||
});
|
||||
|
||||
|
||||
auth.on('error', function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
var clone = require('clone');
|
||||
var json = clone(require('./templates.json'));
|
||||
|
||||
module.exports = {
|
||||
featured: json.splice(1, 5),
|
||||
nearby: json.splice(5)
|
||||
};
|
|
@ -16,7 +16,10 @@ module.exports = function (id, username, cb) {
|
|||
'Content-Type': 'application/json'
|
||||
}
|
||||
}, function (err, resp, body) {
|
||||
if (err) return cb(err);
|
||||
if (err) return cb({
|
||||
status: resp.statusCode,
|
||||
message: body
|
||||
});
|
||||
if (!body) return cb('There was no response');
|
||||
cb(null, JSON.parse(body));
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"name": "From Scratch",
|
||||
"icon": "/images/placeholder_blank.png",
|
||||
"author": {
|
||||
"name": "",
|
||||
"username": "",
|
||||
"location": "",
|
||||
"avatar": ""
|
||||
},
|
||||
|
@ -15,7 +15,7 @@
|
|||
"name": "Journalist",
|
||||
"icon": "/images/placeholder_journalist.png",
|
||||
"author": {
|
||||
"name": "Rafid Daoud",
|
||||
"username": "Rafid Daoud",
|
||||
"location": "",
|
||||
"avatar": ""
|
||||
},
|
||||
|
@ -172,7 +172,7 @@
|
|||
"name": "Vendor",
|
||||
"icon": "/images/placeholder_vendor.png",
|
||||
"author": {
|
||||
"name": "Yousuf",
|
||||
"username": "Yousuf",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_vendor.png"
|
||||
},
|
||||
|
@ -297,7 +297,7 @@
|
|||
"name": "Medic",
|
||||
"icon": "/images/placeholder_medic.png",
|
||||
"author": {
|
||||
"name": "Aya",
|
||||
"username": "Aya",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_medic.png"
|
||||
},
|
||||
|
@ -308,7 +308,7 @@
|
|||
"name": "Teacher",
|
||||
"icon": "/images/placeholder_teacher.png",
|
||||
"author": {
|
||||
"name": "Deepa",
|
||||
"username": "Deepa",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_teacher.png"
|
||||
},
|
||||
|
@ -709,7 +709,7 @@
|
|||
"name": "How To",
|
||||
"icon": "/images/placeholder_howto.png",
|
||||
"author": {
|
||||
"name": "Deepa",
|
||||
"username": "Deepa",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_teacher.png"
|
||||
},
|
||||
|
@ -938,7 +938,7 @@
|
|||
"name": "Scientist",
|
||||
"icon": "/images/placeholder_scientist.png",
|
||||
"author": {
|
||||
"name": "Paola",
|
||||
"username": "Paola",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_scientist.png"
|
||||
},
|
||||
|
@ -949,7 +949,7 @@
|
|||
"name": "Family",
|
||||
"icon": "/images/placeholder_family.png",
|
||||
"author": {
|
||||
"name": "Sita",
|
||||
"username": "Sita",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_family.png"
|
||||
},
|
||||
|
@ -960,7 +960,7 @@
|
|||
"name": "Blogger",
|
||||
"icon": "/images/placeholder_blogger.png",
|
||||
"author": {
|
||||
"name": "Emma",
|
||||
"username": "Emma",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_blogger.png"
|
||||
},
|
||||
|
@ -1051,7 +1051,7 @@
|
|||
"name": "Club",
|
||||
"icon": "/images/placeholder_student.png",
|
||||
"author": {
|
||||
"name": "Pedro",
|
||||
"username": "Pedro",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_student.png"
|
||||
},
|
||||
|
@ -1236,7 +1236,7 @@
|
|||
"name": "Safety",
|
||||
"icon": "/images/placeholder_safety.png",
|
||||
"author": {
|
||||
"name": "Pedro",
|
||||
"username": "Pedro",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_student.png"
|
||||
},
|
||||
|
@ -1274,10 +1274,10 @@
|
|||
"name": "Activist",
|
||||
"icon": "/images/placeholder_activist.png",
|
||||
"author": {
|
||||
"name": "Richard",
|
||||
"username": "Richard",
|
||||
"location": "",
|
||||
"avatar": "/images/avatar_activist.png"
|
||||
},
|
||||
"blocks": []
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -259,12 +259,12 @@
|
|||
"message": "I agree to your terms and conditions",
|
||||
"description": "Label for terms and conditions checkbox"
|
||||
},
|
||||
"Choose a different email": {
|
||||
"message": "Choose a different email",
|
||||
"description": "Button for choosing a different email "
|
||||
"Change": {
|
||||
"message": "Change",
|
||||
"description": "Link for changing things, like choosing a different email address for your account"
|
||||
},
|
||||
"share_message": {
|
||||
"message": "Check out the app \"{{app.name}}\" I made with Mozilla Webmaker",
|
||||
"message": "Check out the app {{app.name}} I made with Mozilla Webmaker",
|
||||
"description": "Message that gets sent when a published app is shared to friends"
|
||||
},
|
||||
"App": {
|
||||
|
@ -318,5 +318,33 @@
|
|||
"Discover": {
|
||||
"message": "Discover",
|
||||
"description": "Title for the Discover page (a listing of apps users can choose from)"
|
||||
},
|
||||
"Make & share": {
|
||||
"message": "Make & share",
|
||||
"description": "Part 1 of 2 for sign-in view headline"
|
||||
},
|
||||
"the web": {
|
||||
"message": "the web",
|
||||
"description": "Part 2 of 2 for sign-in view headline"
|
||||
},
|
||||
"Share": {
|
||||
"message": "Share",
|
||||
"description": "Share an app"
|
||||
},
|
||||
"Open": {
|
||||
"message": "Open",
|
||||
"description": "Open an app"
|
||||
},
|
||||
"Create": {
|
||||
"message": "Create",
|
||||
"description": "Create an app"
|
||||
},
|
||||
"Data": {
|
||||
"message": "Data",
|
||||
"description": "Look at the data collected by an app"
|
||||
},
|
||||
"Publish": {
|
||||
"message": "Publish",
|
||||
"description": "Button label for publishing the current app"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "webmaker",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mozillafordevelopment/webmaker-app"
|
||||
|
@ -16,7 +16,7 @@
|
|||
"vue": "0.10.6",
|
||||
"watchjs": "0.0.0",
|
||||
"clone": "0.1.18",
|
||||
"makedrive": "0.0.63",
|
||||
"makedrive": "k88hudson/makedrive#ffos1.3-patch",
|
||||
"webmaker-auth-client": "0.2.8",
|
||||
"xhr": "1.16.1",
|
||||
"gulp": "3.8.8",
|
||||
|
|
Двоичные данные
static/images/avatar_prompt.png
До Ширина: | Высота: | Размер: 4.1 KiB |
После Ширина: | Высота: | Размер: 3.0 KiB |
Двоичные данные
static/images/bg_ftu_1.jpg
До Ширина: | Высота: | Размер: 140 KiB |
Двоичные данные
static/images/blocks_image.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 6.2 KiB |
Двоичные данные
static/images/blocks_phone.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 6.1 KiB |
Двоичные данные
static/images/blocks_sms.png
До Ширина: | Высота: | Размер: 13 KiB После Ширина: | Высота: | Размер: 6.6 KiB |
Двоичные данные
static/images/blocks_text.png
До Ширина: | Высота: | Размер: 13 KiB После Ширина: | Высота: | Размер: 6.5 KiB |
Двоичные данные
static/images/ftu_00.png
До Ширина: | Высота: | Размер: 355 KiB |
Двоичные данные
static/images/ftu_01.png
До Ширина: | Высота: | Размер: 51 KiB |
Двоичные данные
static/images/ftu_2.png
До Ширина: | Высота: | Размер: 187 KiB |
Двоичные данные
static/images/ftu_3.png
До Ширина: | Высота: | Размер: 75 KiB |
Двоичные данные
static/images/placeholder_activist.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 8.5 KiB |
Двоичные данные
static/images/placeholder_blank.png
До Ширина: | Высота: | Размер: 5.1 KiB После Ширина: | Высота: | Размер: 2.4 KiB |
Двоичные данные
static/images/placeholder_blogger.png
До Ширина: | Высота: | Размер: 8.2 KiB После Ширина: | Высота: | Размер: 5.9 KiB |
Двоичные данные
static/images/placeholder_family.png
До Ширина: | Высота: | Размер: 12 KiB После Ширина: | Высота: | Размер: 8.3 KiB |
Двоичные данные
static/images/placeholder_howto.png
До Ширина: | Высота: | Размер: 11 KiB После Ширина: | Высота: | Размер: 3.6 KiB |
Двоичные данные
static/images/placeholder_journalist.png
До Ширина: | Высота: | Размер: 7.5 KiB После Ширина: | Высота: | Размер: 3.9 KiB |
Двоичные данные
static/images/placeholder_medic.png
До Ширина: | Высота: | Размер: 6.1 KiB После Ширина: | Высота: | Размер: 3.0 KiB |
Двоичные данные
static/images/placeholder_puppy.png
До Ширина: | Высота: | Размер: 11 KiB После Ширина: | Высота: | Размер: 7.7 KiB |
Двоичные данные
static/images/placeholder_safety.png
До Ширина: | Высота: | Размер: 9.7 KiB После Ширина: | Высота: | Размер: 3.1 KiB |
Двоичные данные
static/images/placeholder_scientist.png
До Ширина: | Высота: | Размер: 13 KiB После Ширина: | Высота: | Размер: 8.4 KiB |
Двоичные данные
static/images/placeholder_student.png
До Ширина: | Высота: | Размер: 11 KiB После Ширина: | Высота: | Размер: 7.3 KiB |
Двоичные данные
static/images/placeholder_teacher.png
До Ширина: | Высота: | Размер: 10 KiB После Ширина: | Высота: | Размер: 7.2 KiB |
Двоичные данные
static/images/placeholder_vendor.png
До Ширина: | Высота: | Размер: 9.7 KiB После Ширина: | Высота: | Размер: 6.9 KiB |
|
@ -11,7 +11,9 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><div v-view="currentView" class="main"></div></div>
|
||||
<div id="app">
|
||||
<div v-view="currentView" class="main"></div>
|
||||
</div>
|
||||
<script src="https://login.persona.org/include.js"></script>
|
||||
<script src="/index.js"></script>
|
||||
<!-- Analytics -->
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
padding: 14px 0;
|
||||
color: @turquoise;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-image: none;
|
||||
background-color: #FFF;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
|
|
@ -26,12 +26,12 @@ body, html {
|
|||
#app {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
.header-space;
|
||||
.footer-space;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
|
|
|
@ -13,7 +13,17 @@
|
|||
|
||||
// For .main containers
|
||||
.header-space() {
|
||||
padding: @header-height 0 @footer-height 0;
|
||||
padding: @header-height 0 0;
|
||||
}
|
||||
.footer-space() {
|
||||
margin-bottom: @tabBar-height;
|
||||
}
|
||||
|
||||
// For full pages
|
||||
.full-page() {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Headings for list-cell, forms
|
||||
|
@ -36,3 +46,9 @@
|
|||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.text-truncate() {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@fonts-path: '/fonts';
|
||||
@header-height: 50px;
|
||||
@footer-height: 70px;
|
||||
@tabBar-height: 46px;
|
||||
@editBar-height: 70px;
|
||||
@body-padding: 14px;
|
||||
|
||||
/*********************************************************
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
var mockrequire = require('mockrequire');
|
||||
var assert = require('assert');
|
||||
|
||||
var templates = require('../../lib/templates.json');
|
||||
var mockId = '000d1745-5d3c-4997-ac0c-15df68bbbecz';
|
||||
var mockModelInstance = {
|
||||
data: { apps: [
|
||||
|
@ -8,7 +10,7 @@ var mockModelInstance = {
|
|||
name: 'Sample App',
|
||||
icon: '/images/placeholder_puppy.png',
|
||||
author: {
|
||||
name: 'Andrew',
|
||||
username: 'Andrew',
|
||||
location: 'Portland',
|
||||
avatar: '/images/avatar_puppy.png'
|
||||
},
|
||||
|
@ -48,12 +50,17 @@ var mockBlocks = function (id) {
|
|||
var App = mockrequire('../../lib/app', {
|
||||
'./model': mockModel,
|
||||
'./blocks': mockBlocks,
|
||||
'clone': require('clone')
|
||||
'clone': require('clone'),
|
||||
'./i18n': {
|
||||
get: function(key) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var app = new App(mockId);
|
||||
|
||||
describe('App', function () {
|
||||
describe('App instance', function () {
|
||||
describe('interface', function () {
|
||||
it('should have expected properties', function () {
|
||||
assert.equal(app.id, mockId);
|
||||
|
@ -93,4 +100,24 @@ describe('App', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('App', function () {
|
||||
describe('#createApp', function () {
|
||||
it('should be a function', function () {
|
||||
assert.equal(typeof App, 'function');
|
||||
});
|
||||
it('should return undefined when no template or data is passed in', function () {
|
||||
assert.equal(typeof App.createApp(), 'undefined');
|
||||
assert.equal(typeof App.createApp({template: 'notrealid'}), 'undefined');
|
||||
});
|
||||
it('should return an app instance for valid template id', function () {
|
||||
var template = templates[2];
|
||||
var app = App.createApp({template: template.id, name: 'Bob is my cat'});
|
||||
assert.ok(app instanceof App);
|
||||
assert.ok(app.id && app.id !== template.id);
|
||||
assert.equal(app.data.name, 'Bob is my cat');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ var authorSchema = {
|
|||
pattern: '(^/images/.*)|'
|
||||
}
|
||||
},
|
||||
required: ['name', 'location', 'avatar']
|
||||
required: ['username', 'location', 'avatar']
|
||||
};
|
||||
|
||||
// Template schemas
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#add {
|
||||
padding: 0;
|
||||
.full-page;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
|
@ -5,22 +5,28 @@
|
|||
<img class="app-icon" src="{{app.icon}}" alt="{{app.name}}">
|
||||
<hgroup>
|
||||
<h3>{{app.name}}</h3>
|
||||
<p class="text-light">by Kate</p>
|
||||
<p class="text-light" v-bind-html="'by _'" v-with="name: app.author.username"></p>
|
||||
</hgroup>
|
||||
</header>
|
||||
<ul id="detail-share">
|
||||
<li><a href="/make/{{id}}/share">
|
||||
<span class="fa fa-2x fa-fw fa-share-alt"></span><br>Share</a>
|
||||
<li v-if="!isTemplate"><a href="/make/{{id}}/share">
|
||||
<span class="fa fa-2x fa-fw fa-share-alt"></span><br>{{ 'Share' | i18n }}</a>
|
||||
</li>
|
||||
<li><a href="/make/{{id}}/play">
|
||||
<span class="fa fa-2x fa-fw fa-eye"></span><br>Open</a>
|
||||
<li v-if="!isTemplate"><a href="/make/{{id}}/play">
|
||||
<span class="fa fa-2x fa-fw fa-eye"></span><br>{{ 'Open' | i18n }}</a>
|
||||
</li>
|
||||
<li v-if="isTemplate"><a href="" class="disabled">
|
||||
<span class="fa fa-2x fa-fw fa-eye"></span><br>{{ 'Preview' | i18n }}</a>
|
||||
</li>
|
||||
<li v-if="isTemplate"><a href="#" v-on="click: create">
|
||||
<span class="fa fa-2x fa-fw fa-edit"></span><br>{{ 'Create' | i18n }}</a>
|
||||
</li>
|
||||
<li><a href="" class="disabled">
|
||||
<span class="fa fa-2x fa-fw fa-list"></span><br>Data</a>
|
||||
<span class="fa fa-2x fa-fw fa-list"></span><br>{{ 'Data' | i18n }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p v-if="app.url" class="text-center">
|
||||
<a href="{{app.url}}">View published app</a>
|
||||
<a target="_blank" href="{{app.url}}">View published app</a>
|
||||
</p>
|
||||
</div>
|
||||
<div v-component="tabBar"></div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
var App = require('../../lib/app');
|
||||
var view = require('../../lib/view');
|
||||
var templates = require('../../lib/templates.json');
|
||||
var utils = require('../../lib/utils');
|
||||
|
||||
module.exports = view.extend({
|
||||
id: 'detail',
|
||||
|
@ -8,14 +10,27 @@ module.exports = view.extend({
|
|||
back: true,
|
||||
title: 'App'
|
||||
},
|
||||
methods: {
|
||||
create: function () {
|
||||
var self = this;
|
||||
var app = App.createApp({
|
||||
data: self.$data.app
|
||||
});
|
||||
self.page('/make/' + app.id + '/edit');
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
var self = this;
|
||||
// Fetch app
|
||||
var id = self.$parent.$data.params.id;
|
||||
var app = new App(id);
|
||||
var app = new App(id).data;
|
||||
if (!app) {
|
||||
app = templates[utils.findInArray(templates, 'id', id)] || {};
|
||||
self.$data.isTemplate = true;
|
||||
}
|
||||
|
||||
// Bind app
|
||||
self.$data.id = id;
|
||||
self.$data.app = app.data || {};
|
||||
self.$data.app = app;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ TODO: Restore this toggle when we have implemented 'nearby' view.
|
|||
|
||||
<ul class="list-cell" v-show="mode === 'featured'">
|
||||
<lh>{{ "Featured Apps" | i18n }}</lh>
|
||||
<li v-component="appCell" v-repeat="apps.featured"></li>
|
||||
<li v-component="appCell" v-repeat="apps.featured" mode="detail"></li>
|
||||
</ul>
|
||||
|
||||
<ul class="list-cell" v-show="mode === 'nearby'">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
var view = require('../../lib/view');
|
||||
var fakeDiscovery = require('../../lib/fake-discovery.json');
|
||||
var fakeDiscovery = require('../../lib/fake-discovery');
|
||||
|
||||
module.exports = view.extend({
|
||||
id: 'discover',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<div v-if="!app.id" v-component="alert" type="error" message="errorAppNotFound"></div>
|
||||
<div v-if="app.id">
|
||||
<div v-component="navigationBar" v-with="title: 'Edit'"></div>
|
||||
<input id="name-input" type="text" v-model="app.name">
|
||||
<a class="add" href="/make/{{app.id}}/add">+</a>
|
||||
<div id="blocks">
|
||||
|
|
|
@ -7,7 +7,8 @@ module.exports = view.extend({
|
|||
id: 'edit',
|
||||
template: require('./index.html'),
|
||||
data: {
|
||||
cancel: '/profile'
|
||||
back: '/profile',
|
||||
doneLabel: 'Publish'
|
||||
},
|
||||
created: function () {
|
||||
var self = this;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#edit {
|
||||
margin-bottom: @editBar-height;
|
||||
a.add {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -6,7 +7,7 @@
|
|||
width: 95%;
|
||||
height: 44px;
|
||||
|
||||
margin: 21px 2.5% 15px 2.5%;
|
||||
margin: 15px 2.5% 15px 2.5%;
|
||||
border: 1px dashed @highlight;
|
||||
background-color: transparent;
|
||||
|
||||
|
@ -34,19 +35,18 @@
|
|||
}
|
||||
|
||||
#name-input {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
width: 95%;
|
||||
height: 50px;
|
||||
|
||||
color: @highlight;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-size: 1.1em;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
margin: 15px 2.5% 0;
|
||||
border: 1px dashed @turquoise;
|
||||
|
||||
&:focus {
|
||||
border: 1px dashed @turquoise;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ module.exports = view.extend({
|
|||
message = 'error404';
|
||||
break;
|
||||
default:
|
||||
message = 'defaultError'
|
||||
message = 'defaultError';
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#error {
|
||||
.full-page;
|
||||
padding-top: 0;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#loader {
|
||||
.full-page;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
var App = require('../../lib/app');
|
||||
var templates = require('../../lib/templates.json');
|
||||
var view = require('../../lib/view');
|
||||
var Data = require('../../lib/data');
|
||||
|
||||
|
@ -7,7 +6,8 @@ module.exports = view.extend({
|
|||
id: 'play',
|
||||
template: require('./index.html'),
|
||||
data: {
|
||||
cancel: '/profile'
|
||||
back: '/profile',
|
||||
doneLabel: 'Publish'
|
||||
},
|
||||
created: function () {
|
||||
var self = this;
|
||||
|
@ -19,7 +19,7 @@ module.exports = view.extend({
|
|||
// Bind app
|
||||
self.$data.app = app.data || {};
|
||||
|
||||
self.$data.onDone = '/make/' + id + '/share';
|
||||
self.$data.onDone = '/make/' + id + '/share?publish=true';
|
||||
|
||||
// Listen for Data Submitted by the User
|
||||
var data = new Data(id);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#play {
|
||||
margin-bottom: @editBar-height;
|
||||
}
|
|
@ -1,18 +1,15 @@
|
|||
<div v-component="navigationBar"></div>
|
||||
|
||||
<form v-show="!isPublishing">
|
||||
<h3 v-show="app.url" class="section">URL</h3>
|
||||
<div v-show="app.url" class="form-group">
|
||||
<a href="{{app.url}}">{{app.url}}</a>
|
||||
|
||||
<div v-show="error" v-component="alert" type="error" message="{{error}}"></div>
|
||||
<div v-show="!error">
|
||||
<h3 class="section">Share via SMS</h3>
|
||||
<div v-show="user.username" class="form-group">
|
||||
<textarea name="share-message" v-model="shareMessage" class="textarea-large"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="section">Share by SMS</h3>
|
||||
<div class="form-group" v-show="!user.username">
|
||||
<button v-on="click: login" class="btn btn-block">Log in or sign up</button>
|
||||
</div>
|
||||
<div v-show="user.username" class="form-group">
|
||||
<textarea name="share-message" v-model="shareMessage" class="textarea-large"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-show="isPublishing" id="loader">
|
||||
|
|
|
@ -5,20 +5,32 @@ var publish = require('../../lib/publish');
|
|||
var auth = require('../../lib/auth');
|
||||
var page = require('page');
|
||||
|
||||
var PUBLISH_TIMEOUT = 20000;
|
||||
|
||||
module.exports = view.extend({
|
||||
id: 'share',
|
||||
template: require('./index.html'),
|
||||
data: {
|
||||
title: 'Share',
|
||||
cancel: true,
|
||||
error: false,
|
||||
isPublishing: true,
|
||||
doneDisabled: true
|
||||
},
|
||||
methods: {
|
||||
login: function (e) {
|
||||
e.preventDefault();
|
||||
auth.login();
|
||||
},
|
||||
onDone: function () {
|
||||
var self = this;
|
||||
if (!self.$data.app.url) return;
|
||||
var sms = 'sms:?body=' + encodeURIComponent(self.$data.shareMessage);
|
||||
window.location = sms;
|
||||
page('/make/' + self.$parent.$data.params.id + '/detail');
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
ready: function () {
|
||||
var self = this;
|
||||
|
||||
// Fetch app
|
||||
|
@ -33,33 +45,47 @@ module.exports = view.extend({
|
|||
|
||||
// Share message
|
||||
var message = i18n.get('share_message').replace('{{app.name}}', app.data.name);
|
||||
self.$data.shareMessage = message;
|
||||
self.$data.shareMessage = message + ': ' + app.data.url;
|
||||
|
||||
if (!global.location.search.match('publish=true') && app.data.url) {
|
||||
self.$data.isPublishing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Publish
|
||||
console.log('Starting publish...');
|
||||
self.$data.doneDisabled = true;
|
||||
|
||||
var sync = self.model._sync;
|
||||
self.$data.onDone = function () {
|
||||
if (self.$data.isPublishing) return;
|
||||
var isSynced = false;
|
||||
var syncTimeout;
|
||||
|
||||
function onSynced() {
|
||||
publish(id, self.$data.user.username, function (err, data) {
|
||||
self.$data.isPublishing = false;
|
||||
if (err) return console.error(err);
|
||||
app.data.url = data.url;
|
||||
var sms = 'sms:?body=' + encodeURI(self.$data.shareMessage) + ' ' + data.url;
|
||||
window.location = sms;
|
||||
page('/make/' + id + '/detail');
|
||||
});
|
||||
}
|
||||
function onSynced() {
|
||||
publish(id, self.$data.user.username, function (err, data) {
|
||||
global.clearTimeout(syncTimeout);
|
||||
self.$data.isPublishing = false;
|
||||
if (err) {
|
||||
console.error(err);
|
||||
self.$data.error = (err.status || 'Error') + ': ' + err.message;
|
||||
return;
|
||||
}
|
||||
console.log('Published!');
|
||||
self.$data.error = false;
|
||||
self.$data.doneDisabled = false;
|
||||
app.data.url = data.url;
|
||||
self.$data.shareMessage = message + ': ' + data.url;
|
||||
});
|
||||
}
|
||||
|
||||
// Show spinner
|
||||
self.$data.isPublishing = true;
|
||||
syncTimeout = global.setTimeout(function() {
|
||||
console.log('timed out');
|
||||
self.$data.isPublishing = false;
|
||||
self.$data.error = 'Oops! Your publish is taking too long';
|
||||
}, PUBLISH_TIMEOUT);
|
||||
|
||||
// Sync makedrive - todo. Request doesn't seem to work
|
||||
// sync.once('completed', onSynced);
|
||||
// sync.request();
|
||||
onSynced();
|
||||
onSynced();
|
||||
|
||||
};
|
||||
// sync.once('completed', onSynced);
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
<div class="top">
|
||||
<h1>{{ 'Make & share' | i18n }}</br>{{ 'the web' | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="bottom">
|
||||
<div v-if="!offline" >
|
||||
<a v-if="username" href="/profile" class="btn-next">{{ 'Get Started' | i18n }}</a>
|
||||
|
|
|
@ -1,9 +1,31 @@
|
|||
#sign-in {
|
||||
.full-page;
|
||||
.header-space;
|
||||
background: @turquoise;
|
||||
background-image: url(/images/background.gif);
|
||||
background-size: 400px;
|
||||
background-position: 0px -20px;
|
||||
color: @white;
|
||||
text-align: center;
|
||||
|
||||
.top {
|
||||
width: 100%;
|
||||
margin: 5% auto 0 auto;
|
||||
padding: @body-padding;
|
||||
|
||||
.animated;
|
||||
.fadeIn;
|
||||
|
||||
h1 {
|
||||
font-weight: 400;
|
||||
font-size: 2.6em;
|
||||
line-height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
padding: @body-padding;
|
||||
background: @turquoise;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
<div v-component="navigationBar"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">{{ 'Email' | i18n }}</label>
|
||||
<input type="text" id="email" v-model="email">
|
||||
<p>
|
||||
<button class="btn" v-on="click: login">{{ 'Choose a different email' | i18n }}</button>
|
||||
</p>
|
||||
<div class="email">
|
||||
<h3>{{ 'Email' | i18n }}</h3>
|
||||
{{ email || 'your@email.com' }} - <a v-on="click: login">{{ 'Change' | i18n }}</a>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="username">{{ 'Choose a username' | i18n }}</label>
|
||||
<input type="text" id="username" v-model="user.username" v-on="keyup: checkUsernameExists">
|
||||
<div class="error" id="usernameTaken" v-if="show: errors.usernameTaken">
|
||||
<div class="form-error" id="usernameTaken" v-if="show: errors.usernameTaken">
|
||||
{{'Sorry, that name has already been snagged! Please try another.' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="checkbox">
|
||||
<label for="mailing-list">
|
||||
<input type="checkbox" id="mailing-list" v-model="user.mailingList">
|
||||
|
|
|
@ -16,15 +16,19 @@ module.exports = view.extend({
|
|||
usernameTaken: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
doneDisabled: function () {
|
||||
var self = this;
|
||||
return !self.$data.user.username ||
|
||||
!self.$data.user.terms ||
|
||||
!auth._assertion ||
|
||||
self.$data.errors.usernameTaken;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDone: function () {
|
||||
var self = this;
|
||||
if (!self.$data.user.username) {
|
||||
return;
|
||||
}
|
||||
if (!self.$data.user.terms) {
|
||||
return;
|
||||
}
|
||||
if (self.$data.doneDisabled) return;
|
||||
|
||||
auth.createUser({
|
||||
assertion: auth._assertion,
|
||||
|
@ -59,5 +63,10 @@ module.exports = view.extend({
|
|||
auth.on('newuser', function (assertion, email) {
|
||||
self.$data.email = email;
|
||||
});
|
||||
setTimeout(function() {
|
||||
auth.logout();
|
||||
self.model.offlineConnect();
|
||||
self.page('/sign-in');
|
||||
}, 60000);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#sign-up {
|
||||
.email {
|
||||
font-size: 14px;
|
||||
padding: 14px;
|
||||
color: #638093;
|
||||
|
||||
h3 {
|
||||
font-size: inherit;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,5 +2,4 @@
|
|||
<ul>
|
||||
<li v-component="templateCell" v-repeat="templates"></li>
|
||||
</ul>
|
||||
<div class="tab-bar-space"> </div>
|
||||
<div v-component="tabBar"></div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
var templates = require('../../lib/templates.json');
|
||||
var uuid = require('../../lib/uuid');
|
||||
var view = require('../../lib/view');
|
||||
var i18n = require('../../lib/i18n');
|
||||
var App = require('../../lib/app');
|
||||
|
||||
module.exports = view.extend({
|
||||
id: 'templates',
|
||||
|
@ -13,30 +12,13 @@ module.exports = view.extend({
|
|||
ready: function () {
|
||||
var self = this;
|
||||
|
||||
// Template clone helper
|
||||
function getTemplateClone (id) {
|
||||
for (var i = 0; i < templates.length; i++) {
|
||||
if (templates[i].id === id) {
|
||||
return JSON.parse(JSON.stringify(templates[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Click handler
|
||||
function clickHandler (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = e.currentTarget.getAttribute('data-id');
|
||||
var clone = getTemplateClone(id);
|
||||
|
||||
// Prepare the clone for use
|
||||
clone.id = uuid();
|
||||
clone.name = i18n.get('Untitled App');
|
||||
clone.author = self.model.data.user;
|
||||
|
||||
// Add to model & redirect to editor
|
||||
self.model.data.apps.unshift(clone);
|
||||
self.page('/make/' + clone.id + '/play');
|
||||
var app = App.createApp({template: id});
|
||||
self.page('/make/' + app.id + '/edit');
|
||||
}
|
||||
|
||||
// Apply click handler to each cell
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
#templates {
|
||||
padding: 50px 0 @tabBar-height 0;
|
||||
}
|
||||
|
||||
#templates ul {
|
||||
margin: 0 0 0 0; //Bottom margin as much as bottom tabbar.
|
||||
padding: 0;
|
||||
.clearfix;
|
||||
}
|
||||
|
||||
.tab-bar-space {
|
||||
width: 100%;
|
||||
height: @header-height;
|
||||
}
|
||||
|
|