diff --git a/src/lib/api/piefed/rewrite.ts b/src/lib/api/piefed/rewrite.ts index d662b7e7..3f143763 100644 --- a/src/lib/api/piefed/rewrite.ts +++ b/src/lib/api/piefed/rewrite.ts @@ -1,4 +1,3 @@ -import { Post } from '$lib/feature/post' import { Search } from 'mono-svelte' import type { CommentReplyView, @@ -19,6 +18,7 @@ import type { Person, PersonMentionView, PersonView, + Post, PostView, PrivateMessageView, SortType, diff --git a/src/lib/api/piefed/schema.d.ts b/src/lib/api/piefed/schema.d.ts index 26048a36..2c60c1cb 100644 --- a/src/lib/api/piefed/schema.d.ts +++ b/src/lib/api/piefed/schema.d.ts @@ -252,7 +252,7 @@ export interface paths { parameters: { query: { q: string - type_: 'Communities' | 'Posts' | 'Users' | 'Url' + type_: 'Communities' | 'Posts' | 'Users' | 'Url' | 'Comments' limit?: number listing_type?: | 'All' @@ -266,6 +266,7 @@ export interface paths { | 'Active' | 'Hot' | 'New' + | 'Top' | 'TopHour' | 'TopSixHour' | 'TopTwelveHour' @@ -410,6 +411,54 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/suggest_completion': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Suggest people and communities while users type. */ + get: { + parameters: { + query?: { + q?: string + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['GetSuggestCompletionResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/community': { parameters: { query?: never @@ -550,8 +599,8 @@ export interface paths { limit?: number page?: number show_nsfw?: boolean - sort?: 'Hot' | 'Top' | 'New' - type_?: 'All' | 'Local' | 'Subscribed' + sort?: 'Hot' | 'Top' | 'New' | 'Active' | 'TopAll' + type_?: 'All' | 'Local' | 'Subscribed' | 'ModeratorView' } header?: never path?: never @@ -638,6 +687,56 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/community/rate': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Rate a community. */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['RateCommunityRequest'] + } + } + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['CommunityResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/community/block': { parameters: { query?: never @@ -1272,6 +1371,56 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/domain/block': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Block or unblock a domain */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['DomainBlockRequest'] + } + } + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DomainBlockResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/user': { parameters: { query?: never @@ -1291,6 +1440,7 @@ export interface paths { | 'Active' | 'Hot' | 'New' + | 'Top' | 'TopHour' | 'TopSixHour' | 'TopTwelveHour' @@ -1346,6 +1496,51 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/user/me': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Get the details for the current user */ + get: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['UserMeResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/user/login': { parameters: { query?: never @@ -1463,7 +1658,7 @@ export interface paths { query?: { limit?: number page?: number - sort?: 'Hot' | 'Top' | 'New' | 'Old' + sort?: 'Hot' | 'Top' | 'TopAll' | 'New' | 'Old' | 'Controversial' unread_only?: boolean } header?: never @@ -1514,7 +1709,7 @@ export interface paths { query?: { limit?: number page?: number - sort?: 'Hot' | 'Top' | 'New' | 'Old' + sort?: 'Hot' | 'Top' | 'TopAll' | 'New' | 'Old' | 'Controversial' unread_only?: boolean } header?: never @@ -1552,6 +1747,57 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/user/media': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Get media the current user has uploaded */ + get: { + parameters: { + query?: { + limit?: number + page?: number + sort?: 'Hot' | 'Top' | 'TopAll' | 'New' | 'Old' | 'Controversial' + unread_only?: boolean + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['UserMediaResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/user/block': { parameters: { query?: never @@ -1758,7 +2004,7 @@ export interface paths { get: { parameters: { query: { - status: 'All' | 'Unread' | 'Read' + status: 'All' | 'Unread' | 'Read' | 'New' limit?: number page?: number } @@ -2035,6 +2281,56 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/user/note': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Set a note for a user */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['UserSetNoteRequest'] + } + } + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['UserSetNoteResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/comment/list': { parameters: { query?: never @@ -2048,7 +2344,7 @@ export interface paths { query?: { limit?: number page?: number - sort?: 'Hot' | 'Top' | 'New' | 'Old' + sort?: 'Hot' | 'Top' | 'TopAll' | 'New' | 'Old' | 'Controversial' liked_only?: boolean saved_only?: boolean person_id?: number @@ -2696,6 +2992,7 @@ export interface paths { | 'TopYear' | 'TopAll' | 'New' + | 'Old' | 'Scaled' | 'Active' type_?: @@ -2714,6 +3011,92 @@ export interface paths { liked_only?: boolean feed_id?: number topic_id?: number + /** @description If filtering by community, ignores a post's sticky state */ + ignore_sticky?: boolean + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ListPostsResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/api/alpha/post/list2': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** List posts. For testing only, do not use. */ + get: { + parameters: { + query?: { + q?: string + sort?: + | 'Hot' + | 'Top' + | 'TopHour' + | 'TopSixHour' + | 'TopTwelveHour' + | 'TopWeek' + | 'TopDay' + | 'TopMonth' + | 'TopThreeMonths' + | 'TopSixMonths' + | 'TopNineMonths' + | 'TopYear' + | 'TopAll' + | 'New' + | 'Old' + | 'Scaled' + | 'Active' + type_?: + | 'All' + | 'Local' + | 'Subscribed' + | 'Popular' + | 'Moderating' + | 'ModeratorView' + community_name?: string + community_id?: number + saved_only?: boolean + person_id?: number + limit?: number + page?: string + liked_only?: boolean + feed_id?: number + topic_id?: number + /** @description If filtering by community, ignores a post's sticky state */ + ignore_sticky?: boolean } header?: never path?: never @@ -2888,7 +3271,7 @@ export interface paths { query?: { post_id?: number parent_id?: number - sort?: 'Hot' | 'Top' | 'New' | 'Old' + sort?: 'Hot' | 'Top' | 'TopAll' | 'New' | 'Old' | 'Controversial' max_depth?: number page?: string limit?: number @@ -3307,7 +3690,105 @@ export interface paths { [name: string]: unknown } content: { - 'application/json': components['schemas']['GetPostResponse'] + 'application/json': components['schemas']['GetPostResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/api/alpha/post/mark_as_read': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Mark one or more posts as read or unread. */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['MarkPostAsReadRequest'] + } + } + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SuccessResponse'] + } + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/api/alpha/post/like/list': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** View post votes as a moderator. */ + get: { + parameters: { + query: { + post_id: number + page?: number + limit?: number + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ListPostLikesResponse'] } } /** @description Bad Request */ @@ -3322,13 +3803,15 @@ export interface paths { 422: components['responses']['UNPROCESSABLE_CONTENT'] } } + put?: never + post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/api/alpha/post/mark_as_read': { + '/api/alpha/post/assign_flair': { parameters: { query?: never header?: never @@ -3337,7 +3820,7 @@ export interface paths { } get?: never put?: never - /** Mark one or more posts as read or unread. */ + /** Add/remove flair from a post */ post: { parameters: { query?: never @@ -3347,7 +3830,7 @@ export interface paths { } requestBody: { content: { - 'application/json': components['schemas']['MarkPostAsReadRequest'] + 'application/json': components['schemas']['PostSetFlairRequest'] } } responses: { @@ -3357,7 +3840,7 @@ export interface paths { [name: string]: unknown } content: { - 'application/json': components['schemas']['SuccessResponse'] + 'application/json': components['schemas']['PostSetFlairResponse'] } } /** @description Bad Request */ @@ -3378,26 +3861,28 @@ export interface paths { patch?: never trace?: never } - '/api/alpha/post/like/list': { + '/api/alpha/post/poll_vote': { parameters: { query?: never header?: never path?: never cookie?: never } - /** View post votes as a moderator. */ - get: { + get?: never + put?: never + /** Vote in a poll */ + post: { parameters: { - query: { - post_id: number - page?: number - limit?: number - } + query?: never header?: never path?: never cookie?: never } - requestBody?: never + requestBody: { + content: { + 'application/json': components['schemas']['PollVoteRequest'] + } + } responses: { /** @description OK */ 200: { @@ -3405,7 +3890,7 @@ export interface paths { [name: string]: unknown } content: { - 'application/json': components['schemas']['ListPostLikesResponse'] + 'application/json': components['schemas']['PollVoteResponse'] } } /** @description Bad Request */ @@ -3420,15 +3905,13 @@ export interface paths { 422: components['responses']['UNPROCESSABLE_CONTENT'] } } - put?: never - post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/api/alpha/post/assign_flair': { + '/api/alpha/upload/image': { parameters: { query?: never header?: never @@ -3437,7 +3920,7 @@ export interface paths { } get?: never put?: never - /** Add/remove flair from a post */ + /** Upload a general image. */ post: { parameters: { query?: never @@ -3447,7 +3930,7 @@ export interface paths { } requestBody: { content: { - 'application/json': components['schemas']['PostSetFlairRequest'] + 'multipart/form-data': components['schemas']['ImageUploadRequest'] } } responses: { @@ -3457,7 +3940,7 @@ export interface paths { [name: string]: unknown } content: { - 'application/json': components['schemas']['PostSetFlairResponse'] + 'application/json': components['schemas']['ImageUploadResponse'] } } /** @description Bad Request */ @@ -3470,6 +3953,15 @@ export interface paths { } } 422: components['responses']['UNPROCESSABLE_CONTENT'] + /** @description Too Many Requests */ + 429: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } } } delete?: never @@ -3478,7 +3970,7 @@ export interface paths { patch?: never trace?: never } - '/api/alpha/upload/image': { + '/api/alpha/upload/community_image': { parameters: { query?: never header?: never @@ -3487,7 +3979,7 @@ export interface paths { } get?: never put?: never - /** Upload a general image. */ + /** Upload a community image. */ post: { parameters: { query?: never @@ -3537,7 +4029,7 @@ export interface paths { patch?: never trace?: never } - '/api/alpha/upload/community_image': { + '/api/alpha/upload/user_image': { parameters: { query?: never header?: never @@ -3546,7 +4038,7 @@ export interface paths { } get?: never put?: never - /** Upload a community image. */ + /** Upload a user image. */ post: { parameters: { query?: never @@ -3596,7 +4088,7 @@ export interface paths { patch?: never trace?: never } - '/api/alpha/upload/user_image': { + '/api/alpha/image/delete': { parameters: { query?: never header?: never @@ -3605,7 +4097,7 @@ export interface paths { } get?: never put?: never - /** Upload a user image. */ + /** Delete a user image. */ post: { parameters: { query?: never @@ -3615,7 +4107,7 @@ export interface paths { } requestBody: { content: { - 'multipart/form-data': components['schemas']['ImageUploadRequest'] + 'application/json': components['schemas']['ImageDeleteRequest'] } } responses: { @@ -3625,7 +4117,7 @@ export interface paths { [name: string]: unknown } content: { - 'application/json': components['schemas']['ImageUploadResponse'] + 'application/json': components['schemas']['ImageDeleteResponse'] } } /** @description Bad Request */ @@ -3715,8 +4207,11 @@ export interface paths { /** Get conversation with a specific person. */ get: { parameters: { - query: { - person_id: number + query?: { + /** @description One of either person_id or conversation_id must be specified */ + person_id?: number + /** @description One of either person_id or conversation_id must be specified */ + conversation_id?: number page?: number limit?: number } @@ -3755,6 +4250,54 @@ export interface paths { patch?: never trace?: never } + '/api/alpha/private_message/conversation/leave': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Leave a conversation */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['LeaveConversationRequest'] + } + } + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Bad Request */ + 400: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['DefaultError'] + } + } + 422: components['responses']['UNPROCESSABLE_CONTENT'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/api/alpha/private_message': { parameters: { query?: never @@ -4031,11 +4574,15 @@ export interface components { person_id: number post_count: number } + UserExtraField: { + id: number + /** @example Pronouns */ + label: string + /** @example he/him, she/her, they/them, etc. */ + text: string + } Person: { - /** - * Format: url - * @example https://piefed.social/u/rimu - */ + /** @example https://piefed.social/u/rimu */ actor_id: string banned: boolean bot: boolean @@ -4049,16 +4596,18 @@ export interface components { /** Format: html */ about_html?: string /** Format: url */ - avatar?: string | null + avatar?: string /** Format: url */ - banner?: string | null + banner?: string + extra_fields?: components['schemas']['UserExtraField'][] + note?: string flair?: string /** * Format: datetime * @example 2025-06-07T02:29:07.980084Z */ published?: string - title?: string | null + title?: string } PersonView: { activity_alert: boolean @@ -4084,7 +4633,6 @@ export interface components { all_languages?: components['schemas']['LanguageView'][] description?: string enable_downvotes?: boolean - /** Format: url */ icon?: string /** @enum {string} */ registration_mode?: 'Closed' | 'RequireApplication' | 'Open' @@ -4118,13 +4666,11 @@ export interface components { restricted_to_mods: boolean title: string banned?: boolean - /** Format: url */ - banner?: string | null + banner?: string /** Format: markdown */ description?: string - /** Format: url */ - icon?: string | null - posting_warning?: string | null + icon?: string + posting_warning?: string /** * Format: datetime * @example 2025-06-07T02:29:07.980084Z @@ -4132,8 +4678,8 @@ export interface components { updated?: string } CommunityBlockView: { - community?: components['schemas']['Community'] - person?: components['schemas']['Person'] + community: components['schemas']['Community'] + person: components['schemas']['Person'] } CommunityFollowerView: { community: components['schemas']['Community'] @@ -4162,7 +4708,13 @@ export interface components { } LocalUser: { /** @enum {string} */ - default_comment_sort_type: 'Hot' | 'Top' | 'New' | 'Old' + default_comment_sort_type: + | 'Hot' + | 'Top' + | 'TopAll' + | 'New' + | 'Old' + | 'Controversial' /** @enum {string} */ default_listing_type: | 'All' @@ -4176,6 +4728,7 @@ export interface components { | 'Active' | 'Hot' | 'New' + | 'Top' | 'TopHour' | 'TopSixHour' | 'TopTwelveHour' @@ -4287,6 +4840,7 @@ export interface components { active_weekly?: number active_monthly?: number active_6monthly?: number + average_rating?: number } CommunityFlair: { id: number @@ -4307,7 +4861,7 @@ export interface components { * Format: url * @description Legacy tags that existed prior to 1.2 and some tags for remote communities might not have a defined ap_id */ - ap_id: string | null + ap_id: string } CommunityView: { activity_alert: boolean @@ -4345,8 +4899,74 @@ export interface components { reply_count?: number community_name?: string } + PostEvent: { + /** + * Format: datetime + * @example 2025-06-07T02:29:07.980084Z + */ + start: string + /** + * Format: datetime + * @example 2025-06-07T02:29:07.980084Z + */ + end?: string + /** @example America/New_York */ + timezone?: string + /** @default 0 */ + max_attendees: number + /** @default 0 */ + participant_count: number + /** @default false */ + full: boolean + online_link?: string + /** + * @description free, restricted, external, invite + * @example free + */ + join_mode?: string + external_participation_url?: string + /** @default false */ + anonymous_participation: boolean + /** @default false */ + online: boolean + buy_tickets_link?: string + /** @example USD */ + event_fee_currency?: string + /** @default 0 */ + event_fee_amount: number + /** @description JSON object containing location details */ + location?: { + [key: string]: unknown + } + } + PollChoice: { + id: number + choice_text: string + sort_order: number + /** @default 0 */ + num_votes: number + } + PostPoll: { + /** + * Format: datetime + * @example 2025-06-07T02:29:07.980084Z + */ + end_poll?: string + /** + * @description single or multiple - determines whether people can vote for one or multiple options + * @example single + */ + mode: string + /** @default false */ + local_only: boolean + /** + * Format: datetime + * @example 2025-06-07T02:29:07.980084Z + */ + latest_vote?: string + choices: components['schemas']['PollChoice'][] + } Post: { - /** Format: url */ ap_id: string community_id: number deleted: boolean @@ -4367,19 +4987,20 @@ export interface components { alt_text?: string /** Format: markdown */ body?: string - /** Format: url */ small_thumbnail_url?: string - /** Format: url */ thumbnail_url?: string /** * Format: datetime * @example 2025-06-07T02:29:07.980084Z */ updated?: string - /** Format: url */ url?: string image_details?: components['schemas']['WidthHeight'] cross_posts?: components['schemas']['MiniCrossPosts'][] + /** @enum {string} */ + post_type: 'Link' | 'Discussion' | 'Image' | 'Video' | 'Poll' | 'Event' + event?: components['schemas']['PostEvent'] + poll?: components['schemas']['PostPoll'] } PostView: { banned_from_community: boolean @@ -4399,6 +5020,7 @@ export interface components { activity_alert?: boolean my_vote?: number flair_list?: components['schemas']['CommunityFlair'][] + can_auth_user_moderate?: boolean } Comment: { /** Format: url */ @@ -4425,6 +5047,8 @@ export interface components { */ updated?: string locked?: boolean + } & { + [key: string]: unknown } CommentAggregates: { child_count: number @@ -4451,13 +5075,17 @@ export interface components { creator_is_moderator: boolean post: components['schemas']['Post'] saved: boolean - subscribed: string + /** + * @description Indicates whether auth'ed user is subscribed to the community this comment is in or not. + * @enum {string} + */ + subscribed: 'Subscribed' | 'NotSubscribed' | 'Pending' my_vote?: number can_auth_user_moderate?: boolean } SearchResponse: { /** @enum {string} */ - type_: 'Communities' | 'Posts' | 'Users' | 'Url' + type_: 'Communities' | 'Posts' | 'Users' | 'Url' | 'Comments' communities: components['schemas']['CommunityView'][] posts: components['schemas']['PostView'][] users: components['schemas']['PersonView'][] @@ -4493,6 +5121,9 @@ export interface components { GetFederatedInstancesResponse: { federated_instances?: components['schemas']['FederatedInstancesView'] } + GetSuggestCompletionResponse: { + result: string[] + } GetCommunityResponse: { community_view: components['schemas']['CommunityView'] discussion_languages: number[] @@ -4501,7 +5132,7 @@ export interface components { } ListCommunitiesResponse: { communities: components['schemas']['CommunityView'][] - next_page?: string | null + next_page?: string } FollowCommunityRequest: { community_id: number @@ -4511,6 +5142,10 @@ export interface components { community_view: components['schemas']['CommunityView'] discussion_languages: number[] } + RateCommunityRequest: { + community_id: number + rating: number + } BlockCommunityRequest: { block: boolean community_id: number @@ -4522,13 +5157,11 @@ export interface components { CreateCommunityRequest: { name: string title: string - /** Format: url */ - banner_url?: string | null + banner_url?: string /** Format: markdown */ description?: string discussion_languages?: number[] - /** Format: url */ - icon_url?: string | null + icon_url?: string local_only?: boolean nsfw?: boolean restricted_to_mods?: boolean @@ -4537,13 +5170,11 @@ export interface components { EditCommunityRequest: { community_id: number title?: string - /** Format: url */ - banner_url?: string | null + banner_url?: string /** Format: markdown */ description?: string discussion_languages?: number[] - /** Format: url */ - icon_url?: string | null + icon_url?: string local_only?: boolean nsfw?: boolean restricted_to_mods?: boolean @@ -4568,23 +5199,23 @@ export interface components { CommunityModerationBanItem: { banned_by?: components['schemas']['Person'] banned_user?: components['schemas']['Person'] - community?: components['schemas']['Community'] + community: components['schemas']['Community'] expired?: boolean /** * Format: datetime * @example 2025-06-07T02:29:07.980084Z, null=permanent ban */ - expired_at?: string | null + expired_at?: string /** * Format: datetime * @example 2025-06-07T02:29:07.980084Z, null=permanent ban */ - expires_at?: string | null + expires_at?: string reason?: string } CommunityModerationBansListResponse: { items?: components['schemas']['CommunityModerationBanItem'][] - next_page?: string | null + next_page?: string } CommunityModerationUnbanRequest: { community_id: number @@ -4642,7 +5273,7 @@ export interface components { * Format: url * @description Legacy tags that existed prior to 1.2 and some tags for remote communities might not have a defined ap_id */ - ap_id: string | null + ap_id: string } CommunityFlairEditRequest: { flair_id: number @@ -4678,7 +5309,7 @@ export interface components { * Format: url * @description Legacy tags that existed prior to 1.2 and some tags for remote communities might not have a defined ap_id */ - ap_id: string | null + ap_id: string } CommunityFlairDeleteRequest: { flair_id: number @@ -4721,15 +5352,13 @@ export interface components { updated: string /** @description user_id of the feed creator/owner */ user_id: number - /** Format: url */ - banner?: string | null + banner?: string /** Format: markdown */ - description?: string | null + description?: string /** Format: html */ - description_html?: string | null - /** Format: url */ - icon?: string | null - parent_feed_id?: number | null + description_html?: string + icon?: string + parent_feed_id?: number } FeedListResponse: { feeds: components['schemas']['FeedView'][] @@ -4742,11 +5371,18 @@ export interface components { name: string show_posts_from_children: boolean title: string - parent_topic_id?: number | null + parent_topic_id?: number } TopicListResponse: { topics: components['schemas']['TopicView'][] } + DomainBlockRequest: { + block: boolean + domain: string + } + DomainBlockResponse: { + blocked: boolean + } GetUserResponse: { comments: components['schemas']['CommentView'][] moderates: components['schemas']['CommunityModeratorView'][] @@ -4754,6 +5390,15 @@ export interface components { posts: components['schemas']['PostView'][] site?: components['schemas']['Site'] } + UserMeResponse: { + community_blocks: components['schemas']['CommunityBlockView'][] + discussion_languages: components['schemas']['LanguageView'][] + follows: components['schemas']['CommunityFollowerView'][] + instance_blocks: components['schemas']['InstanceBlockView'][] + local_user_view: components['schemas']['LocalUserView'] + moderates: components['schemas']['CommunityModeratorView'][] + person_blocks: components['schemas']['PersonBlockView'][] + } UserLoginRequest: { username: string password: string @@ -4800,13 +5445,21 @@ export interface components { subscribed: 'Subscribed' | 'NotSubscribed' | 'Pending' } UserRepliesResponse: { - next_page?: string | null + next_page?: string replies: components['schemas']['CommentReplyView'][] } UserMentionsResponse: { - next_page?: string | null + next_page?: string replies: components['schemas']['CommentReplyView'][] } + MediaView: { + url?: string + name?: string + } + UserMediaResponse: { + next_page?: string + media: components['schemas']['MediaView'][] + } UserBlockRequest: { block: boolean person_id: number @@ -4827,23 +5480,33 @@ export interface components { person_view: components['schemas']['PersonView'] subscribed: boolean } + NewUserExtraField: { + /** @description Pass an id of an existing extra field with null/missing/empty label or text to remove a field. Pass an id of an existing extra field with both label and text to edit an existing extra field. */ + id?: number + /** @description Pass a label and text without an id to create a new extra field. */ + label?: string + /** @description Pass a label and text without an id to create a new extra field. */ + text?: string + } UserSaveSettingsRequest: { /** * Format: url * @description Pass a null value to remove the image */ - avatar?: string | null + avatar?: string /** Format: markdown */ bio?: string /** * Format: url * @description Pass a null value to remove the image */ - cover?: string | null + cover?: string /** @enum {string} */ default_comment_sort_type?: 'Hot' | 'Top' | 'New' | 'Old' /** @enum {string} */ default_sort_type?: 'Hot' | 'Top' | 'New' | 'Active' | 'Old' | 'Scaled' + /** @description A user can't have more than four total extra fields. */ + extra_fields?: components['schemas']['NewUserExtraField'][] show_nsfw?: boolean show_nsfl?: boolean show_read_posts?: boolean @@ -4875,6 +5538,8 @@ export interface components { /** @description returned for notif_types: 3, 4, 6 (comment_mention subtype) */ comment?: components['schemas']['Comment'] /** @description returned for notif_types: 3, 4, 6 (comment_mention subtype) */ + comment_view?: components['schemas']['CommentView'] + /** @description returned for notif_types: 3, 4, 6 (comment_mention subtype) */ comment_id?: number /** @description returned for notif_type 1 */ community?: components['schemas']['Community'] @@ -4887,9 +5552,9 @@ export interface components { counts: components['schemas']['UserNotificationsCounts'] items: components['schemas']['UserNotificationItemView'][] /** @enum {string} */ - status: 'All' | 'Unread' | 'Read' + status: 'All' | 'Unread' | 'Read' | 'New' username: string - next_page?: string | null + next_page?: string } UserNotificationStateRequest: { notif_id: number @@ -4906,14 +5571,22 @@ export interface components { UserSetFlairRequest: { community_id: number /** @description Either omit or set to null to remove existing flair */ - flair_text?: string | null + flair_text?: string } UserSetFlairResponse: { - person_view: components['schemas']['PersonView'] + person_view?: components['schemas']['PersonView'] + } + UserSetNoteRequest: { + person_id: number + /** @description Pass a value of null to remove existing note */ + note: string + } + UserSetNoteResponse: { + person_view?: components['schemas']['PersonView'] } ListCommentsResponse: { comments: components['schemas']['CommentView'][] - next_page?: string | null + next_page?: string } LikeCommentRequest: { comment_id: number @@ -5000,7 +5673,11 @@ export interface components { creator_is_moderator: boolean post: components['schemas']['Post'] saved: boolean - subscribed: string + /** + * @description Indicates whether auth'ed user is subscribed to the community this comment is in or not. + * @enum {string} + */ + subscribed: 'Subscribed' | 'NotSubscribed' | 'Pending' my_vote?: number can_auth_user_moderate?: boolean comment_report: components['schemas']['CommentReport'] @@ -5033,11 +5710,11 @@ export interface components { } ListCommentLikesResponse: { comment_likes: components['schemas']['CommentLikeView'][] - next_page?: string | null + next_page?: string } ListPostsResponse: { posts: components['schemas']['PostView'][] - next_page?: string | null + next_page?: string } GetPostResponse: { post_view: components['schemas']['PostView'] @@ -5049,7 +5726,7 @@ export interface components { activity_alert: boolean banned_from_community: boolean comment: components['schemas']['Comment'] - community?: components['schemas']['Community'] + community: components['schemas']['Community'] counts: components['schemas']['CommentAggregates'] creator: components['schemas']['Person'] creator_banned_from_community: boolean @@ -5058,20 +5735,23 @@ export interface components { creator_is_moderator: boolean post?: components['schemas']['Post'] saved: boolean - subscribed: string + /** + * @description Indicates whether auth'ed user is subscribed to the community this comment is in or not. + * @enum {string} + */ + subscribed: 'Subscribed' | 'NotSubscribed' | 'Pending' my_vote?: number can_auth_user_moderate?: boolean replies?: components['schemas']['PostReplyView'][] } GetPostRepliesResponse: { comments?: components['schemas']['PostReplyView'][] - next_page?: string | null + next_page?: string } LikePostRequest: { post_id: number score: number private?: boolean - auth?: string } SavePostRequest: { post_id: number @@ -5085,19 +5765,22 @@ export interface components { title: string community_id: number body?: string - /** Format: url */ url?: string nsfw?: boolean language_id?: number + event?: components['schemas']['PostEvent'] + poll?: components['schemas']['PostPoll'] } EditPostRequest: { post_id: number title?: string body?: string - /** Format: url */ + /** @description Pass value of null to remove the post url */ url?: string nsfw?: boolean language_id?: number + event?: components['schemas']['PostEvent'] + poll?: components['schemas']['PostPoll'] } DeletePostRequest: { post_id: number @@ -5106,6 +5789,12 @@ export interface components { ReportPostRequest: { post_id: number reason: string + description?: string + /** + * @description Also send report to originating instance + * @default true + */ + report_remote: boolean } PostReport: { id: number @@ -5146,7 +5835,8 @@ export interface components { FeaturePostRequest: { post_id: number featured: boolean - feature_type: string + /** @enum {string} */ + feature_type?: 'Community' } RemovePostRequest: { post_id: number @@ -5169,12 +5859,12 @@ export interface components { } ListPostLikesResponse: { post_likes: components['schemas']['PostLikeView'][] - next_page?: string | null + next_page?: string } PostSetFlairRequest: { post_id: number /** @description A list of all the flair id to assign to the post. Either pass an empty list or null to remove flair */ - flair_id_list?: number[] | null + flair_id_list?: number[] } PostSetFlairResponse: { banned_from_community: boolean @@ -5194,18 +5884,47 @@ export interface components { activity_alert?: boolean my_vote?: number flair_list?: components['schemas']['CommunityFlair'][] + can_auth_user_moderate?: boolean + } + PollVoteRequest: { + post_id: number + } + PollVoteResponse: { + banned_from_community: boolean + community: components['schemas']['Community'] + counts: components['schemas']['PostAggregates'] + creator: components['schemas']['Person'] + creator_banned_from_community: boolean + creator_is_admin: boolean + creator_is_moderator: boolean + hidden: boolean + post: components['schemas']['Post'] + read: boolean + saved: boolean + /** @enum {string} */ + subscribed: 'Subscribed' | 'NotSubscribed' | 'Pending' + unread_comments: number + activity_alert?: boolean + my_vote?: number + flair_list?: components['schemas']['CommunityFlair'][] + can_auth_user_moderate?: boolean } ImageUploadRequest: { /** Format: binary */ file: string } ImageUploadResponse: { - /** Format: url */ url: string liked_only?: boolean saved_only?: boolean q?: string } + ImageDeleteRequest: { + file: string + } + ImageDeleteResponse: { + result: string + } PrivateMessage: { id: number creator_id: number @@ -5226,6 +5945,7 @@ export interface components { private_message: components['schemas']['PrivateMessage'] creator: components['schemas']['Person'] recipient: components['schemas']['Person'] + conversation_id?: number } ListPrivateMessagesResponse: { private_messages: components['schemas']['PrivateMessageView'][] @@ -5233,6 +5953,9 @@ export interface components { GetPrivateMessageConversationResponse: { private_messages: components['schemas']['PrivateMessageView'][] } + LeaveConversationRequest: { + conversation_id: number + } CreatePrivateMessageRequest: { content: string recipient_id: number diff --git a/src/lib/api/types/generated.ts b/src/lib/api/types/generated.ts index 74f8220d..3e08a9c7 100644 --- a/src/lib/api/types/generated.ts +++ b/src/lib/api/types/generated.ts @@ -1950,6 +1950,8 @@ export interface Post { featured_local: boolean url_content_type?: string alt_text?: string + event?: PostEvent + poll?: PostPoll } export interface PostAggregates { @@ -2301,6 +2303,25 @@ export interface VoteView { score: number } +export interface SearchResponse { + type_: SearchType + comments: Array + posts: Array + communities: Array + users: Array +} + +export interface SiteResponse { + site_view: SiteView + taglines: Array +} +export interface SuccessResponse { + success: boolean +} +export interface UpdateTotpResponse { + enabled: boolean +} + export interface UploadImage { image: File | Buffer } @@ -2395,21 +2416,37 @@ export interface AssignFlair { flair_id_list: number[] } -export interface SearchResponse { - type_: SearchType - comments: Array - posts: Array - communities: Array - users: Array +export interface PostPoll { + end_poll?: string + mode: string + local_only: boolean + latest_vote?: string + choices: PollChoice[] } -export interface SiteResponse { - site_view: SiteView - taglines: Array -} -export interface SuccessResponse { - success: boolean -} -export interface UpdateTotpResponse { - enabled: boolean +export interface PollChoice { + id: number + choice_text: string + sort_order: number + num_votes: number +} + +export interface PostEvent { + start: string + end?: string + timezone?: string + max_attendees: number + participant_count: number + full: boolean + online_link?: string + join_mode?: string + external_participation_url?: string + anonymous_participation: boolean + online: boolean + buy_tickets_link?: string + event_fee_currency?: string + event_fee_amount: number + location?: { + [key: string]: unknown + } } diff --git a/src/lib/app/i18n/en.json b/src/lib/app/i18n/en.json index df84c6a6..f61ab8f5 100644 --- a/src/lib/app/i18n/en.json +++ b/src/lib/app/i18n/en.json @@ -579,6 +579,9 @@ "meta": { "lastEdited": "Last edited {{default}}", "created": "Created {{default}}" + }, + "poll": { + "votes": "{{votes:number}} votes" } }, "comment": { diff --git a/src/lib/feature/post/Post.svelte b/src/lib/feature/post/Post.svelte index 7f7e4be1..1c57136a 100644 --- a/src/lib/feature/post/Post.svelte +++ b/src/lib/feature/post/Post.svelte @@ -66,7 +66,7 @@ ], } }) - let type = $derived(mediaType(post.post.url)) + let type = $derived(mediaType(post.post)) let rule = $derived(getTagRule(tags.tags)) let hideTitle = $derived( settings.posts.deduplicateEmbed && diff --git a/src/lib/feature/post/helpers.ts b/src/lib/feature/post/helpers.ts index 761bf8b3..a65a1388 100644 --- a/src/lib/feature/post/helpers.ts +++ b/src/lib/feature/post/helpers.ts @@ -70,11 +70,29 @@ export function postLink(post: Post) { return `/post/${encodeURIComponent(profile.current.instance)}/${post.id}` } -export type MediaType = 'video' | 'image' | 'iframe' | 'embed' | 'none' +export type MediaType = + | 'video' + | 'image' + | 'iframe' + | 'embed' + | 'poll' + | 'event' + | 'none' export type IframeType = 'youtube' | 'video' | 'none' -export function mediaType(url?: string): MediaType { +export function mediaType(post?: Post | string): MediaType { + if (!post) return 'none' + + const isPost = typeof post != 'string' + const url = isPost ? post.url : post + + if (isPost) { + if (post.poll) return 'poll' + if (post.event) return 'event' + } + if (!url) return 'none' + try { new URL(url) } catch { diff --git a/src/lib/feature/post/media/PostMedia.svelte b/src/lib/feature/post/media/PostMedia.svelte index 6178314e..01461aa3 100644 --- a/src/lib/feature/post/media/PostMedia.svelte +++ b/src/lib/feature/post/media/PostMedia.svelte @@ -3,6 +3,7 @@ import { settings, type View } from '$lib/app/settings.svelte' import { PostIframe, PostImage, PostLink } from '..' import { iframeType, type MediaType } from '../helpers' + import PostPoll from './PostPoll.svelte' interface Props { view?: View @@ -49,4 +50,6 @@ {view} {...rest} /> +{:else if type == 'poll' && post.poll} + {/if} diff --git a/src/lib/feature/post/media/PostPoll.svelte b/src/lib/feature/post/media/PostPoll.svelte new file mode 100644 index 00000000..9e43e8f1 --- /dev/null +++ b/src/lib/feature/post/media/PostPoll.svelte @@ -0,0 +1,93 @@ + + +
+ + {#each post.poll.choices as choice} +
  • + {#if chosen} +
    + {/if} + +
  • + {/each} +
    + + {$t('post.poll.votes', { votes: totalVotes })} + {#snippet action()} + {#if post.poll.latest_vote} + + {/if} + {/snippet} + +
    diff --git a/src/routes/post/[instance]/[id=integer]/+page.svelte b/src/routes/post/[instance]/[id=integer]/+page.svelte index e6b458cf..9f9eec75 100644 --- a/src/routes/post/[instance]/[id=integer]/+page.svelte +++ b/src/routes/post/[instance]/[id=integer]/+page.svelte @@ -159,7 +159,7 @@ Date: Fri, 14 Nov 2025 13:48:07 -0800 Subject: [PATCH 02/11] fix: allow future dates with RelativeDate.svelte --- src/lib/ui/util/RelativeDate.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ui/util/RelativeDate.svelte b/src/lib/ui/util/RelativeDate.svelte index 3ec071fb..4776cbd1 100644 --- a/src/lib/ui/util/RelativeDate.svelte +++ b/src/lib/ui/util/RelativeDate.svelte @@ -24,7 +24,7 @@ ] for (let i = thresholds.length - 1; i >= 0; i--) { - if (diffInMillis >= thresholds[i].threshold) { + if (Math.abs(diffInMillis) >= thresholds[i].threshold) { const value = Math.round(diffInMillis / thresholds[i].threshold) let language = locale ?? 'en' -- 2.51.2 From 90d7ac17ff14e8a226c14184e3af46f002d8f740 Mon Sep 17 00:00:00 2001 From: Xylight Date: Fri, 14 Nov 2025 14:28:27 -0800 Subject: [PATCH 03/11] feat: [2/3]: add poll vote functionality --- src/lib/api/base.ts | 1 + src/lib/api/piefed/adapter.ts | 10 ++ src/lib/api/piefed/schema.d.ts | 1 + src/lib/api/types/generated.ts | 5 + src/lib/app/i18n/en.json | 7 +- src/lib/feature/post/feed/VirtualFeed.svelte | 2 +- src/lib/feature/post/media/PostPoll.svelte | 130 ++++++++++++++----- 7 files changed, 119 insertions(+), 37 deletions(-) diff --git a/src/lib/api/base.ts b/src/lib/api/base.ts index 87c90a9f..c26f1700 100644 --- a/src/lib/api/base.ts +++ b/src/lib/api/base.ts @@ -235,6 +235,7 @@ export abstract class BaseClient { abstract getTopics?(form: types.GetTopics): Promise abstract assignFlair?(form: types.AssignFlair): Promise abstract listMedia(form: types.ListMedia): Promise + abstract voteOnPoll?(form: types.PollVote): Promise } export type NullableFnArg = T extends ( diff --git a/src/lib/api/piefed/adapter.ts b/src/lib/api/piefed/adapter.ts index 2efea6d2..476a3f19 100644 --- a/src/lib/api/piefed/adapter.ts +++ b/src/lib/api/piefed/adapter.ts @@ -1020,4 +1020,14 @@ export class PiefedClient implements BaseClient { async listMedia(): ReturnType { throw new Error('unsupported') } + + async voteOnPoll( + params: NullableFnArg[0], + ): NullableFnReturn { + const response = ( + await this.#client.POST('/api/alpha/post/poll_vote', { body: params }) + ).data! + + return toPostView(response) + } } diff --git a/src/lib/api/piefed/schema.d.ts b/src/lib/api/piefed/schema.d.ts index 2c60c1cb..d0627795 100644 --- a/src/lib/api/piefed/schema.d.ts +++ b/src/lib/api/piefed/schema.d.ts @@ -5888,6 +5888,7 @@ export interface components { } PollVoteRequest: { post_id: number + choice_id: number } PollVoteResponse: { banned_from_community: boolean diff --git a/src/lib/api/types/generated.ts b/src/lib/api/types/generated.ts index 3e08a9c7..2eeb6c5b 100644 --- a/src/lib/api/types/generated.ts +++ b/src/lib/api/types/generated.ts @@ -2450,3 +2450,8 @@ export interface PostEvent { [key: string]: unknown } } + +export interface PollVote { + post_id: number + choice_id: number +} diff --git a/src/lib/app/i18n/en.json b/src/lib/app/i18n/en.json index f61ab8f5..ed7444fc 100644 --- a/src/lib/app/i18n/en.json +++ b/src/lib/app/i18n/en.json @@ -581,7 +581,12 @@ "created": "Created {{default}}" }, "poll": { - "votes": "{{votes:number}} votes" + "votes": "{{votes:number}} votes", + "results": { + "show": "Show results", + "hide": "Hide results", + "ends": "Ends {{time}}" + } } }, "comment": { diff --git a/src/lib/feature/post/feed/VirtualFeed.svelte b/src/lib/feature/post/feed/VirtualFeed.svelte index c8729e18..92e69019 100644 --- a/src/lib/feature/post/feed/VirtualFeed.svelte +++ b/src/lib/feature/post/feed/VirtualFeed.svelte @@ -203,7 +203,7 @@ onhide={() => { posts = posts.toSpliced(row, 1) }} - class="px-3 sm:px-6 hover:bg-slate-100/50 hover:dark:bg-zinc-900/50 transition-colors" + class="px-3 sm:px-6 hover:bg-slate-100/30 hover:dark:bg-zinc-900/30 transition-colors" > {/snippet} diff --git a/src/lib/feature/post/media/PostPoll.svelte b/src/lib/feature/post/media/PostPoll.svelte index 9e43e8f1..70981ee4 100644 --- a/src/lib/feature/post/media/PostPoll.svelte +++ b/src/lib/feature/post/media/PostPoll.svelte @@ -1,11 +1,14 @@
    - {#each post.poll.choices as choice} -
  • + {#each options.toSorted((a, b) => a.sort_order - b.sort_order) as choice} + {@const active = selected == choice.id} + {@const percentage = Math.floor( + (choice.num_votes / totalVotes || 0) * 100, + )} +
  • {#if chosen}
    {/if} diff --git a/src/lib/ui/shared/button/Button.svelte b/src/lib/ui/shared/button/Button.svelte index 101719a7..da153ccb 100644 --- a/src/lib/ui/shared/button/Button.svelte +++ b/src/lib/ui/shared/button/Button.svelte @@ -314,21 +314,25 @@ .btn-square-sm { width: calc(var(--spacing) * 6); height: calc(var(--spacing) * 6); + padding: 0; } .btn-square-md { width: calc(var(--spacing) * 8); height: calc(var(--spacing) * 8); + padding: 0; } .btn-square-lg { width: calc(var(--spacing) * 9.5); height: calc(var(--spacing) * 9.5); + padding: 0; } .btn-square-xl { width: calc(var(--spacing) * 12); height: calc(var(--spacing) * 12); + padding: 0; } .btn-disabled { diff --git a/src/lib/ui/shared/button/ButtonGroup.svelte b/src/lib/ui/shared/button/ButtonGroup.svelte index 477fddb6..6ff46d45 100644 --- a/src/lib/ui/shared/button/ButtonGroup.svelte +++ b/src/lib/ui/shared/button/ButtonGroup.svelte @@ -39,7 +39,7 @@ .btn-group.btn-group-horizontal :global(> *) { position: relative; - border-radius: 8px 8px 8px 8px !important; + border-radius: 8px !important; &:first-child { border-radius: 24px 12px 12px 24px !important; diff --git a/src/routes/create/+layout.svelte b/src/routes/create/+layout.svelte index 477466bd..0c3c7996 100644 --- a/src/routes/create/+layout.svelte +++ b/src/routes/create/+layout.svelte @@ -7,26 +7,24 @@ let { children } = $props() +{#if !site || !(site.data?.site_view.local_site.community_creation_admin_only && !profile.isAdmin)} + +{/if} +
    {$t('nav.create.label')} - {#snippet extended()} - {#if !site || !(site.data?.site_view.local_site.community_creation_admin_only && !profile.isAdmin)} - - {/if} - {/snippet}
    {@render children?.()} -- 2.51.2 From 1e09647e5d303db417689b99c4a50d938a80784f Mon Sep 17 00:00:00 2001 From: Xylight Date: Sat, 15 Nov 2025 16:27:01 -0800 Subject: [PATCH 07/11] fix: extract type selection to PostFormState class for consistency --- .../feature/post/actions/PostActions.svelte | 5 +++- src/lib/feature/post/form/PostForm.svelte | 24 +++++++++---------- src/lib/feature/post/form/postform.svelte.ts | 17 ++++++------- src/routes/create/post/+page.svelte | 6 ++++- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/lib/feature/post/actions/PostActions.svelte b/src/lib/feature/post/actions/PostActions.svelte index 6c233860..a60f4ec3 100644 --- a/src/lib/feature/post/actions/PostActions.svelte +++ b/src/lib/feature/post/actions/PostActions.svelte @@ -89,7 +89,10 @@ post = e onedit?.(e) }} - init={new PostFormState({ ...post.post })} + init={new PostFormState({ + type: post.post.poll ? 'poll' : post.post.event ? 'event' : 'normal', + ...post.post, + })} > {#snippet title()}{/snippet} diff --git a/src/lib/feature/post/form/PostForm.svelte b/src/lib/feature/post/form/PostForm.svelte index d7c31282..38420556 100644 --- a/src/lib/feature/post/form/PostForm.svelte +++ b/src/lib/feature/post/form/PostForm.svelte @@ -56,9 +56,8 @@ let { editPost, init, title, onsubmit }: Props = $props() - let form = $state(init ?? new PostFormState()) - let type = $derived<'normal' | 'poll' | 'event'>( - init?.poll ? 'poll' : init?.event ? 'event' : 'normal', + let form = $state( + init ?? new PostFormState({ type: 'normal' }), ) let extendedCommunity = $derived.by(() => { @@ -83,7 +82,7 @@ $t('form.post.types.poll'), $t('form.post.types.event'), ]} - bind:selected={type} + bind:selected={form.type} /> {/if} @@ -173,7 +172,7 @@ loading = true form - .submit(type, editPost) + .submit(editPost) .then(onsubmit) .catch((err) => pushError({ message: errorMessage(err as string), scope: 'post-form' }), @@ -242,7 +241,7 @@ previewButton /> - {#if type == 'normal' && form.url !== undefined} + {#if form.type == 'normal' && form.url !== undefined} {/if} - {#if type == 'poll' && form.poll} + {#if form.type == 'poll' && form.poll}
    - {#each form.poll.choices as choice, index} + + {#each form.poll.choices as _, index}
  • - {/if} - {#if settings.debugInfo} - {form.poll?.end_poll} + {#if settings.debugInfo} + {form.poll?.end_poll} + {/if} {/if}
    @@ -354,7 +354,7 @@ orientation="horizontal" class="flex flex-row flex-wrap w-full" > - {#if type == 'normal'} + {#if form.type == 'normal'} + +
    + diff --git a/src/routes/u/[name]/UserActions.svelte b/src/routes/u/[name]/UserActions.svelte index 3c15bb05..d91a3155 100644 --- a/src/routes/u/[name]/UserActions.svelte +++ b/src/routes/u/[name]/UserActions.svelte @@ -5,6 +5,7 @@ import { t } from '$lib/app/i18n' import { ban } from '$lib/feature/moderation/moderation' import { blockUser, isBlocked } from '$lib/feature/user' + import UserNote from '$lib/feature/user/UserNote.svelte' import { Button, Menu, @@ -23,11 +24,13 @@ NoSymbol, ShieldCheck, ShieldExclamation, + Tag, } from 'svelte-hero-icons/dist' let { person }: { person: PersonView } = $props() let purgingUser = $state(false) + let setNote = $state(false) async function purgeUser() { purgingUser = false @@ -67,6 +70,27 @@ {/if} +{#if setNote} + + {#snippet customTitle()} + {$t('routes.user.note')} + {/snippet} + { + // lol + person.person.note = e ?? undefined + setNote = !setNote + toast({ + content: $t('message.success'), + type: 'success', + }) + }} + /> + +{/if} + {#if profile.current?.user && profile.current.jwt && person.person.id != profile.current.user.local_user_view.person.id}