From 34e8d0eae336870bc2bcb7e9a80723e85af3ecb3 Mon Sep 17 00:00:00 2001 From: John Groszko Date: Thu, 23 Apr 2026 14:54:47 -0500 Subject: [PATCH] UI for advanced sub permissions --- web/api.py | 28 ++++-- web/vite/src/ProfilePermissions.tsx | 129 ++++++++++++++++++++++++++++ web/vite/src/UserOrderSets.tsx | 25 ++++-- web/vite/src/index.d.ts | 4 + 4 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 web/vite/src/ProfilePermissions.tsx diff --git a/web/api.py b/web/api.py index 3977d13..ec8ba4e 100644 --- a/web/api.py +++ b/web/api.py @@ -6,7 +6,7 @@ from flask import Blueprint, jsonify, abort, request from flask_login import login_required, current_user from db.constants import TIMELINE_ORDERS_POOL_CREATED, TIMELINE_ORDERS_POOL_DELETED, TIMELINE_ORDERS_POOL_UPDATED from db.models import database, OrdersPool, Order, OrderAddOn, MastodonServer -from db.queries import timeline_event_put, timeline_event_recent, user_can_orders_pools_details, user_can_orders_pools_edit, user_can_orders_pools_view, user_get, domsubusers_list, orders_pool_list, orders_pool, mastodon_server_get, mastodon_server_put, user_has_doms, user_mastodon_server_set, user_preferences_set +from db.queries import timeline_event_put, timeline_event_recent, user_can_orders_pools_details, user_can_orders_pools_edit, user_can_orders_pools_view, user_doms, user_get, domsubusers_list, orders_pool_list, orders_pool, mastodon_server_get, mastodon_server_put, user_has_doms, user_mastodon_server_set, user_preferences_set from settings import MASTODON_OAUTH_CLIENT_NAME, MASTODON_OAUTH_REDIRECT_URI, MASTODON_OAUTH_SCOPES, MASTODON_OAUTH_CLIENT_WEBSITE from util import time_sqlite @@ -164,15 +164,27 @@ def authorized_sub(func): @authorized_sub def sub(username, sub): if request.method == "POST": - if user_has_doms(sub) and sub.id == current_user.db_user.id: + if (user_has_doms(sub) and sub.id == current_user.db_user.id and + ('permission_orders_pools_view' in request.json or + 'permission_orders_pools_details' in request.json or + 'permission_orders_pools_edit' in request.json)): abort(403) return - sub.verify_mastodon_favorite = bool(request.json['verify_mastodon_favorite']) - sub.verify_mastodon_alt_text = bool(request.json['verify_mastodon_alt_text']) - if request.json['verify_delay'] is not None: + if 'verify_mastodon_favorite' in request.json: + sub.verify_mastodon_favorite = bool(request.json['verify_mastodon_favorite']) + if 'verify_mastodon_alt_text' in request.json: + sub.verify_mastodon_alt_text = bool(request.json['verify_mastodon_alt_text']) + if 'verify_delay' in request.json and request.json['verify_delay'] is not None: sub.verify_delay = int(request.json["verify_delay"]) + if 'permission_orders_pools_view' in request.json: + sub.permission_orders_pools_view = bool(request.json['permission_orders_pools_view']) + if 'permission_orders_pools_details' in request.json: + sub.permission_orders_pools_details = bool(request.json['permission_orders_pools_details']) + if 'permission_orders_pools_edit' in request.json: + sub.permission_orders_pools_edit = bool(request.json['permission_orders_pools_edit']) + sub.save() return ('', 204) @@ -183,7 +195,11 @@ def sub(username, sub): "mastodon_username": sub.mastodon_username, "verify_mastodon_alt_text": sub.verify_mastodon_alt_text, "verify_mastodon_favorite": sub.verify_mastodon_favorite, - "verify_delay": sub.verify_delay + "verify_delay": sub.verify_delay, + "can_edit_permissions": current_user.db_user in user_doms(sub.id), + "permission_orders_pools_view": sub.permission_orders_pools_view, + "permission_orders_pools_details": sub.permission_orders_pools_details, + "permission_orders_pools_edit": sub.permission_orders_pools_edit }) @api.route('/orders/') diff --git a/web/vite/src/ProfilePermissions.tsx b/web/vite/src/ProfilePermissions.tsx new file mode 100644 index 0000000..ee1550a --- /dev/null +++ b/web/vite/src/ProfilePermissions.tsx @@ -0,0 +1,129 @@ +import React from "react"; +import { Button, Checkbox, Paper, Title } from "@mantine/core"; +import { useForm } from "@mantine/form"; +import { notifications } from "@mantine/notifications"; +import { fetchHeaders } from "./fetch"; + +type ProfilePermissionsProps = Pick< + UserProfile, + | "permission_orders_pools_view" + | "permission_orders_pools_details" + | "permission_orders_pools_edit" +> & { username: string }; + +type ProfilePermissionsForm = Pick< + UserProfile, + | "permission_orders_pools_view" + | "permission_orders_pools_details" + | "permission_orders_pools_edit" +>; + +export const ProfilePermissions: React.FC = ({ + username, + permission_orders_pools_view, + permission_orders_pools_details, + permission_orders_pools_edit, +}) => { + const [loading, setLoading] = React.useState(false); + const [detailsDisabled, setDetailsDisabled] = React.useState( + !permission_orders_pools_view, + ); + const [editDisabled, setEditDisabled] = React.useState( + !permission_orders_pools_details, + ); + const form = useForm({ + mode: "uncontrolled", + initialValues: { + permission_orders_pools_view, + permission_orders_pools_details, + permission_orders_pools_edit, + }, + }); + + form.watch("permission_orders_pools_view", ({ value }) => { + if (!value) { + form.setFieldValue("permission_orders_pools_details", false); + setDetailsDisabled(true); + } else { + setDetailsDisabled(false); + } + }); + + form.watch("permission_orders_pools_details", ({ value }) => { + if (!value) { + form.setFieldValue("permission_orders_pools_edit", false); + setEditDisabled(true); + } else { + setEditDisabled(false); + } + }); + + const handleSubmit = React.useCallback( + form.onSubmit((values) => { + setLoading(true); + fetch(`/api/subs/${username}`, { + method: "POST", + headers: fetchHeaders(), + body: JSON.stringify(values), + }) + .then((response) => { + if (response.ok) { + notifications.show({ + title: "Success", + message: "Permissions have been saved", + color: "green", + }); + } else { + notifications.show({ + title: "Error", + message: "There was a problem saving permissions", + color: "red", + }); + } + }) + .finally(() => { + setLoading(false); + }); + }), + [], + ); + + return ( + +
+ + Permissions + + + + + + +
+ ); +}; diff --git a/web/vite/src/UserOrderSets.tsx b/web/vite/src/UserOrderSets.tsx index 1ebbd6e..35ba630 100644 --- a/web/vite/src/UserOrderSets.tsx +++ b/web/vite/src/UserOrderSets.tsx @@ -3,6 +3,7 @@ import { Params, useLoaderData, useParams, Link } from "react-router"; import { OrderSetProps, OrderSets, OrderSetsResponse } from "./OrderSets"; import { ProfileVerification } from "./ProfileVerification"; import { Anchor, Title } from "@mantine/core"; +import { ProfilePermissions } from "./ProfilePermissions"; export const userOrderSetsLoader = async ({ params: { username }, @@ -16,11 +17,11 @@ export const UserOrderSets: React.FC = () => { const [profile, setProfile] = React.useState(null); React.useEffect(() => { - // fetch(`/api/subs/${sub_username}`).then(async (response) => { - // if (response.ok) { - // setProfile(await response.json()); - // } - // }); + fetch(`/api/subs/${sub_username}`).then(async (response) => { + if (response.ok) { + setProfile(await response.json()); + } + }); }, [sub_username]); return ( @@ -46,6 +47,20 @@ export const UserOrderSets: React.FC = () => { verify_mastodon_favorite={profile.verify_mastodon_favorite} verify_delay={profile.verify_delay} /> + {profile.can_edit_permissions ? ( + + ) : null} ) : null} diff --git a/web/vite/src/index.d.ts b/web/vite/src/index.d.ts index 08028d2..ea4e6a4 100644 --- a/web/vite/src/index.d.ts +++ b/web/vite/src/index.d.ts @@ -9,6 +9,10 @@ type UserProfile = { verify_mastodon_alt_text?: boolean; verify_mastodon_favorite?: boolean; verify_delay?: number; + can_edit_permissions?: boolean; + permission_orders_pools_view?: boolean; + permission_orders_pools_details?: boolean; + permission_orders_pools_edit?: boolean; } type OrderSetOrderAddOn = {