Browse Source

Overhaul JS, make sidebar a template

mistress
Daniel Muckerman 4 years ago
parent
commit
950131c9b0
10 changed files with 206 additions and 202 deletions
  1. +8
    -13
      app.py
  2. BIN
      pocket/readitlater.db
  3. +37
    -0
      static/add_modal.js
  4. +16
    -0
      static/style.css
  5. +53
    -0
      templates/bookmarklet.j2
  6. +16
    -0
      templates/fragments/add_modal.j2
  7. +56
    -0
      templates/fragments/sidebar.j2
  8. +6
    -40
      templates/list.j2
  9. +0
    -148
      templates/save.j2
  10. +14
    -1
      utils.py

+ 8
- 13
app.py View File

@ -4,12 +4,10 @@ from ldap3 import Server, Connection, ALL, MODIFY_REPLACE
from flask import Flask, g, request, session, redirect, url_for, render_template from flask import Flask, g, request, session, redirect, url_for, render_template
from flask_simpleldap import LDAP from flask_simpleldap import LDAP
from flask_bootstrap import Bootstrap from flask_bootstrap import Bootstrap
from readabilipy import simple_json_from_html_string
import os import os
import sqlite3 import sqlite3
import requests import requests
from requests.api import head
from utils import clean_articles
from utils import clean_articles, get_article
app = Flask(__name__) app = Flask(__name__)
Bootstrap(app) Bootstrap(app)
@ -65,7 +63,7 @@ def index():
conn.commit() conn.commit()
conn.close() conn.close()
return render_template('list.j2', articles = clean_articles(rows))
return render_template('list.j2', articles = rows)
@app.route('/archived') @app.route('/archived')
@ -81,13 +79,13 @@ def archived():
conn.commit() conn.commit()
conn.close() conn.close()
return render_template('list.j2', articles = clean_articles(rows))
return render_template('list.j2', articles = rows)
@app.route('/save')
@app.route('/bookmarklet')
@ldap.login_required @ldap.login_required
def save():
return render_template('save.j2')
def bookmarklet():
return render_template('bookmarklet.j2')
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
@ -145,16 +143,13 @@ def add_url():
c = conn.cursor() c = conn.cursor()
if url is not None and len(url) > 0: if url is not None and len(url) > 0:
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
response = requests.get(url, headers=headers)
article = simple_json_from_html_string(response.text, use_readability=True)
article, short_domain = get_article(url)
c.execute("SELECT * FROM articles WHERE url=?", (url,)) c.execute("SELECT * FROM articles WHERE url=?", (url,))
rows = c.fetchall() rows = c.fetchall()
if (len(rows) == 0): if (len(rows) == 0):
c.execute("INSERT INTO articles (url, content, title, byline) VALUES (?, ?, ?, ?)", (url, article['content'], article['title'], article['byline']))
c.execute("INSERT INTO articles (url, content, title, byline) VALUES (?, ?, ?, ?)", (url, article['content'], article['title'], short_domain))
c.execute("SELECT * FROM articles WHERE url=?", (url,)) c.execute("SELECT * FROM articles WHERE url=?", (url,))
rows = c.fetchall() rows = c.fetchall()

BIN
pocket/readitlater.db View File


+ 37
- 0
static/add_modal.js View File

@ -0,0 +1,37 @@
function openLinkModal() {
$('#addLinkModal').modal('show');
}
function addUrl() {
$("#link-btn").prop("disabled", true);
url = $('#link-form').val();
if (url.trim().length === 0) {
showError('URL is required');
return false;
}
$.ajax({
url: '/add',
method: 'POST',
data: { "url": url },
success: function(data) {
$('#link-form').val('');
$("#link-btn").prop("disabled", false);
if (data !== 'Error')
window.location.reload();
else
showError('URL cannot be empty');
}
});
}
function showError(error) {
$('#error-alert').text(error);
$('#error-alert').show();
}
$('#addLinkModal').on('shown.bs.modal', function () {
$('#link-form').trigger('focus');
});

+ 16
- 0
static/style.css View File

@ -39,6 +39,14 @@
color: black; color: black;
} }
#sidebar div > a {
color: rgba(0,0,0,.5);
}
#sidebar div.active > a {
color: black;
}
#content { #content {
width: 100%; width: 100%;
padding: 20px; padding: 20px;
@ -64,6 +72,14 @@
#sidebar ul > li.active > a { #sidebar ul > li.active > a {
color: white; color: white;
} }
#sidebar div > a {
color: rgba(255,255,255,.5);
}
#sidebar div.active > a {
color: white;
}
body { body {
background-color: #111 !important; background-color: #111 !important;

+ 53
- 0
templates/bookmarklet.j2 View File

@ -0,0 +1,53 @@
{% extends "bootstrap/base.html" %}
{% block title %}Read TI Later{% endblock %}
{% block styles %}
{{super()}}
<link rel="stylesheet" href="{{url_for('.static', filename='style.css')}}">
{% endblock %}
{% block navbar %}
<div id="navbar" class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
<div class="navbar-brand">Read TI Later</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown"
aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('index') }}">My List</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('archived') }}">Archived</a>
</li>
</ul>
<form class="form-inline ml-auto">
<a class="btn btn-primary" href="/logout" role="button">Logout</a>
</form>
</div>
</div>
{% endblock %}
{% block content %}
<div class="wrapper">
<!-- Sidebar -->
<div id="sidebar">
{% include "fragments/sidebar.j2" %}
</div>
<!-- Page Content -->
<div id="content">
<div>
<p style="text-align: center;">Would you rather save on the go? Try our bookmarklet!</p>
<code>
javascript:(function(){var%20url%20=%20location.href;var%20otherWindow=window.open('about:blank','_blank');otherWindow.opener=null;otherWindow.location='https://read.technicalincompetence.club/add?close=1&url='+encodeURIComponent(url);})();
</code>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
{% endblock %}

+ 16
- 0
templates/fragments/add_modal.j2 View File

@ -0,0 +1,16 @@
<div class="modal fade" id="addLinkModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Link</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input id="link-form" type="text" class="form-control" placeholder="https://example.com" style="width: 80%; float: left;">
<button id="link-btn" type="button" class="btn btn-primary" onclick="addUrl();" style="float: right;">Add</button>
</div>
</div>
</div>
</div>

+ 56
- 0
templates/fragments/sidebar.j2 View File

@ -0,0 +1,56 @@
<ul class="list-unstyled components">
{% if request.path == url_for('index') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('index') }}">
<svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#home"/>
</svg>My List
</a>
</li>
{% if request.path == url_for('archived') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('archived') }}"><svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#archive"/>
</svg>Archived</a>
</li>
</ul>
{% if request.path == url_for('bookmarklet') %}
<div style="position: fixed; bottom: 20px;" class="active">
{% else %}
<div style="position: fixed; bottom: 20px;">
{% endif %}
<a href="{{ url_for('bookmarklet') }}"><svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#book"/>
</svg>Bookmarklet</a>
</div>

+ 6
- 40
templates/list.j2 View File

@ -31,7 +31,7 @@
</li> </li>
</ul> </ul>
<form class="form-inline ml-auto"> <form class="form-inline ml-auto">
<a class="btn btn-primary" href="/save" role="button" style="margin-right: 20px;">Save</a>
<a class="btn btn-info" href="#addLink" onclick="openLinkModal();" role="button" style="margin-right: 20px;">Add Link</a>
<a class="btn btn-primary" href="/logout" role="button">Logout</a> <a class="btn btn-primary" href="/logout" role="button">Logout</a>
</form> </form>
</div> </div>
@ -42,44 +42,7 @@
<div class="wrapper"> <div class="wrapper">
<!-- Sidebar --> <!-- Sidebar -->
<div id="sidebar"> <div id="sidebar">
<ul class="list-unstyled components">
{% if request.path == url_for('index') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('index') }}">
<svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#home"/>
</svg>My List
</a>
</li>
{% if request.path == url_for('archived') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('archived') }}"><svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#archive"/>
</svg>Archived</a>
</li>
</ul>
{% include "fragments/sidebar.j2" %}
</div> </div>
<!-- Page Content --> <!-- Page Content -->
@ -90,7 +53,7 @@
{% for article in articles|reverse %} {% for article in articles|reverse %}
<div style="color: black; padding: 10px; margin-bottom: 10px" class="card" id="article"> <div style="color: black; padding: 10px; margin-bottom: 10px" class="card" id="article">
<a href="/article/{{article[0]}}">{{ article[2] }}</a> <a href="/article/{{article[0]}}">{{ article[2] }}</a>
<a href="{{ article[1] }}" target="_blank" style="color: darkgray">{{ article[4] }}</a>
<a href="{{ article[1] }}" target="_blank" style="color: darkgray">{{ article[3] }}</a>
<br> <br>
<span> <span>
<a href="/archive/{{article[0]}}" class='text-success'>Archive</a> <a href="/archive/{{article[0]}}" class='text-success'>Archive</a>
@ -104,8 +67,11 @@
</form> </form>
</div> </div>
</div> </div>
{% include "fragments/add_modal.j2" %}
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script type="text/javascript" src="{{url_for('.static', filename='add_modal.js')}}"></script>
{% endblock %} {% endblock %}

+ 0
- 148
templates/save.j2 View File

@ -1,148 +0,0 @@
{% extends "bootstrap/base.html" %}
{% block title %}Read TI Later{% endblock %}
{% block styles %}
{{super()}}
<link rel="stylesheet" href="{{url_for('.static', filename='style.css')}}">
{% endblock %}
{% block navbar %}
<div id="navbar" class="navbar navbar-expand-lg sticky-top navbar-dark bg-dark">
<div class="navbar-brand">Read TI Later</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown"
aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('index') }}">My List</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('archived') }}">Archived</a>
</li>
</ul>
<form class="form-inline ml-auto">
<a class="btn btn-primary" href="/logout" role="button">Logout</a>
</form>
</div>
</div>
{% endblock %}
{% block content %}
<div class="wrapper">
<!-- Sidebar -->
<div id="sidebar">
<ul class="list-unstyled components">
{% if request.path == url_for('index') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('index') }}">
<svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#home"/>
</svg>My List
</a>
</li>
{% if request.path == url_for('archived') %}
<li class="nav-item active">
{% else %}
<li class="nav-item">
{% endif %}
<a href="{{ url_for('archived') }}"><svg
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="height: 1rem; vertical-align: middle; margin-bottom: 4px;">
<use xlink:href="{{url_for('.static', filename='feather-sprite.svg')}}#archive"/>
</svg>Archived</a>
</li>
</ul>
</div>
<!-- Page Content -->
<div id="content">
<div id="error-alert" class="alert alert-danger" role="alert" style="display: none;">
This is a danger alert—check it out!
</div>
<form>
<div class="row justify-content-center">
<div class="col-lg-8">
<label for="formGroupExampleInput4">URL</label>
<input id="link-form" type="text" class="form-control" placeholder="https://example.com">
<br>
<button type="button" class="btn btn-primary" onclick="addUrl();">Add</button>
<br>
<br>
</div>
<div style="position:fixed; bottom: 20px;">
<p style="text-align: center;">Would you rather save on the go? Try our bookmarklet!</p>
<code>
javascript:(function(){var%20url%20=%20location.href;var%20otherWindow=window.open('about:blank','_blank');otherWindow.opener=null;otherWindow.location='{{ request.url_root }}add?close=1&url='+encodeURIComponent(url);})();
</code>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script>
function addUrl() {
url = $('#link-form').val();
if (url.trim().length === 0) {
showError('URL is required');
return false;
}
$.ajax({
url: '/add',
method: 'POST',
data: { "url": url },
success: function(data) {
if (data !== 'Error')
window.location.reload();
else
showError('URL cannot be empty');
}
});
}
function showError(error) {
hideSuccess();
$('#error-alert').text(error);
$('#error-alert').show();
}
function showArticle(message) {
hideError();
$('#article').html(message);
$('#article img').css('max-width', '100%')
$('#article').show();
}
function hideError(error) {
$('#error-alert').hide();
}
function hideArticle(error) {
$('#article').hide();
}
</script>
{% endblock %}

+ 14
- 1
utils.py View File

@ -1,4 +1,6 @@
from urllib.parse import urlparse from urllib.parse import urlparse
from readabilipy import simple_json_from_html_string
import requests
def clean_articles(rows): def clean_articles(rows):
#article_id, url, title, byline #article_id, url, title, byline
@ -9,4 +11,15 @@ def clean_articles(rows):
result = '{uri.netloc}'.format(uri=parsed_uri) result = '{uri.netloc}'.format(uri=parsed_uri)
out.append([row[0], row[1], row[2], row[3], result]) out.append([row[0], row[1], row[2], row[3], result])
return out
return out
def get_article(url):
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}
response = requests.get(url, headers=headers)
article = simple_json_from_html_string(response.text, use_readability=True)
parsed_uri = urlparse(url)
result = '{uri.netloc}'.format(uri=parsed_uri)
return article, result

Loading…
Cancel
Save