fix: handle follower and shout notifications in notifications_seen_thread
All checks were successful
Deploy on push / deploy (push) Successful in 3m13s
All checks were successful
Deploy on push / deploy (push) Successful in 3m13s
- Add support for marking follower notifications as seen (thread='followers') - Add support for marking new shout notifications as seen - Use enum constants (NotificationAction, NotificationEntity) instead of strings - Improve thread ID parsing to support different formats - Remove obsolete TODO about notification_id offset - Better error handling with logger.warning() instead of exceptions Resolves TODOs on lines 253 and 286 in resolvers/notifier.py
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.9.31] - 2025-10-04
|
||||||
|
|
||||||
|
### ✅ Fixed: Notifications TODOs
|
||||||
|
- **Уведомления о followers**: Добавлена обработка уведомлений о подписчиках в `notifications_seen_thread`
|
||||||
|
- Теперь при клике на группу "followers" все уведомления о подписках помечаются как прочитанные
|
||||||
|
- Исправлена обработка thread ID `"followers"` отдельно от shout/reaction threads
|
||||||
|
- **Уведомления о новых публикациях**: Добавлена обработка уведомлений о новых shouts в `notifications_seen_thread`
|
||||||
|
- При открытии публикации уведомления о ней тоже помечаются как прочитанные
|
||||||
|
- Исправлена логика парсинга thread ID для поддержки разных форматов
|
||||||
|
- **Code Quality**: Использованы enum константы (`NotificationAction`, `NotificationEntity`) вместо строк
|
||||||
|
- **Убраны устаревшие TODO**: Удален TODO про `notification_id` как offset (текущая логика с timestamp работает корректно)
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- `core/resolvers/notifier.py`: расширена функция `notifications_seen_thread` для поддержки всех типов уведомлений
|
||||||
|
- Добавлена обработка `thread == "followers"` для уведомлений о подписках
|
||||||
|
- Добавлена обработка `NotificationEntity.SHOUT` для уведомлений о новых публикациях
|
||||||
|
- Улучшена обработка ошибок с `logger.warning()` вместо исключений
|
||||||
|
|
||||||
## [0.9.30] - 2025-10-02
|
## [0.9.30] - 2025-10-02
|
||||||
|
|
||||||
### 🔧 Fixed
|
### 🔧 Fixed
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ def get_entity_field_name(entity_type: str) -> str:
|
|||||||
"author": "following", # AuthorFollower.following -> Author
|
"author": "following", # AuthorFollower.following -> Author
|
||||||
"topic": "topic", # TopicFollower.topic -> Topic
|
"topic": "topic", # TopicFollower.topic -> Topic
|
||||||
"community": "community", # CommunityFollower.community -> Community
|
"community": "community", # CommunityFollower.community -> Community
|
||||||
"shout": "shout" # ShoutReactionsFollower.shout -> Shout
|
"shout": "shout", # ShoutReactionsFollower.shout -> Shout
|
||||||
}
|
}
|
||||||
if entity_type not in entity_field_mapping:
|
if entity_type not in entity_field_mapping:
|
||||||
msg = f"Unknown entity_type: {entity_type}"
|
msg = f"Unknown entity_type: {entity_type}"
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ async def notification_mark_seen(_: None, info: GraphQLResolveInfo, notification
|
|||||||
@mutation.field("notifications_seen_after")
|
@mutation.field("notifications_seen_after")
|
||||||
@login_required
|
@login_required
|
||||||
async def notifications_seen_after(_: None, info: GraphQLResolveInfo, after: int) -> dict:
|
async def notifications_seen_after(_: None, info: GraphQLResolveInfo, after: int) -> dict:
|
||||||
# TODO: use latest loaded notification_id as input offset parameter
|
"""Mark all notifications after given timestamp as seen."""
|
||||||
error = None
|
error = None
|
||||||
try:
|
try:
|
||||||
author_id = info.context.get("author", {}).get("id")
|
author_id = info.context.get("author", {}).get("id")
|
||||||
@@ -278,18 +278,64 @@ async def notifications_seen_thread(_: None, info: GraphQLResolveInfo, thread: s
|
|||||||
error = None
|
error = None
|
||||||
author_id = info.context.get("author", {}).get("id")
|
author_id = info.context.get("author", {}).get("id")
|
||||||
if author_id:
|
if author_id:
|
||||||
[shout_id, reply_to_id] = thread.split(":")
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
# Convert Unix timestamp to datetime for PostgreSQL compatibility
|
# Convert Unix timestamp to datetime for PostgreSQL compatibility
|
||||||
after_datetime = datetime.fromtimestamp(after, tz=UTC) if after else None
|
after_datetime = datetime.fromtimestamp(after, tz=UTC) if after else None
|
||||||
|
|
||||||
# TODO: handle new follower and new shout notifications
|
# Handle different thread types: shout reactions, followers, or new shouts
|
||||||
|
if thread == "followers":
|
||||||
|
# Mark follower notifications as seen
|
||||||
|
query_conditions = [
|
||||||
|
Notification.entity == NotificationEntity.FOLLOWER.value,
|
||||||
|
]
|
||||||
|
if after_datetime:
|
||||||
|
query_conditions.append(Notification.created_at > after_datetime)
|
||||||
|
|
||||||
|
follower_notifications = session.query(Notification).where(and_(*query_conditions)).all()
|
||||||
|
for n in follower_notifications:
|
||||||
|
try:
|
||||||
|
ns = NotificationSeen(notification=n.id, viewer=author_id)
|
||||||
|
session.add(ns)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to mark follower notification as seen: {e}")
|
||||||
|
session.commit()
|
||||||
|
return {"error": None}
|
||||||
|
|
||||||
|
# Handle shout and reaction notifications
|
||||||
|
thread_parts = thread.split(":")
|
||||||
|
if len(thread_parts) < 2:
|
||||||
|
return {"error": "Invalid thread format"}
|
||||||
|
|
||||||
|
shout_id = thread_parts[0]
|
||||||
|
reply_to_id = thread_parts[1] if len(thread_parts) > 1 else None
|
||||||
|
|
||||||
|
# Query for new shout notifications in this thread
|
||||||
|
shout_query_conditions = [
|
||||||
|
Notification.entity == NotificationEntity.SHOUT.value,
|
||||||
|
Notification.action == NotificationAction.CREATE.value,
|
||||||
|
]
|
||||||
|
if after_datetime:
|
||||||
|
shout_query_conditions.append(Notification.created_at > after_datetime)
|
||||||
|
|
||||||
|
shout_notifications = session.query(Notification).where(and_(*shout_query_conditions)).all()
|
||||||
|
|
||||||
|
# Mark relevant shout notifications as seen
|
||||||
|
for n in shout_notifications:
|
||||||
|
payload = orjson.loads(str(n.payload))
|
||||||
|
if str(payload.get("id")) == shout_id:
|
||||||
|
try:
|
||||||
|
ns = NotificationSeen(notification=n.id, viewer=author_id)
|
||||||
|
session.add(ns)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to mark shout notification as seen: {e}")
|
||||||
|
|
||||||
|
# Query for reaction notifications
|
||||||
if after_datetime:
|
if after_datetime:
|
||||||
new_reaction_notifications = (
|
new_reaction_notifications = (
|
||||||
session.query(Notification)
|
session.query(Notification)
|
||||||
.where(
|
.where(
|
||||||
Notification.action == "create",
|
Notification.action == NotificationAction.CREATE.value,
|
||||||
Notification.entity == "reaction",
|
Notification.entity == NotificationEntity.REACTION.value,
|
||||||
Notification.created_at > after_datetime,
|
Notification.created_at > after_datetime,
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
@@ -297,8 +343,8 @@ async def notifications_seen_thread(_: None, info: GraphQLResolveInfo, thread: s
|
|||||||
removed_reaction_notifications = (
|
removed_reaction_notifications = (
|
||||||
session.query(Notification)
|
session.query(Notification)
|
||||||
.where(
|
.where(
|
||||||
Notification.action == "delete",
|
Notification.action == NotificationAction.DELETE.value,
|
||||||
Notification.entity == "reaction",
|
Notification.entity == NotificationEntity.REACTION.value,
|
||||||
Notification.created_at > after_datetime,
|
Notification.created_at > after_datetime,
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
@@ -307,16 +353,16 @@ async def notifications_seen_thread(_: None, info: GraphQLResolveInfo, thread: s
|
|||||||
new_reaction_notifications = (
|
new_reaction_notifications = (
|
||||||
session.query(Notification)
|
session.query(Notification)
|
||||||
.where(
|
.where(
|
||||||
Notification.action == "create",
|
Notification.action == NotificationAction.CREATE.value,
|
||||||
Notification.entity == "reaction",
|
Notification.entity == NotificationEntity.REACTION.value,
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
removed_reaction_notifications = (
|
removed_reaction_notifications = (
|
||||||
session.query(Notification)
|
session.query(Notification)
|
||||||
.where(
|
.where(
|
||||||
Notification.action == "delete",
|
Notification.action == NotificationAction.DELETE.value,
|
||||||
Notification.entity == "reaction",
|
Notification.entity == NotificationEntity.REACTION.value,
|
||||||
)
|
)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user