Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update npf-renderer to 0.13.0 #85

Merged
merged 12 commits into from
Feb 18, 2025
4 changes: 4 additions & 0 deletions assets/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ body {
--color-primary-900: hsl(25, 100%, 15%);
--color-primary-800: hsl(27, 50%, 30%);
--color-primary-700: hsl(30, 50%, 40%);
--color-primary-650: hsl(33, 70%, 42%);
--color-primary-600: hsl(33, 70%, 45%);
--color-primary-500: hsl(36, 80%, 50%);
--color-primary-400: hsl(36, 90%, 60%);
Expand Down Expand Up @@ -107,6 +108,9 @@ body {
--color-poll-proportion-bar-bg: var(--light, var(--color-gray-250)) var(--dark, var(--color-dt-gray-300));
--color-poll-choice-bg: var(--light, var(--color-gray-150)) var(--dark, var(--color-dt-gray-500));

--color-post-reveal-truncated-content-button: var(--light, var(--color-primary-500)) var(--dark, var(--color-primary-650));
--color-post-reveal-truncated-content-button-hover: var(--light, var(--color-primary-400)) var(--dark, var(--color-primary-600));

--color-post-footer: var(--light, var(--color-gray-500)) var(--dark, var(--color-dt-gray-250));
--color-post-footer-post-interaction: var(--light, var(--color-gray-500)) var(--dark, var(--color-dt-gray-225));
--color-post-tag-bg: var(--light, var(--color-gray-125)) var(--dark, var(--color-dt-gray-575));
Expand Down
29 changes: 28 additions & 1 deletion assets/css/post-layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,38 @@

.post-body > .text-block:last-child,
.post-body > .layout-row:last-child > .text-block:last-child,
.post-body > .layout-truncated:last-child > .text-block:last-child,
.post-body > .link-block:last-child,
.post-body > .layout-row:last-child > .link-block:last-child {
.post-body > .layout-row:last-child > .link-block:last-child
.post-body > .layout-truncated:last-child > .text-block:last-child {
margin-bottom: 0;
}

.layout-row {
margin: 0;
}

.layout-truncated > summary {
color: var(--color-post-reveal-truncated-content-button);
list-style: none;
font-weight: 650;
cursor: pointer;
display: inline-block;

position: relative;
left: 50%;
transform: translateX(-50%);
}

.layout-truncated > summary:hover {
color: var(--color-post-reveal-truncated-content-button-hover);
}

.layout-truncated[open=""] > summary {
display: none;
}


.heading1, .heading2, .quote {
font-weight: 500;
}
Expand Down
6 changes: 3 additions & 3 deletions assets/js/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ function fill_poll_results(poll_element, results) {
}

const answerIdChoiceElementArray = [];
const pollBody = poll_element.getElementsByClassName("poll-body")[0];
const pollChoices = poll_element.getElementsByClassName("poll-choices")[0];

for (let choiceElement of pollBody.children) {
for (let choiceElement of pollChoices.children) {
answerIdChoiceElementArray.push([choiceElement.dataset.answerId, choiceElement]);
}

Expand All @@ -63,7 +63,7 @@ function fill_poll_results(poll_element, results) {
voteProportionElement.classList.add("vote-proportion");
voteProportionElement["style"] = `width: ${((numericalVoteProportion) * 100).toFixed(3)}%;`;

const voteCountElement = document.createElement("span");
const voteCountElement = document.createElement("p");
voteCountElement.classList.add("vote-count");

// A greater rounding precision is needed here
Expand Down
73 changes: 72 additions & 1 deletion locales/en_US/LC_MESSAGES/priviblur.po
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,75 @@ msgid "post_note_viewer_view_replies_filter_sort_oldest"
msgstr "Oldest first"

msgid "post_note_viewer_view_replies_filter_sort_newest"
msgstr "Newest first"
msgstr "Newest first"

msgid "settings_expand_blogger_truncated_posts"
msgstr "Expand posts"

msgid "settings_expand_blogger_truncated_posts_desc"
msgstr "Expands truncated posts automatically"

msgid "npf_renderer_asker_with_no_attribution"
msgstr "Anonymous"

msgid "npf_renderer_asker_and_ask_verb"
msgstr "{name} asked"

msgid "npf_renderer_unsupported_block_header"
msgstr "Unsupported NPF block"

msgid "npf_renderer_unsupported_block_description"
msgstr "Placeholder for the unsupported \"{block}\" type NPF block Please report me over at https://github.com/syeopite/npf-renderer"

msgid "npf_renderer_generic_image_alt_text"
msgstr "image"

msgid "npf_renderer_link_block_poster_alt_text"
msgstr "Preview image for \"{site}\""

msgid "npf_renderer_link_block_fallback_embeds_are_disabled"
msgstr "Embeds are disabled"

msgid "npf_renderer_error_video_link_block_fallback_heading"
msgstr "Error: unable to render video block"

msgid "npf_renderer_video_link_block_fallback_description"
msgstr "Please click me to watch on the original site"

msgid "npf_renderer_error_link_block_fallback_native_video_player_non_tumblr_source"
msgstr "Error: non-tumblr source for video player"

msgid "npf_renderer_fallback_audio_block_thumbnail_alt_text"
msgstr "Album art"

msgid "npf_renderer_error_audio_link_block_fallback_heading"
msgstr "Error: unable to render audio block"

msgid "npf_renderer_audio_link_block_fallback_description"
msgstr "Please click me to listen on the original site"

msgid "npf_renderer_error_link_block_fallback_native_audio_player_non_tumblr_source"
msgstr "Error: non-tumblr source for audio player"

msgid "npf_renderer_poll_total_votes"
msgid_plural "npf_renderer_poll_total_votes_plural"
msgstr[0] "{votes} vote"
msgstr[1] "{votes} votes"

msgid "npf_renderer_poll_remaining_time"
msgstr "{duration} remaining"

msgid "npf_renderer_poll_ended_on"
msgstr "Ended on: {ended_date}"

msgid "npf_renderer_post_attribution"
msgstr "From {author}"

msgid "npf_renderer_blog_attribution"
msgstr "Created by {author}"

msgid "npf_renderer_app_attribution"
msgstr "View on {platform}"

msgid "npf_renderer_unsupported_attribution"
msgstr "Attributed via an unsupported (\"{attributee}\") attribution type. Please report this over at https://github.com/syeopite/npf-renderer"
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ intervaltree==3.1.0
Jinja2==3.1.5
MarkupSafe==3.0.2
multidict==6.1.0
npf_renderer==0.12.2
npf_renderer==0.13.0
orjson==3.10.15
PyYAML==6.0.2
redis==5.2.1
Expand Down
1 change: 1 addition & 0 deletions src/config/user_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ class DefaultUserPreferences(NamedTuple):

language: str = "en_US"
theme: str = "auto"
expand_posts: bool = False
46 changes: 39 additions & 7 deletions src/helpers/ext_npf_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,30 @@ async def parse(self):


class NPFFormatter(npf_renderer.format.Formatter):
def __init__(self, content, layout=None, blog_name=None, post_id=None, *, url_handler=None, forbid_external_iframes=False):
super().__init__(content, layout, url_handler=url_handler, forbid_external_iframes=forbid_external_iframes)
def __init__(
self,
content,
layout=None,
*,
blog_name=None,
post_id=None,
url_handler=None,
forbid_external_iframes=False,
request=None,
):
initialization_arguments = {
"content": content,
"layout": layout,
"url_handler": url_handler,
"forbid_external_iframes": forbid_external_iframes
}

if request:
# Asking to expand a post is the reverse of asking to truncate a post
initialization_arguments["truncate"] = not request.ctx.preferences.expand_posts
initialization_arguments["localizer"] = request.app.ctx.LANGUAGES[request.ctx.language].npf_renderer_localizer

super().__init__(**initialization_arguments)

# We store the blog and post ID as to be able to render a link to
# fetch poll results for JS disabled users
Expand All @@ -114,9 +136,9 @@ def _format_poll(self, block):
poll_html = super()._format_poll(block)
poll_html["data-poll-id"] = block.poll_id

poll_body = poll_html[1]
poll_choices = poll_html[1][0]
for index, answer_id in enumerate(block.answers.keys()):
poll_body[index]["data-answer-id"] = answer_id
poll_choices[index]["data-answer-id"] = answer_id

if (self.blog_name and self.post_id) and not block.votes:
poll_footer = poll_html[2]
Expand All @@ -132,8 +154,8 @@ def _format_poll(self, block):

return poll_html

def _format_image(self, block, row_length=1, override_padding=None):
image_html = super()._format_image(block, row_length, override_padding)
def _format_image(self, block, row_length=1, override_aspect_ratio=None):
image_html = super()._format_image(block, row_length, override_aspect_ratio)

try:
image_element = image_html.getElementsByTagName("img")
Expand Down Expand Up @@ -171,7 +193,14 @@ def _add_alt_text_element(self, block, image_container):
)


async def format_npf(contents, layouts=None, blog_name=None, post_id=None,*, poll_callback=None):
async def format_npf(
contents,
layouts=None,
blog_name=None,
post_id=None,*,
poll_callback=None,
request=None
):
"""Wrapper around npf_renderer.format_npf for extra functionalities

- Replaces internal Parser and Formatter with the modified variants above
Expand All @@ -183,6 +212,8 @@ async def format_npf(contents, layouts=None, blog_name=None, post_id=None,*, pol
Name of the blog the post comes from. This is used to render links to the parent post
post_id:
Unique ID of the post. This is used to render links to the parent post
request:
Sanic request object. Used to check user preferences
"""
try:
contents = await NPFParser(contents, poll_callback=poll_callback).parse()
Expand All @@ -197,6 +228,7 @@ async def format_npf(contents, layouts=None, blog_name=None, post_id=None,*, pol
post_id=post_id,
url_handler=url_handler,
forbid_external_iframes=True,
request=request
).format()

except npf_renderer.exceptions.RenderErrorDisclaimerError as e:
Expand Down
1 change: 1 addition & 0 deletions src/i18n/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .i18n import SUPPORTED_LANGUAGES, initialize_locales, translate
17 changes: 11 additions & 6 deletions src/helpers/i18n.py → src/i18n/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
import sanic

from .i18n_data import LOCALE_DATA
from .npf_renderer_localizer import NPFRendererGettextFallback, NPFRendererLocalizer


class Language:
"""Stores metadata about supported translations"""
def __init__(self, locale, gettext_instance,) -> None:
def __init__(self, locale, priviblur_gettext) -> None:
self.locale = locale
self.instance = gettext_instance

self.priviblur_translations = priviblur_gettext

self.npf_renderer_localizer = NPFRendererLocalizer(locale, translate)

self.name, self.translation_percentage = LOCALE_DATA[locale]


SUPPORTED_LANGUAGES = [
"en_US", "cs_CZ", "fr", "ja", "uk", "zh_Hans", "zh_Hant", "es", "nb_NO", "de", "ta"
]
Expand All @@ -27,18 +32,18 @@ def initialize_locales() -> typing.Mapping[str, Language]:
try:
# Initialize english locale first so that we may use it as a fallback

english_instance = gettext.translation("priviblur", localedir="locales", languages=("en_US",))
priviblur_english_instance = gettext.translation("priviblur", localedir="locales", languages=("en_US",))

languages = {
"en_US": Language("en_US", english_instance)
"en_US": Language("en_US", priviblur_english_instance)
}

for locale in SUPPORTED_LANGUAGES:
if locale == "en_US":
continue

instance = gettext.translation("priviblur", localedir="locales", languages=(locale,))
instance.add_fallback(english_instance)
instance.add_fallback(priviblur_english_instance)

languages[locale] = Language(locale, instance)
except FileNotFoundError as e:
Expand All @@ -58,7 +63,7 @@ def translate(language : str, id : str, number : int | float | None = None,
substitution : str | dict | None = None) -> str:
app = sanic.Sanic.get_app("Priviblur")

gettext_instance = app.ctx.LANGUAGES[language].instance
gettext_instance = app.ctx.LANGUAGES[language].priviblur_translations

if number is not None:
translated = gettext_instance.ngettext(id, f"{id}_plural", number)
Expand Down
File renamed without changes.
52 changes: 52 additions & 0 deletions src/i18n/npf_renderer_localizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import functools
import gettext

import babel.dates
import babel.numbers
import npf_renderer

class NPFRendererGettextFallback(gettext.NullTranslations):
def gettext(self, message):
return npf_renderer.DEFAULT_LOCALIZATION["strings"][message]

def ngettext(self, msgid1: str, msgid2: str, n: int) -> str:
return npf_renderer.DEFAULT_LOCALIZATION["strings"][msgid1]


class NPFRendererLocalizer:
"""Localizes strings and provide formatting functions based on the given locale

This class serves as a bridge to translate Priviblur's Gettext based translation system to npf-renderer's
dict based api
"""
def __init__(self, language, translate_func) -> None:
self.language = language
self.strings_localizer = NPFRendererStringsLocalizer(language, translate_func)

self.locale_formatting = {
"duration": {"__default__": functools.partial(babel.dates.format_timedelta, threshold=1.1, locale=language)},
"datetime": {"__default__": functools.partial(babel.dates.format_datetime, format=f"short", locale=language)},
"decimal": {"__default__": functools.partial(babel.numbers.format_decimal, locale=language)},
}

def __getitem__(self, key : str):
if key == "strings":
return self.strings_localizer
return self.locale_formatting


class NPFRendererStringsLocalizer:
def __init__(self, language, translate_func):
self.language = language

self.translate_func = translate_func

def translate(self, key, *args):
key = f"npf_renderer_{key}"
return self.translate_func(self.language, key, *args)

def __getitem__(self, key : str):
if key[:7] == "plural_":
return lambda number : self.translate(key[7:], number)
else:
return self.translate(key)
4 changes: 3 additions & 1 deletion src/preferences.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import dataclasses
import urllib.parse

from .helpers.i18n import SUPPORTED_LANGUAGES
from .i18n import SUPPORTED_LANGUAGES

VERSION = 1

Expand All @@ -11,6 +11,8 @@ class UserPreferences:
language: str
theme: str

expand_posts: bool

# Tracks major revisions of the settings cookie
# Only bump in case of breaking changes.
version: int = 1
Expand Down
Loading