Add inline_svg

This commit is contained in:
Yusur 2021-10-17 10:20:47 +02:00
parent baed59ea39
commit 8b5e2ed41b
18 changed files with 56 additions and 21 deletions

View file

@ -2,6 +2,7 @@
## 0.9-dev ## 0.9-dev
* Website redesign: added some material icons, implemented via a `inline_svg` function, injected by default in templates and defined in `utils.py`.
* Added positive feedback mechanism: now you can +1 a message. So, `score_message_add` and `score_message_remove` API endpoints were added, and `MessageUpvote` table was created. * Added positive feedback mechanism: now you can +1 a message. So, `score_message_add` and `score_message_remove` API endpoints were added, and `MessageUpvote` table was created.
* Added notifications support for API. * Added notifications support for API.
* Added `create_account` endpoint to API. This endpoint does not require an access token. * Added `create_account` endpoint to API. This endpoint does not require an access token.

View file

@ -30,6 +30,8 @@ __version__ = '0.9-dev'
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
raise RuntimeError('Python 3 required') raise RuntimeError('Python 3 required')
os.chdir(os.path.dirname(os.path.dirname(__file__)))
app = Flask(__name__) app = Flask(__name__)
app.config.from_pyfile('../config.py') app.config.from_pyfile('../config.py')
@ -60,7 +62,11 @@ def after_request(response):
@app.context_processor @app.context_processor
def _inject_variables(): def _inject_variables():
return {'site_name': app.config['SITE_NAME'], 'locations': locations} return {
'site_name': app.config['SITE_NAME'],
'locations': locations,
'inline_svg': inline_svg
}
@login_manager.user_loader @login_manager.user_loader
def _inject_user(userid): def _inject_user(userid):

View file

@ -4,7 +4,7 @@ Filter functions used in the website templates.
from flask import Markup from flask import Markup
import html, datetime, re, time import html, datetime, re, time
from .utils import tokenize from .utils import tokenize, inline_svg as _inline_svg
from . import app from . import app
@app.template_filter() @app.template_filter()
@ -64,3 +64,4 @@ def is_following(from_user, to_user):
def locationdata(key): def locationdata(key):
if key > 0: if key > 0:
return locations[str(key)] return locations[str(key)]

View file

@ -17,7 +17,7 @@ import os
# here should go `from .utils import get_current_user`, but it will cause # here should go `from .utils import get_current_user`, but it will cause
# import errors. It's instead imported at function level. # import errors. It's instead imported at function level.
database = SqliteDatabase(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'coriplus.sqlite')) database = SqliteDatabase('coriplus.sqlite')
class BaseModel(Model): class BaseModel(Model):
class Meta: class Meta:
@ -51,8 +51,7 @@ class User(BaseModel):
return False return False
@property @property
def is_authenticated(self): def is_authenticated(self):
from .utils import get_current_user return True
return self == get_current_user()
# it often makes sense to put convenience methods on model instances, for # it often makes sense to put convenience methods on model instances, for
# example, "give me all the users this user is following": # example, "give me all the users this user is following":

View file

@ -1,9 +1,16 @@
body,button,input,select,textarea{font-family:'Segoe UI',Arial,Helvetica,sans-serif} body,button,input,select,textarea{font-family:Roboto,'Segoe UI',Arial,Helvetica,sans-serif}
body{margin:0} body{margin:0}
.header{padding:12px;color:white;background-color:#ff3018} a{text-decoration:none}
a:hover{text-decoration:underline}
@media (max-width:640px){
.mobile-collapse{display:none}
}
.header{padding:12px;color:white;background-color:#ff3018;box-shadow:0 0 3px 3px #ccc}
.content{padding:12px} .content{padding:12px}
.header a{color:white} .header a{color:white}
.header a svg{fill:white}
.content a{color:#3399ff} .content a{color:#3399ff}
.content a svg{fill:#3399ff}
.content a.plus{color:#ff3018} .content a.plus{color:#ff3018}
.metanav{float:right} .metanav{float:right}
.header h1{margin:0;display:inline-block} .header h1{margin:0;display:inline-block}
@ -14,7 +21,9 @@ body{margin:0}
} }
.weak{opacity:.5} .weak{opacity:.5}
.field_desc{display:block} .field_desc{display:block}
.message-visual img{max-width:100%;max-height:8em} ul.timeline{padding:0;margin:auto;max-width:960px}
ul.timeline > li{list-style:none;border-bottom:#808080 1px solid}
.message-visual img{max-width:100%;margin:auto}
.message-options-showhide::before{content:'\2026'} .message-options-showhide::before{content:'\2026'}
.message-options{display:none} .message-options{display:none}
.create_text{width:100%;height:8em} .create_text{width:100%;height:8em}

View file

@ -12,18 +12,18 @@
<h1><a href="{{ url_for('website.homepage') }}">{{ site_name }}</a></h1> <h1><a href="{{ url_for('website.homepage') }}">{{ site_name }}</a></h1>
<div class="metanav"> <div class="metanav">
{% if current_user.is_anonymous %} {% if current_user.is_anonymous %}
<a href="{{ url_for('website.login', next=request.full_path) }}">log in</a> <a href="{{ url_for('website.login', next=request.full_path) }}">{{ inline_svg('exit_to_app') }} log in</a>
<a href="{{ url_for('website.register', next=request.full_path) }}">register</a> <a href="{{ url_for('website.register', next=request.full_path) }}">{{ inline_svg('person_add') }} register</a>
{% else %} {% else %}
<a href="{{ url_for('website.user_detail', username=current_user.username) }}">{{ current_user.username }}</a> <a href="{{ url_for('website.user_detail', username=current_user.username) }}">{{ inline_svg('person') }} {{ current_user.username }}</a>
{% set notification_count = current_user.unseen_notification_count() %} {% set notification_count = current_user.unseen_notification_count() %}
{% if notification_count > 0 %} {% if notification_count > 0 %}
<a href="{{ url_for('website.notifications') }}">(<strong>{{ notification_count }}</strong>)</a> <a href="{{ url_for('website.notifications') }}">(<strong>{{ notification_count }}</strong>)</a>
{% endif %} {% endif %}
- -
<a href="{{ url_for('website.public_timeline') }}">explore</a> <a href="{{ url_for('website.public_timeline') }}">{{ inline_svg('explore') }} explore</a>
<a href="{{ url_for('website.create') }}">create</a> <a href="{{ url_for('website.create') }}">{{ inline_svg('edit') }} create</a>
<a href="{{ url_for('website.logout') }}">log out</a> <a href="{{ url_for('website.logout') }}">{{ inline_svg('exit_to_app') }} log out</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -36,7 +36,8 @@
<div class="footer"> <div class="footer">
<p class="copyright">&copy; 2019 Sakuragasaki46. <p class="copyright">&copy; 2019 Sakuragasaki46.
<a href="/about/">About</a> - <a href="/terms/">Terms</a> - <a href="/about/">About</a> - <a href="/terms/">Terms</a> -
<a href="/privacy/">Privacy</a></p> <a href="/privacy/">Privacy</a> -
<a href="https://github.com/sakuragasaki46/coriplus">GitHub</a></p>
</div> </div>
<script src="/static/lib.js"></script> <script src="/static/lib.js"></script>
</body> </body>

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<h2>Explore</h2> <h2>Explore</h2>
<ul> <ul class="timeline">
{% for message in message_list %} {% for message in message_list %}
<li id="{{ message.id }}">{% include "includes/message.html" %}</li> <li id="{{ message.id }}">{% include "includes/message.html" %}</li>
{% endfor %} {% endfor %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block body %} {% block body %}
<h2>Your Timeline</h2> <h2>Your Timeline</h2>
<ul> <ul class="timeline">
{% for message in message_list %} {% for message in message_list %}
<li id="{{ message.id }}">{% include "includes/message.html" %}</li> <li id="{{ message.id }}">{% include "includes/message.html" %}</li>
{% endfor %} {% endfor %}

View file

@ -30,6 +30,6 @@
<a href="{{ url_for('website.user_following', username=user.username) }}"><strong>{{ user.following()|count }}</strong></a> following <a href="{{ url_for('website.user_following', username=user.username) }}"><strong>{{ user.following()|count }}</strong></a> following
</p> </p>
{% if user == current_user %} {% if user == current_user %}
<p><a href="/edit_profile/">Edit profile</a></p> <p><a href="/edit_profile/">{{ inline_svg('edit', 18) }} Edit profile</a></p>
{% endif %} {% endif %}
</div> </div>

View file

@ -18,7 +18,7 @@
<a href="/create/">Create a message</a> <a href="/create/">Create a message</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
<ul> <ul class="timeline">
{% for message in message_list %} {% for message in message_list %}
<li id="{{ message.id }}">{% include "includes/message.html" %}</li> <li id="{{ message.id }}">{% include "includes/message.html" %}</li>
{% endfor %} {% endfor %}

View file

@ -5,7 +5,7 @@ A list of utilities used across modules.
import datetime, re, base64, hashlib, string, sys, json import datetime, re, base64, hashlib, string, sys, json
from .models import User, Message, Notification, MSGPRV_PUBLIC, MSGPRV_UNLISTED, \ from .models import User, Message, Notification, MSGPRV_PUBLIC, MSGPRV_UNLISTED, \
MSGPRV_FRIENDS, MSGPRV_ONLYME MSGPRV_FRIENDS, MSGPRV_ONLYME
from flask import abort, render_template, request, session from flask import Markup, abort, render_template, request, session
_forbidden_extensions = 'com net org txt'.split() _forbidden_extensions = 'com net org txt'.split()
_username_characters = frozenset(string.ascii_letters + string.digits + '_') _username_characters = frozenset(string.ascii_letters + string.digits + '_')
@ -82,7 +82,7 @@ class Visibility(object):
def get_locations(): def get_locations():
data = {} data = {}
with open('locations.txt') as f: with open('locations.txt', encoding='utf-8') as f:
for line in f: for line in f:
line = line.rstrip() line = line.rstrip()
if line.startswith('#'): if line.startswith('#'):
@ -215,3 +215,14 @@ def create_mentions(cur_user, text, privacy):
push_notification('mention', mention_user, user=user.id) push_notification('mention', mention_user, user=user.id)
except User.DoesNotExist: except User.DoesNotExist:
pass pass
# New in 0.9
def inline_svg(name, width=None):
try:
with open('icons/' + name + '-24px.svg') as f:
data = f.read()
if isinstance(width, int):
data = re.sub(r'( (?:height|width)=")\d+(")', lambda x:x.group(1) + str(width) + x.group(2), data)
return Markup(data)
except OSError:
return ''

1
icons/edit-24px.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 287 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 302 B

1
icons/explore-24px.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 261 B

1
icons/person-24px.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

After

Width:  |  Height:  |  Size: 247 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>

After

Width:  |  Height:  |  Size: 279 B

1
icons/shuffle-24px.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/></svg>

After

Width:  |  Height:  |  Size: 311 B