diff --git a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx
index 24d75c4..e62d42c 100644
--- a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx
+++ b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx
@@ -1,8 +1,11 @@
+import { Roles, RolesConstants } from "@/app/ts/Roles";
+import { MapfixStatus } from "@/app/ts/Mapfix";
 import { Button, ButtonOwnProps } from "@mui/material";
+import { useState, useEffect } from "react";
 
 type Actions    = "Completed" | "Submit" | "Reject" | "Revoke"
-type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating"
-type Review     = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes"
+type ApiActions = Lowercase<Actions> | "request-changes" | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating"
+type Review     = Actions | "Request Changes" | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes"
 
 interface ReviewButton {
 	name: Review,
@@ -12,7 +15,9 @@ interface ReviewButton {
 }
 
 interface ReviewId {
-	mapfixId: string
+	mapfixId: string,
+	mapfixStatus: number,
+	mapfixSubmitter: number,
 }
 
 async function ReviewButtonClicked(action: ApiActions, mapfixId: string) {
@@ -45,7 +50,6 @@ function ReviewButton(props: ReviewButton) {
 }
 
 export default function ReviewButtons(props: ReviewId) {
-	const mapfixId = props.mapfixId
 	// When is each button visible?
 	// Multiple buttons can be visible at once.
 	// Action         | Role      | When Current Status is One of:
@@ -59,16 +63,82 @@ export default function ReviewButtons(props: ReviewId) {
 	// RequestChanges | Reviewer  | Validated, Accepted, Submitted
 	// Upload         | MapAdmin  | Validated
 	// ResetUploading | MapAdmin  | Uploading
+	const { mapfixId, mapfixStatus } = props;
+	const [user, setUser] = useState<number|null>(null);
+	const [roles, setRoles] = useState<Roles>(RolesConstants.Empty);
+	const [loading, setLoading] = useState(true);
+
+	useEffect(() => {
+		async function fetchData() {
+			try {
+				const [rolesData, userData] = await Promise.all([
+					fetch("/api/session/roles").then(rolesResponse => rolesResponse.json()),
+					fetch("/api/session/user").then(userResponse => userResponse.json())
+				]);
+
+				setRoles(rolesData.Roles);
+				setUser(userData.UserID);
+			} catch (error) {
+				console.error("Error fetching data:", error);
+			} finally {
+				setLoading(false);
+			}
+		}
+
+		fetchData();
+	}, [mapfixId]);
+
+	if (loading) return <p>Loading...</p>;
+
+	const visibleButtons: ReviewButton[] = [];
+
+	const is_submitter = user === props.mapfixSubmitter;
+	if (is_submitter) {
+		if ([MapfixStatus.UnderConstruction, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
+			visibleButtons.push({ name: "Submit", action: "submit", color: "info", mapfixId });
+		}
+		if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) {
+			visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", mapfixId });
+		}
+	}
+
+	if (roles&RolesConstants.MapfixReview) {
+		// you can't review your own mapfix!
+		// note that this means there needs to be more than one person with MapfixReview
+		if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) {
+			visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", mapfixId });
+			visibleButtons.push({ name: "Reject", action: "reject", color: "error", mapfixId });
+		}
+		if (mapfixStatus === MapfixStatus.Accepted) {
+			visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", mapfixId });
+		}
+		if (mapfixStatus === MapfixStatus.Validating) {
+			visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", mapfixId });
+		}
+		// this button serves the same purpose as Revoke if you are both
+		// the map submitter and have MapfixReview when status is Submitted
+		if (
+			[MapfixStatus.Validated, MapfixStatus.Accepted].includes(mapfixStatus!)
+			|| !is_submitter && mapfixStatus == MapfixStatus.Submitted
+		) {
+			visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", mapfixId });
+		}
+	}
+
+	if (roles&RolesConstants.MapfixUpload) {
+		if (mapfixStatus === MapfixStatus.Validated) {
+			visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", mapfixId });
+		}
+		if (mapfixStatus === MapfixStatus.Uploading) {
+			visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", mapfixId });
+		}
+	}
+
 	return (
 		<section className="review-set">
-			<ReviewButton color="info"  name="Submit"    action="submit"           mapfixId={mapfixId}/>
-			<ReviewButton color="info"  name="Revoke"    action="revoke"           mapfixId={mapfixId}/>
-			<ReviewButton color="info"  name="Accept"    action="trigger-validate" mapfixId={mapfixId}/>
-			<ReviewButton color="info"  name="Validate"  action="retry-validate"   mapfixId={mapfixId}/>
-			<ReviewButton color="error" name="Reject"    action="reject"           mapfixId={mapfixId}/>
-			<ReviewButton color="info"  name="Upload"    action="trigger-upload"   mapfixId={mapfixId}/>
-			<ReviewButton color="error" name="Reset Uploading (fix softlocked status)"    action="reset-uploading"   mapfixId={mapfixId}/>
-			<ReviewButton color="error" name="Reset Validating (fix softlocked status)"   action="reset-validating"  mapfixId={mapfixId}/>
+			{visibleButtons.map((btn) => (
+				<ReviewButton key={btn.action} {...btn} />
+			))}
 		</section>
-	)
+	);
 }