mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
b4cbb39f17
* feat: Request document access Allow users without permission to a document to request access. Notifies document managers via in-app notification and email; managers can grant or dismiss the request. - Adds AccessRequest model, migration, policy, presenter - Adds accessRequests.create/info/approve/dismiss endpoints - Adds DocumentAccessRequestNotificationsTask + email - Adds Error403 request flow with loading state and pending indicator - Auto-opens notifications popover via ?notifications=true (used in email) - Adds SplitButton primitive for permission selection in notifications - Refactors useConsumeQueryParam hook * refactor * fix: Make approve/dismiss idempotent on access requests Return success when the access request has already been dismissed, or when the user already has document membership at approve time, instead of throwing 400. Avoids racy double-clicks on notification actions producing user-visible errors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * Minor fixes --------- Co-authored-by: Tom Moor <tom@getoutline.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
35 lines
1.0 KiB
TypeScript
35 lines
1.0 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
import { useHistory } from "react-router-dom";
|
|
import useQuery from "./useQuery";
|
|
|
|
/**
|
|
* Hook that reads a query parameter from the URL and removes it after the first
|
|
* render. Returns the value that was present when the component mounted, or
|
|
* null if the parameter was absent.
|
|
*
|
|
* @param name - the query parameter name to consume.
|
|
* @returns the consumed value, or null.
|
|
*/
|
|
export default function useConsumeQueryParam(name: string): string | null {
|
|
const query = useQuery();
|
|
const history = useHistory();
|
|
const value = query.get(name);
|
|
const consumedRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (value && !consumedRef.current) {
|
|
consumedRef.current = true;
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
params.delete(name);
|
|
const search = params.toString();
|
|
history.replace({
|
|
pathname: window.location.pathname,
|
|
search: search ? `?${search}` : "",
|
|
});
|
|
}
|
|
}, [value, name, history]);
|
|
|
|
return consumedRef.current ? null : value;
|
|
}
|