diff --git a/devplacepy/routers/projects/index.py b/devplacepy/routers/projects/index.py index aac1fe21..ad434b25 100644 --- a/devplacepy/routers/projects/index.py +++ b/devplacepy/routers/projects/index.py @@ -6,6 +6,7 @@ from sqlalchemy import or_ from fastapi import Depends, APIRouter, Request from devplacepy.models import ProjectForm, ProjectEditForm, ProjectFlagForm, ForkForm from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse +from devplacepy.attachments import get_attachments_batch from devplacepy.database import ( get_table, get_users_by_uids, @@ -13,6 +14,10 @@ from devplacepy.database import ( get_site_stats, get_user_votes, get_recent_comments_by_target_uids, + get_reactions_by_targets, + get_user_bookmarks, + get_polls_by_post_uids, + get_project_devlog, paginate, text_search_clause, resolve_by_slug, @@ -171,7 +176,7 @@ async def projects_page( ) @router.get("/{project_slug}", response_class=HTMLResponse) -async def project_detail(request: Request, project_slug: str): +async def project_detail(request: Request, project_slug: str, before: str = None): user = get_current_user(request) detail = load_detail("projects", "project", project_slug, user) if not detail: @@ -216,6 +221,25 @@ async def project_detail(request: Request, project_slug: str): if parent else None ) + + devlog_posts, devlog_next_cursor = get_project_devlog( + project["uid"], before=before, viewer=user + ) + if devlog_posts: + post_uids = [item["post"]["uid"] for item in devlog_posts] + attachments_map = get_attachments_batch("post", post_uids) + reactions_map = get_reactions_by_targets("post", post_uids, user) + bookmark_set = ( + get_user_bookmarks(user["uid"], "post", post_uids) if user else set() + ) + polls_map = get_polls_by_post_uids(post_uids, user) + for item in devlog_posts: + uid = item["post"]["uid"] + item["attachments"] = attachments_map.get(uid, []) + item["reactions"] = reactions_map.get(uid, {"counts": {}, "mine": []}) + item["bookmarked"] = uid in bookmark_set + item["poll"] = polls_map.get(uid) + return respond( request, "project_detail.html", @@ -235,6 +259,8 @@ async def project_detail(request: Request, project_slug: str): "forked_from": forked_from, "fork_count": count_forks(project["uid"]), "file_count": count_files(project["uid"]), + "devlog_posts": devlog_posts, + "devlog_next_cursor": devlog_next_cursor, }, ), model=ProjectDetailOut, @@ -464,3 +490,5 @@ async def set_project_readonly( request: Request, project_slug: str, data: Annotated[ProjectFlagForm, Depends(json_or_form(ProjectFlagForm))] ): return _set_project_flag(request, project_slug, "read_only", data.value) + + diff --git a/devplacepy/schemas/listings.py b/devplacepy/schemas/listings.py index b3b7f2a7..ecbab0b7 100644 --- a/devplacepy/schemas/listings.py +++ b/devplacepy/schemas/listings.py @@ -163,6 +163,8 @@ class ProjectDetailOut(_Out): forked_from: Optional[dict] = None fork_count: int = 0 file_count: int = 0 + devlog_posts: list[FeedItemOut] = [] + devlog_next_cursor: Optional[str] = None class GistsOut(_Out): @@ -232,3 +234,4 @@ class LeaderboardOut(_Out): class SavedOut(_Out): items: list[SavedItemOut] = [] next_cursor: Optional[str] = None + diff --git a/devplacepy/templates/project_detail.html b/devplacepy/templates/project_detail.html index 2b57ad19..df14fe10 100644 --- a/devplacepy/templates/project_detail.html +++ b/devplacepy/templates/project_detail.html @@ -102,6 +102,22 @@ +
+

Devlog

+ {% if devlog_posts %} + {% for item in devlog_posts %} + {% set _author = item.author %}{% set _time = item.time_ago %}{% set _show_share = false %}{% set _show_comment_form = false %}{% include "_post_card.html" %} + {% endfor %} + {% if devlog_next_cursor %} + + {% endif %} + {% else %} +

No devlog posts yet.

+ {% endif %} +
+ {% if is_owner %} {% call modal('edit-project-modal', 'Edit Project') %}
@@ -184,3 +200,5 @@ if (actions) { } {% endblock %} + +