From 25daafcf5fa2626d03107d510c744ee15d575749 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= jaakko.keranen@iki.fi
Date: Fri, 11 Oct 2024 13:31:20 +0300
Subject: [PATCH 1/1] Auto-update post title from first segment
Regardless of whether you are creating or editing segments,
if the first text segment starts with a level 1 heading, it
is extracted and used as the post title.
composer.py | 16 ++++++++++++++++
feeds.py | 10 +---------
model.py | 20 ++++++++++++++++++--
settings.py | 2 +-
utils.py | 13 +++++++++++++
5 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/composer.py b/composer.py
index 07dbf23..27d5caa 100644
--- a/composer.py
+++ b/composer.py
@@ -35,6 +35,7 @@ def edit_segment(session):
'Move to which § number? (Other actions: "X" to remove; "." to insert text; ":" to insert long text; "S" to split text; "M" to merge text; "/" to insert a link; "P" to insert poll)'
post = db.get_post(segment.post)
if not session.is_editable(post):
return 61, 'Cannot edit posts by other users'
@@ -47,6 +48,10 @@ def edit_segment(session):
if req.content_mime and not req.content_mime.startswith('text/'):
return 50, 'Bad content format (must be text)'
seg_text = req.content.decode('utf-8')
if db.is_first_segment_of_post(post, segment):
title, seg_text = extract_title_heading(session, seg_text)
if title is not None:
db.update_post(post, title=title)
db.update_segment(segment, content=seg_text)
elif segment.type == Segment.LINK:
if session.is_gemini:
@@ -75,6 +80,10 @@ def edit_segment(session):
seg_text = clean_text(req.content.decode('utf-8'))
seg_text = seg_text.rstrip()
if db.is_first_segment_of_post(post, segment):
title, seg_text = extract_title_heading(session, seg_text)
if title is not None:
db.update_post(post, title=title)
if user.flags & User.COMPOSER_SPLIT_FLAG:
parts = split_paragraphs(seg_text)
else:
@@ -220,12 +229,19 @@ def make_composer_page(session):
if not is_empty_query(req) or (session.is_titan and len(req.content)):
seg_text = clean_query(req) if session.is_gemini \
else clean_text(req.content.decode('utf-8'))
# In the first text segment, always detect a heading and make it the post title.
if db.get_segment_count(post) == 0:
title, seg_text = extract_title_heading(session, seg_text)
else:
title = None
if session.user.flags & User.COMPOSER_SPLIT_FLAG:
parts = split_paragraphs(seg_text)
else:
parts = [seg_text.rstrip()]
for part in parts:
db.create_segment(post, Segment.TEXT, content=part)
if title is not None:
db.update_post(post, title=title)
return 30, gemini_link
return 10, 'Add text segment:'
diff --git a/feeds.py b/feeds.py
index 9055aa7..76e4d3c 100644
--- a/feeds.py
+++ b/feeds.py
@@ -172,15 +172,7 @@ def make_post_page_or_configure_feed(session):
title = ''
if not url:
found = re.match(r'^\s*#\s*(.+)$', lines[0])
if found:
title = found[1]
body = '\n'.join(lines[1:]).strip()
elif session.is_context_tracker:
title = lines[0]
body = '\n'.join(lines[1:]).strip()
else:
title = ''
title, body = extract_title_heading(session, body)
post_id = db.create_post(session.user, session.context.id, title=title)
post = db.get_post(post_id, draft=True)
diff --git a/model.py b/model.py
index cf3802a..e710cdb 100644
--- a/model.py
+++ b/model.py
@@ -505,7 +505,7 @@ class Post:
SORT_CREATED, SORT_ACTIVE, SORT_HOTNESS = range(3)
num_cmts, num_likes, tags, ts_created, ts_edited, summary,
sub_name=None, sub_owner=None, poster_avatar=None, poster_name=None,
poster_flair=None, num_notifs=0, num_per_day=None, ts_comment=None):
@@ -1777,6 +1777,22 @@ class Database:
return segments
cur = self.conn.cursor()
cur.execute("SELECT COUNT(id) FROM segments WHERE post=? AND type!=?",
(post.id, Segment.POLL))
for (count,) in cur:
return count
return 0
cur = self.conn.cursor()
cur.execute("SELECT MIN(pos) FROM segments WHERE post=? AND type!=?",
(post.id, Segment.POLL))
for (min_pos,) in cur:
return min_pos == segment.pos
return False
def get_segments_of_similar_type(self, post, is_poll):
return list(filter(lambda s:
(is_poll and s.type == Segment.POLL or
@@ -1885,7 +1901,7 @@ class Database:
self.get_segments_of_similar_type(post, is_poll)))
segments = segments[:new_pos] + [moved_segment] + segments[new_pos:]
cur = self.conn.cursor()
pos = 0
pos = 1
for seg in segments:
cur.execute('UPDATE segments SET pos=? WHERE id=?', (pos, seg.id))
pos += 1
diff --git a/settings.py b/settings.py
index b7143d6..d8efa51 100644
--- a/settings.py
+++ b/settings.py
@@ -898,7 +898,7 @@ def make_settings_page(session):
page += f'=> /settings/omit-feed {CHECKS[nonzero(user_space.flags & Subspace.OMIT_FROM_FEED_BY_DEFAULT)]} ' + \
'Omit posts from Gemini/Atom feed by default\n'
page += 'Individual posts can be included or excluded from All Posts and Gemini/Atom feeds using the composer.\n'
page += f'=> /settings/autosplit {CHECKS[nonzero(user.flags & User.COMPOSER_SPLIT_FLAG)]} Auto-split paragraphs in composer\n'
page += f'\n=> /settings/autosplit {CHECKS[nonzero(user.flags & User.COMPOSER_SPLIT_FLAG)]} Auto-split paragraphs in composer\n'
page += f'=> /settings/titan-edit {CHECKS[nonzero(user.flags & User.COMPOSER_TITAN_EDIT_FLAG)]} Edit text with Titan\nYour client must support the Titan "edit" parameter.\n'
page += '\n=> /settings/profile ⚙️ Profile\n'
diff --git a/utils.py b/utils.py
index c7b9722..dbb8588 100644
--- a/utils.py
+++ b/utils.py
@@ -241,6 +241,19 @@ def shorten_text(text, n):
return text.strip()
+def extract_title_heading(session, body):
title = found[1]
body = '\n'.join(lines[1:]).strip()
title = lines[0]
body = '\n'.join(lines[1:]).strip()
def time_delta_text(sec, date_ts, suffix='ago', now='Now',
date_prefix='',
date_fmt='%Y-%m-%d',
--
2.25.1
text/plain
This content has been proxied by September (ba2dc).