working on super simple offline behavior

This commit is contained in:
Kyle Mahan 2016-06-05 13:46:28 -07:00
parent 9fb14910d2
commit f8e4ce1d62
13 changed files with 249 additions and 1516 deletions

View file

@ -1,5 +1,8 @@
$(function(){
if (navigator.serviceWorker) {
navigator.serviceWorker.register('./sw.js')
}
$(function(){
function updateTimestamps() {
$(".permalink time").each(function() {
var absolute = $(this).attr('datetime');
@ -20,22 +23,6 @@ $(function(){
});
}
function clickShowReplyForm(evt) {
var a = $(this);
evt.preventDefault();
$(".like-form", a.parent()).hide();
$(".reply-form", a.parent()).toggle();//css('display', 'inherit');
//a.css('display', 'none');
}
function clickShowLikeForm(evt) {
var a = $(this);
evt.preventDefault();
$(".reply-form", a.parent()).hide();
$(".like-form", a.parent()).toggle();
//a.css('display', 'none');
}
function submitMicropubForm(evt) {
evt.preventDefault();

70
frontend/indieconfig.js Normal file
View file

@ -0,0 +1,70 @@
/*jslint browser: true, plusplus: true, vars: true, indent: 2 */
window.loadIndieConfig = (function () {
'use strict';
// Indie-Config Loading script
// by Pelle Wessman, voxpelli.com
// MIT-licensed
// http://indiewebcamp.com/indie-config
var config, configFrame, configTimeout,
callbacks = [],
handleConfig, parseConfig;
// When the configuration has been loaded deregister all loading mechanics and call all callbacks
handleConfig = function () {
config = config || {};
configFrame.parentNode.removeChild(configFrame);
configFrame = undefined;
window.removeEventListener('message', parseConfig);
clearTimeout(configTimeout);
while (callbacks[0]) {
callbacks.shift()(config);
}
};
// When we receive a message, check if the source is right and try to parse it
parseConfig = function (message) {
var correctSource = (configFrame && message.source === configFrame.contentWindow);
if (correctSource && config === undefined) {
try {
config = JSON.parse(message.data);
} catch (ignore) {}
handleConfig();
}
};
return function (callback) {
// If the config is already loaded, call callback right away
if (config) {
callback(config);
return;
}
// Otherwise add the callback to the queue
callbacks.push(callback);
// Are we already trying to load the Indie-Config, then wait
if (configFrame) {
return;
}
// Create the iframe that will load the Indie-Config
configFrame = document.createElement('iframe');
configFrame.src = 'web+action:load';
document.getElementsByTagName('body')[0].appendChild(configFrame);
configFrame.style.display = 'none';
// Listen for messages so we will catch the Indie-Config message
window.addEventListener('message', parseConfig);
// And if no such Indie-Config message has been loaded in a while, abort the loading
configTimeout = setTimeout(handleConfig, 3000);
};
}());

16
frontend/manifest.json Normal file
View file

@ -0,0 +1,16 @@
{
"name": "Woodwind",
"short_name": "Woodwind",
"start_url": "/",
"scope": "/",
"display": "standalone",
"theme_color": "#9b6137",
"background_color": "#9b6137",
"icons": [
{
"src": "/static/logo.png",
"sizes": "512x512",
"type": "image/png",
}
]
}

33
frontend/package.json Normal file
View file

@ -0,0 +1,33 @@
{
"name": "woodwind-fe",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "mocha"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kylewm/woodwind.git"
},
"keywords": [
"indieweb",
"reader"
],
"author": "Kyle Mahan",
"license": "BSD-2-Clause",
"bugs": {
"url": "https://github.com/kylewm/woodwind/issues"
},
"homepage": "https://github.com/kylewm/woodwind#readme",
"devDependencies": {
"babel": "^6.5.2",
"babel-cli": "^6.9.0",
"babel-preset-es2015": "^6.9.0",
"mocha": "^2.5.3"
},
"dependencies": {
"jquery": "^2.2.4",
"moment": "^2.13.0"
}
}

31
frontend/sw.js Normal file
View file

@ -0,0 +1,31 @@
var version = 'v2';
this.addEventListener('install', function (event) {
event.waitUntil(
caches.open(version).then(function (cache) {
return cache.addAll([
'/static/logo.png',
'/static/style.css',
'/offline',
])
})
);
})
this.addEventListener('fetch', function (event) {
console.log('caught fetch: ' + event)
event.respondWith(
caches.match(event.request)
.then(function (response) {
console.log('cache got response: ' + response)
return response || fetch(event.request);
})
.then(function (response) {
console.log('fetch got response: ' + response)
return response
})
.catch(function (err) {
return caches.match('/offline')
})
)
})

63
frontend/webaction.js Normal file
View file

@ -0,0 +1,63 @@
/*jslint browser: true, plusplus: true, vars: true, indent: 2 */
(function () {
'use strict';
var loadingClassRegexp = /(^|\s)indieconfig-loading(\s|$)/;
var doTheAction = function (indieConfig) {
var href, action, anchors;
// Don't block the tag anymore as the queued action is now handled
this.className = this.className.replace(loadingClassRegexp, ' ');
// Pick the correct endpoint for the correct action
action = this.getAttribute('do');
href = indieConfig[action];
// If no endpoint is found, try the URL of the first a-tag within it
if (!href) {
anchors = this.getElementsByTagName('a');
if (anchors[0]) {
href = anchors[0].href;
}
}
// We have found an endpoint!
if (href) {
//Resolve a relative target
var target = document.createElement('a');
target.href = this.getAttribute('with');
target = target.href;
// Insert the target into the endpoint
href = href.replace('{url}', encodeURIComponent(target || window.location.href));
// And redirect to it
window.open( href, '_blank');
}
};
// Event handler for a click on an indie-action tag
var handleTheAction = function (e) {
// Prevent the default of eg. any a-tag fallback within the indie-action tag
e.preventDefault();
// Make sure this tag hasn't already been queued for the indieconfig-load
if (!loadingClassRegexp.test(this.className)) {
this.className += ' indieconfig-loading';
// Set "doTheAction" to be called when the indie-config has been loaded
window.loadIndieConfig(doTheAction.bind(this));
}
};
// Once the page is loased add click event listeners to all indie-action tags
window.addEventListener('DOMContentLoaded', function () {
var actions = document.querySelectorAll('indie-action'),
i,
length = actions.length;
for (i = 0; i < length; i++) {
actions[i].addEventListener('click', handleTheAction);
}
});
}());

View file

@ -8,11 +8,11 @@ db = SQLAlchemy()
micropub = MicropubClient(client_id='https://woodwind.xyz/')
login_mgr = LoginManager()
login_mgr.login_view = 'views.index'
toolbar = DebugToolbarExtension()
#toolbar = DebugToolbarExtension()
def init_app(app):
db.init_app(app)
micropub.init_app(app)
login_mgr.init_app(app)
toolbar.init_app(app)
# toolbar.init_app(app)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -10,9 +10,11 @@
<link rel="stylesheet" href="{{ url_for('static', filename='style.css', version='2016-03-08') }}"/>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"/>
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="{{ url_for('static', filename='moment.min.js') }}"></script>
<script src="{{ url_for('static', filename='cassis.js') }}"></script>
<link rel="manifest" href="/manifest.json"/>
<script src="/node_modules/jquery/dist/jquery.js"></script>
<script src="/node_modules/moment/moment.js"></script>
{% block head %}{% endblock %}
</head>
@ -52,15 +54,18 @@
<div class="flash">{{ message }}</div>
{% endfor %}
{% if not current_user.is_authenticated %}
<form action="{{ url_for('.login') }}" method="POST">
<input type="url" name="me" placeholder="https://yourdomain.com" />
<input type="hidden" name="next" placeholder="{{ request.path }}" />
<button style="text-align: right;" type="submit">Login</button>
</form>
Your Woodwind account is tied to your personal domain name. Check out IndieWebCamp's <a href="http://indiewebcamp.com/Getting_Started" target="_blank">Getting Started</a> page for details.
{% block login %}
{% endif %}
{% if not current_user.is_authenticated %}
<form action="{{ url_for('.login') }}" method="POST">
<input type="url" name="me" placeholder="https://yourdomain.com" />
<input type="hidden" name="next" placeholder="{{ request.path }}" />
<button style="text-align: right;" type="submit">Login</button>
</form>
Your Woodwind account is tied to your personal domain name. Check out IndieWebCamp's <a href="http://indiewebcamp.com/Getting_Started" target="_blank">Getting Started</a> page for details.
{% endif %}
{% endblock login %}
{% block header %}{% endblock %}

View file

@ -4,7 +4,7 @@
{% if ws_topic %}
<script>var WS_TOPIC = "{{ ws_topic }}";</script>
{% endif %}
<script src="{{url_for('static', filename='feed.js', version='2016-05-21')}}"></script>
<script src="/feed.js"></script>
{% if current_user and current_user.settings
and current_user.settings.get('reply-method') == 'indie-config' %}

View file

@ -0,0 +1,9 @@
{% extends "base.jinja2" %}
{% block login %}{% endblock login %}
{% block body %}
Offline, and it feels so good
{% endblock body %}

View file

@ -24,6 +24,10 @@ IMAGE_TAG_RE = re.compile(r'<img([^>]*) src="(https?://[^">]+)"')
views = flask.Blueprint('views', __name__)
@views.route('/offline')
def offline():
return flask.render_template('offline.jinja2')
@views.route('/')
def index():