Skip to content
129 changes: 124 additions & 5 deletions docs/killrvideo_openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,11 @@ paths:
\ Authenticated *viewer*-level callers receive 403.\n \u2013 The owner\
\ (*creator*) or any *moderator* can still access \u2192 404 to\n remain\
\ consistent with current spec (not explicitly tested yet).\n\u2022 A READY\
\ video is public: anyone can record a view (204)."
\ video is public: anyone can record a view (204).\n\nActivity logging: When\
\ an authenticated user records a view, the backend **must** write a row to\
\ the `user_activity` table with `activity_type = 'view'`, including the caller's\
\ `userid`, the `videoid`, and an `activity_timestamp` (timeuuid). Unauthenticated\
\ views increment the counter only and do not produce an activity row."
operationId: record_view_api_v1_videos_id__video_id_path__view_post
security:
- OAuth2PasswordBearer: []
Expand Down Expand Up @@ -714,7 +718,10 @@ paths:
tags:
- Comments & Ratings
summary: Add comment to video
description: Endpoint for viewers to add a comment to a READY video.
description: "Endpoint for viewers to add a comment to a READY video.\n\nActivity\
\ logging: On success the backend **must** write a row to the `user_activity`\
\ table with `activity_type = 'comment'`, including the caller's `userid`,\
\ the `videoid`, and an `activity_timestamp` (timeuuid)."
operationId: post_comment_to_video_api_v1_videos__video_id_path__comments_post
security:
- OAuth2PasswordBearer: []
Expand Down Expand Up @@ -845,7 +852,11 @@ paths:
tags:
- Comments & Ratings
summary: Rate a video (create or update)
description: Upsert a rating (1-5) for the specified video by the current viewer.
description: "Upsert a rating (1-5) for the specified video by the current viewer.\n\
\nActivity logging: On success the backend **must** write a row to the `user_activity`\
\ table with `activity_type = 'rate'`, including the caller's `userid`, the\
\ `videoid`, and an `activity_timestamp` (timeuuid). This row is written for\
\ both new ratings and updates to an existing rating."
operationId: post_rating_video_api_v1_videos__video_id_path__ratings_post
security:
- OAuth2PasswordBearer: []
Expand Down Expand Up @@ -1271,6 +1282,66 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/v1/users/{user_id_path}/activity:
get:
tags:
- User Activity
summary: Get user activity timeline
description: Return a paginated timeline of a user's activity over the last
30 days.
operationId: get_user_activity_api_v1_users__user_id_path__activity_get
parameters:
- name: user_id_path
in: path
required: true
schema:
type: string
format: uuid
title: User Id Path
- name: activity_type
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
description: Filter by activity type (view, comment, rate)
title: Activity Type
description: Filter by activity type (view, comment, rate)
- name: page
in: query
required: false
schema:
type: integer
minimum: 1
description: Page number
default: 1
title: Page
description: Page number
- name: pageSize
in: query
required: false
schema:
type: integer
maximum: 100
minimum: 1
description: Items per page
default: 10
title: Pagesize
description: Items per page
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedResponse_UserActivityResponse_'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/:
get:
summary: Health check
Expand Down Expand Up @@ -1579,6 +1650,20 @@ components:
- data
- pagination
title: PaginatedResponse[FlagResponse]
PaginatedResponse_UserActivityResponse_:
properties:
data:
items:
$ref: '#/components/schemas/UserActivityResponse'
type: array
title: Data
pagination:
$ref: '#/components/schemas/Pagination'
type: object
required:
- data
- pagination
title: PaginatedResponse[UserActivityResponse]
PaginatedResponse_VideoSummary_:
properties:
data:
Expand Down Expand Up @@ -1743,6 +1828,40 @@ components:
- email
- userId
title: User
UserActivityResponse:
properties:
userid:
type: string
format: uuid
title: Userid
activity_type:
type: string
enum:
- view
- comment
- rate
title: Activity Type
activity_id:
type: string
format: uuid
title: Activity Id
activity_timestamp:
type: string
format: date-time
title: Activity Timestamp
videoid:
type: string
format: uuid
title: Videoid
type: object
required:
- userid
- activity_type
- activity_id
- activity_timestamp
- videoid
title: UserActivityResponse
description: API response representation for a single user activity item.
UserCreateRequest:
properties:
firstName:
Expand Down Expand Up @@ -1873,7 +1992,7 @@ components:
description:
anyOf:
- type: string
maxLength: 1000
maxLength: 2000
- type: 'null'
title: Description
tags:
Expand Down Expand Up @@ -2127,7 +2246,7 @@ components:
description:
anyOf:
- type: string
maxLength: 1000
maxLength: 2000
- type: 'null'
title: Description
tags:
Expand Down
8 changes: 7 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: ["dist"] },
{ ignores: ["dist", ".claude"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
Expand All @@ -25,5 +25,11 @@ export default tseslint.config(
],
"@typescript-eslint/no-unused-vars": "off",
},
},
{
files: ["src/components/ui/**/*.{ts,tsx}"],
rules: {
"react-refresh/only-export-components": "off",
},
}
);
1 change: 1 addition & 0 deletions src/components/comments/CommentsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const CommentsSection = ({ videoId }: CommentsSectionProps) => {
// Append new comments when page data arrives
useEffect(() => {
if (commentPage) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setComments((prev) =>
page === 1 ? (commentPage.data as Comment[]) : [...prev, ...(commentPage.data as Comment[])]
);
Expand Down
1 change: 1 addition & 0 deletions src/components/educational/ExplainerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const ExplainerModal = ({

const abortController = new AbortController();

// eslint-disable-next-line react-hooks/set-state-in-effect
setIsLoading(true);
setError(null);

Expand Down
1 change: 1 addition & 0 deletions src/components/educational/WelcomeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const WelcomeModal = () => {

// Show modal if not welcomed and tour is not already enabled
if (!hasBeenWelcomed && !guidedTourEnabled) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setOpen(true);
}
}, [guidedTourEnabled]);
Expand Down
Loading