mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-22 03:36:39 +00:00
Compare commits
4 commits
6b810c6670
...
e62f5f9dbc
Author | SHA1 | Date | |
---|---|---|---|
e62f5f9dbc | |||
49eb8f602e | |||
cad76b8a25 | |||
3b037d165c |
|
@ -78,11 +78,13 @@ Some examples:
|
||||||
|
|
||||||
#### Visibility Level of Posts to Show on Your Profile
|
#### Visibility Level of Posts to Show on Your Profile
|
||||||
|
|
||||||
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web view of your profile, and served in your RSS feed (if you have enabled RSS).
|
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web views of your profile, of your statuses, and in your RSS feed (if you have enabled RSS).
|
||||||
|
|
||||||
**By default, GoToSocial shows only Public visibility posts on the web view of your profile, not Unlisted.** You can adjust this setting to also show Unlisted visibility posts on your profile, which is similar to the default for other ActivityPub softwares like Mastodon etc.
|
**By default, GoToSocial shows only Public visibility posts on its web views, not Unlisted.** You can adjust this setting to also show Unlisted visibility posts, which is similar to the default for other ActivityPub softwares like Mastodon etc.
|
||||||
|
|
||||||
You can also choose to show no posts at all on the web view of your profile. This allows you to write posts without having to worry about scrapers, rubberneckers, and other nosy parkers visiting your web profile and looking at your posts.
|
You can also choose to show no posts at all on GoToSocial's web views. This allows you to write posts without having to worry about scrapers, rubberneckers, and other nosy parkers visiting your web profile and looking at your posts.
|
||||||
|
|
||||||
|
This setting only applies to the visibility of your own posts. Other user's Unlisted posts are never shown.
|
||||||
|
|
||||||
This setting does not affect visibility of your posts over the ActivityPub protocol, so even if you choose to show no posts on your public web profile, others will be able to see your posts in their client if they follow you, and/or have your posts boosted onto their timeline, use a link to search a post of yours, etc.
|
This setting does not affect visibility of your posts over the ActivityPub protocol, so even if you choose to show no posts on your public web profile, others will be able to see your posts in their client if they follow you, and/or have your posts boosted onto their timeline, use a link to search a post of yours, etc.
|
||||||
|
|
||||||
|
|
|
@ -20,17 +20,17 @@
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type DomainPermissionDraft struct {
|
type DomainPermissionDraft struct {
|
||||||
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
|
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
|
||||||
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was created.
|
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was created.
|
||||||
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was last updated.
|
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Time when this item was last updated.
|
||||||
PermissionType DomainPermissionType `bun:",notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Permission type of the draft.
|
PermissionType DomainPermissionType `bun:",notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Permission type of the draft.
|
||||||
Domain string `bun:",nullzero,notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Domain to block or allow. Eg. 'whatever.com'.
|
Domain string `bun:",nullzero,notnull,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // Domain to block or allow. Eg. 'whatever.com'.
|
||||||
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this subscription.
|
CreatedByAccountID string `bun:"type:CHAR(26),nullzero,notnull"` // Account ID of the creator of this subscription.
|
||||||
CreatedByAccount *Account `bun:"-"` // Account corresponding to createdByAccountID.
|
CreatedByAccount *Account `bun:"-"` // Account corresponding to createdByAccountID.
|
||||||
PrivateComment string `bun:",nullzero"` // Private comment on this perm, viewable to admins.
|
PrivateComment string `bun:",nullzero"` // Private comment on this perm, viewable to admins.
|
||||||
PublicComment string `bun:",nullzero"` // Public comment on this perm, viewable (optionally) by everyone.
|
PublicComment string `bun:",nullzero"` // Public comment on this perm, viewable (optionally) by everyone.
|
||||||
Obfuscate *bool `bun:",nullzero,notnull,default:false"` // Obfuscate domain name when displaying it publicly.
|
Obfuscate *bool `bun:",nullzero,notnull,default:false"` // Obfuscate domain name when displaying it publicly.
|
||||||
SubscriptionID string `bun:"type:CHAR(26),nullzero,unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // ID of the subscription that created this draft, if any.
|
SubscriptionID string `bun:"type:CHAR(26),unique:domain_permission_drafts_permission_type_domain_subscription_id_uniq"` // ID of the subscription that created this draft, if any.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DomainPermissionDraft) GetID() string {
|
func (d *DomainPermissionDraft) GetID() string {
|
||||||
|
|
|
@ -21,14 +21,15 @@ import { gtsApi } from "../../gts-api";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
DomainPerm,
|
DomainPerm,
|
||||||
ModeratorDomainPermissionDraftSearchParams,
|
DomainPermDraftCreateParams,
|
||||||
ModeratorDomainPermissionDraftSearchResp,
|
DomainPermDraftSearchParams,
|
||||||
|
DomainPermDraftSearchResp,
|
||||||
} from "../../../types/domain-permission";
|
} from "../../../types/domain-permission";
|
||||||
import parse from "parse-link-header";
|
import parse from "parse-link-header";
|
||||||
|
|
||||||
const extended = gtsApi.injectEndpoints({
|
const extended = gtsApi.injectEndpoints({
|
||||||
endpoints: (build) => ({
|
endpoints: (build) => ({
|
||||||
searchDomainPermissionDrafts: build.query<ModeratorDomainPermissionDraftSearchResp, ModeratorDomainPermissionDraftSearchParams>({
|
searchDomainPermissionDrafts: build.query<DomainPermDraftSearchResp, DomainPermDraftSearchParams>({
|
||||||
query: (form) => {
|
query: (form) => {
|
||||||
const params = new(URLSearchParams);
|
const params = new(URLSearchParams);
|
||||||
Object.entries(form).forEach(([k, v]) => {
|
Object.entries(form).forEach(([k, v]) => {
|
||||||
|
@ -66,6 +67,17 @@ const extended = gtsApi.injectEndpoints({
|
||||||
{ type: 'DomainPermissionDraft', id }
|
{ type: 'DomainPermissionDraft', id }
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
createDomainPermissionDraft: build.mutation<DomainPerm, DomainPermDraftCreateParams>({
|
||||||
|
query: (formData) => ({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v1/admin/domain_permission_drafts`,
|
||||||
|
asForm: true,
|
||||||
|
body: formData,
|
||||||
|
discardEmpty: true
|
||||||
|
}),
|
||||||
|
invalidatesTags: [{ type: "DomainPermissionDraft", id: "TRANSFORMED" }],
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,7 +91,13 @@ const useLazySearchDomainPermissionDraftsQuery = extended.useLazySearchDomainPer
|
||||||
*/
|
*/
|
||||||
const useGetDomainPermissionDraftQuery = extended.useGetDomainPermissionDraftQuery;
|
const useGetDomainPermissionDraftQuery = extended.useGetDomainPermissionDraftQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a domain permission draft with the given parameters.
|
||||||
|
*/
|
||||||
|
const useCreateDomainPermissionDraftMutation = extended.useCreateDomainPermissionDraftMutation;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useLazySearchDomainPermissionDraftsQuery,
|
useLazySearchDomainPermissionDraftsQuery,
|
||||||
useGetDomainPermissionDraftQuery,
|
useGetDomainPermissionDraftQuery,
|
||||||
|
useCreateDomainPermissionDraftMutation,
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,6 +33,8 @@ export interface DomainPerm {
|
||||||
private_comment?: string;
|
private_comment?: string;
|
||||||
public_comment?: string;
|
public_comment?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
|
created_by?: string;
|
||||||
|
subscription_id?: string;
|
||||||
|
|
||||||
// Keys that should be stripped before
|
// Keys that should be stripped before
|
||||||
// sending the domain permission (if imported).
|
// sending the domain permission (if imported).
|
||||||
|
@ -99,7 +101,7 @@ export interface ExportDomainPermsParams {
|
||||||
/**
|
/**
|
||||||
* Parameters for GET to /api/v1/admin/domain_permission_drafts.
|
* Parameters for GET to /api/v1/admin/domain_permission_drafts.
|
||||||
*/
|
*/
|
||||||
export interface ModeratorDomainPermissionDraftSearchParams {
|
export interface DomainPermDraftSearchParams {
|
||||||
/**
|
/**
|
||||||
* Show only drafts created by the given subscription ID.
|
* Show only drafts created by the given subscription ID.
|
||||||
*/
|
*/
|
||||||
|
@ -136,7 +138,7 @@ export interface ModeratorDomainPermissionDraftSearchParams {
|
||||||
/**
|
/**
|
||||||
* Parameters for POST to /api/v1/admin/domain_permission_drafts/{id}/accept.
|
* Parameters for POST to /api/v1/admin/domain_permission_drafts/{id}/accept.
|
||||||
*/
|
*/
|
||||||
export interface ModeratorDomainPermissionDraftAcceptParams {
|
export interface DomainPermDraftAcceptParams {
|
||||||
/**
|
/**
|
||||||
* ID of the domain permission draft.
|
* ID of the domain permission draft.
|
||||||
*/
|
*/
|
||||||
|
@ -151,7 +153,7 @@ export interface ModeratorDomainPermissionDraftAcceptParams {
|
||||||
/**
|
/**
|
||||||
* Parameters for POST to /api/v1/admin/domain_permission_drafts/{id}/accept.
|
* Parameters for POST to /api/v1/admin/domain_permission_drafts/{id}/accept.
|
||||||
*/
|
*/
|
||||||
export interface ModeratorDomainPermissionDraftRejectParams {
|
export interface DomainPermDraftRejectParams {
|
||||||
/**
|
/**
|
||||||
* ID of the domain permission draft.
|
* ID of the domain permission draft.
|
||||||
*/
|
*/
|
||||||
|
@ -163,7 +165,34 @@ export interface ModeratorDomainPermissionDraftRejectParams {
|
||||||
ignore_target?: boolean;
|
ignore_target?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModeratorDomainPermissionDraftSearchResp {
|
export interface DomainPermDraftSearchResp {
|
||||||
drafts: DomainPerm[];
|
drafts: DomainPerm[];
|
||||||
links: Links | null;
|
links: Links | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DomainPermDraftCreateParams {
|
||||||
|
/**
|
||||||
|
* Domain to create the permission draft for.
|
||||||
|
*/
|
||||||
|
domain: string;
|
||||||
|
/**
|
||||||
|
* Create a draft "allow" or a draft "block".
|
||||||
|
*/
|
||||||
|
permission_type: PermType;
|
||||||
|
/**
|
||||||
|
* Obfuscate the name of the domain when serving it publicly.
|
||||||
|
* Eg., `example.org` becomes something like `ex***e.org`.
|
||||||
|
*/
|
||||||
|
obfuscate?: boolean;
|
||||||
|
/**
|
||||||
|
* Public comment about this domain permission. This will be displayed
|
||||||
|
* alongside the domain permission if you choose to share permissions.
|
||||||
|
*/
|
||||||
|
public_comment?: string;
|
||||||
|
/**
|
||||||
|
* Private comment about this domain permission.
|
||||||
|
* Will only be shown to other admins, so this is a useful way of
|
||||||
|
* internally keeping track of why a certain domain ended up permissioned.
|
||||||
|
*/
|
||||||
|
private_comment?: string;
|
||||||
|
}
|
||||||
|
|
48
web/source/settings/lib/util/formvalidators.ts
Normal file
48
web/source/settings/lib/util/formvalidators.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import isValidDomain from "is-valid-domain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the "domain" field of a form.
|
||||||
|
* @param domain
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function formDomainValidator(domain: string): string {
|
||||||
|
if (domain.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain[domain.length-1] === ".") {
|
||||||
|
return "invalid domain";
|
||||||
|
}
|
||||||
|
|
||||||
|
const valid = isValidDomain(domain, {
|
||||||
|
subdomain: true,
|
||||||
|
wildcard: false,
|
||||||
|
allowUnicode: true,
|
||||||
|
topLevel: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "invalid domain";
|
||||||
|
}
|
|
@ -1359,6 +1359,50 @@ button.tab-button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.domain-permission-drafts-view {
|
||||||
|
.domain-permission-draft {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
&.block {
|
||||||
|
border-left: 0.3rem solid $error3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.allow {
|
||||||
|
border-left: 0.3rem solid $green1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $fg-accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-list {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.info-list-entry {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-permission-draft-details {
|
||||||
|
.info-list {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-permission-drafts-view,
|
||||||
|
.domain-permission-draft-details {
|
||||||
|
dd.permission-type {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.35rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.instance-rules {
|
.instance-rules {
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -27,7 +27,7 @@ import MutationButton from "../../../../components/form/mutation-button";
|
||||||
import { useLocation, useSearch } from "wouter";
|
import { useLocation, useSearch } from "wouter";
|
||||||
import { AdminAccount } from "../../../../lib/types/account";
|
import { AdminAccount } from "../../../../lib/types/account";
|
||||||
import Username from "../../../../components/username";
|
import Username from "../../../../components/username";
|
||||||
import isValidDomain from "is-valid-domain";
|
import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||||
|
|
||||||
export function AccountSearchForm() {
|
export function AccountSearchForm() {
|
||||||
const [ location, setLocation ] = useLocation();
|
const [ location, setLocation ] = useLocation();
|
||||||
|
@ -45,28 +45,7 @@ export function AccountSearchForm() {
|
||||||
display_name: useTextInput("display_name", { defaultValue: urlQueryParams.get("display_name") ?? ""}),
|
display_name: useTextInput("display_name", { defaultValue: urlQueryParams.get("display_name") ?? ""}),
|
||||||
by_domain: useTextInput("by_domain", {
|
by_domain: useTextInput("by_domain", {
|
||||||
defaultValue: urlQueryParams.get("by_domain") ?? "",
|
defaultValue: urlQueryParams.get("by_domain") ?? "",
|
||||||
validator: (v: string) => {
|
validator: formDomainValidator,
|
||||||
if (v.length === 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v[v.length-1] === ".") {
|
|
||||||
return "invalid domain";
|
|
||||||
}
|
|
||||||
|
|
||||||
const valid = isValidDomain(v, {
|
|
||||||
subdomain: true,
|
|
||||||
wildcard: false,
|
|
||||||
allowUnicode: true,
|
|
||||||
topLevel: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "invalid domain";
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
email: useTextInput("email", { defaultValue: urlQueryParams.get("email") ?? ""}),
|
email: useTextInput("email", { defaultValue: urlQueryParams.get("email") ?? ""}),
|
||||||
ip: useTextInput("ip", { defaultValue: urlQueryParams.get("ip") ?? ""}),
|
ip: useTextInput("ip", { defaultValue: urlQueryParams.get("ip") ?? ""}),
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useEffect, useMemo } from "react";
|
||||||
|
import { useParams } from "wouter";
|
||||||
|
import Loading from "../../../../components/loading";
|
||||||
|
import { useBaseUrl } from "../../../../lib/navigation/util";
|
||||||
|
import BackButton from "../../../../components/back-button";
|
||||||
|
import { useGetDomainPermissionDraftQuery } from "../../../../lib/query/admin/domain-permissions/drafts";
|
||||||
|
import { Error as ErrorC } from "../../../../components/error";
|
||||||
|
import Username from "../../../../components/username";
|
||||||
|
import { useLazyGetAccountQuery } from "../../../../lib/query/admin";
|
||||||
|
|
||||||
|
export default function DomainPermissionDraftDetail() {
|
||||||
|
const baseUrl = useBaseUrl();
|
||||||
|
const backLocation: String = history.state?.backLocation ?? `~${baseUrl}`;
|
||||||
|
const params = useParams();
|
||||||
|
|
||||||
|
let draftID = params.permDraftId as string | undefined;
|
||||||
|
if (!draftID) {
|
||||||
|
throw "no perm ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: permDraft,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useGetDomainPermissionDraftQuery(draftID);
|
||||||
|
|
||||||
|
// Once we've triggered loading the perm draft,
|
||||||
|
// trigger getting the account that created it.
|
||||||
|
const [ getAccount, getAccountRes ] = useLazyGetAccountQuery();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!permDraft) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permDraft.created_by) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccount(permDraft.created_by, true);
|
||||||
|
}, [getAccount, permDraft]);
|
||||||
|
|
||||||
|
// Load the createdByAccount if possible,
|
||||||
|
// returning a username lozenge with
|
||||||
|
// a link to the account.
|
||||||
|
const createdByAccount = useMemo(() => {
|
||||||
|
const {
|
||||||
|
data: account,
|
||||||
|
isLoading: isLoadingAccount,
|
||||||
|
isFetching: isFetchingAccount,
|
||||||
|
isError: isErrorAccount,
|
||||||
|
} = getAccountRes;
|
||||||
|
|
||||||
|
// Wait for query to finish, returning
|
||||||
|
// loading spinner in the meantime.
|
||||||
|
if (isLoadingAccount || isFetchingAccount || !permDraft) {
|
||||||
|
return <Loading />;
|
||||||
|
} else if (isErrorAccount || account === undefined) {
|
||||||
|
// Fall back to account ID.
|
||||||
|
return permDraft?.created_by;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Username
|
||||||
|
account={account}
|
||||||
|
linkTo={`~/settings/moderation/accounts/${account.id}`}
|
||||||
|
backLocation={`~${location}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [getAccountRes, permDraft]);
|
||||||
|
|
||||||
|
if (isLoading || isFetching) {
|
||||||
|
return <Loading />;
|
||||||
|
} else if (isError) {
|
||||||
|
return <ErrorC error={error} />;
|
||||||
|
} else if (permDraft === undefined) {
|
||||||
|
return <ErrorC error={new Error("permission draft was undefined")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const created = permDraft.created_at ? new Date(permDraft.created_at).toDateString(): "unknown";
|
||||||
|
const domain = permDraft.domain;
|
||||||
|
const permType = permDraft.permission_type;
|
||||||
|
if (!permType) {
|
||||||
|
return <ErrorC error={new Error("permission_type was undefined")} />;
|
||||||
|
}
|
||||||
|
const publicComment = permDraft.public_comment ?? "[none]";
|
||||||
|
const privateComment = permDraft.private_comment ?? "[none]";
|
||||||
|
const subscriptionID = permDraft.subscription_id ?? "[none]";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="domain-permission-draft-details">
|
||||||
|
<h1><BackButton to={backLocation} /> Domain Permission Draft Detail</h1>
|
||||||
|
<dl className="info-list">
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Created</dt>
|
||||||
|
<dd><time dateTime={permDraft.created_at}>{created}</time></dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Created By</dt>
|
||||||
|
<dd>{createdByAccount}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Domain</dt>
|
||||||
|
<dd>{domain}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Permission type</dt>
|
||||||
|
<dd className={`permission-type ${permType}`}>
|
||||||
|
<i
|
||||||
|
aria-hidden={true}
|
||||||
|
className={`fa fa-${permType === "allow" ? "check" : "close"}`}
|
||||||
|
></i>
|
||||||
|
{permType}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Private comment</dt>
|
||||||
|
<dd>{privateComment}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Public comment</dt>
|
||||||
|
<dd>{publicComment}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Subscription ID</dt>
|
||||||
|
<dd>{subscriptionID}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -25,6 +25,9 @@ import MutationButton from "../../../../components/form/mutation-button";
|
||||||
import { useLocation, useSearch } from "wouter";
|
import { useLocation, useSearch } from "wouter";
|
||||||
import { useLazySearchDomainPermissionDraftsQuery } from "../../../../lib/query/admin/domain-permissions/drafts";
|
import { useLazySearchDomainPermissionDraftsQuery } from "../../../../lib/query/admin/domain-permissions/drafts";
|
||||||
import { DomainPerm } from "../../../../lib/types/domain-permission";
|
import { DomainPerm } from "../../../../lib/types/domain-permission";
|
||||||
|
import { Error as ErrorC } from "../../../../components/error";
|
||||||
|
import { Select, TextInput } from "../../../../components/form/inputs";
|
||||||
|
import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||||
|
|
||||||
export default function DomainPermissionDraftsSearch() {
|
export default function DomainPermissionDraftsSearch() {
|
||||||
return (
|
return (
|
||||||
|
@ -53,7 +56,10 @@ function DomainPermissionDraftsSearchForm() {
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
subscription_id: useTextInput("subscription_id", { defaultValue: urlQueryParams.get("subscription_id") ?? "" }),
|
subscription_id: useTextInput("subscription_id", { defaultValue: urlQueryParams.get("subscription_id") ?? "" }),
|
||||||
domain: useTextInput("domain", { defaultValue: urlQueryParams.get("domain") ?? "" }),
|
domain: useTextInput("domain", {
|
||||||
|
defaultValue: urlQueryParams.get("domain") ?? "",
|
||||||
|
validator: formDomainValidator,
|
||||||
|
}),
|
||||||
permission_type: useTextInput("permission_type", { defaultValue: urlQueryParams.get("permission_type") ?? "" }),
|
permission_type: useTextInput("permission_type", { defaultValue: urlQueryParams.get("permission_type") ?? "" }),
|
||||||
limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "20" })
|
limit: useTextInput("limit", { defaultValue: urlQueryParams.get("limit") ?? "20" })
|
||||||
};
|
};
|
||||||
|
@ -111,8 +117,8 @@ function DomainPermissionDraftsSearchForm() {
|
||||||
return (
|
return (
|
||||||
<DraftListEntry
|
<DraftListEntry
|
||||||
key={draft.id}
|
key={draft.id}
|
||||||
draft={draft}
|
permDraft={draft}
|
||||||
linkTo={`/${draft.id}`}
|
linkTo={`/drafts/${draft.id}`}
|
||||||
backLocation={backLocation}
|
backLocation={backLocation}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -126,17 +132,35 @@ function DomainPermissionDraftsSearchForm() {
|
||||||
// trying to fill in fields.
|
// trying to fill in fields.
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
>
|
>
|
||||||
{/* <Select
|
<Select
|
||||||
field={form.resolved}
|
field={form.permission_type}
|
||||||
label="Report status"
|
label="Permission type"
|
||||||
options={
|
options={
|
||||||
<>
|
<>
|
||||||
<option value="false">Unresolved only</option>
|
|
||||||
<option value="true">Resolved only</option>
|
|
||||||
<option value="">Any</option>
|
<option value="">Any</option>
|
||||||
|
<option value="block">Block</option>
|
||||||
|
<option value="allow">Allow</option>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
></Select> */}
|
></Select>
|
||||||
|
<TextInput
|
||||||
|
field={form.domain}
|
||||||
|
label={`Domain (without "https://" prefix)`}
|
||||||
|
placeholder="example.org"
|
||||||
|
autoCapitalize="none"
|
||||||
|
spellCheck="false"
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
field={form.limit}
|
||||||
|
label="Items per page"
|
||||||
|
options={
|
||||||
|
<>
|
||||||
|
<option value="20">20</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100">100</option>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
></Select>
|
||||||
<MutationButton
|
<MutationButton
|
||||||
disabled={false}
|
disabled={false}
|
||||||
label={"Search"}
|
label={"Search"}
|
||||||
|
@ -159,17 +183,25 @@ function DomainPermissionDraftsSearchForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DraftEntryProps {
|
interface DraftEntryProps {
|
||||||
draft: DomainPerm;
|
permDraft: DomainPerm;
|
||||||
linkTo: string;
|
linkTo: string;
|
||||||
backLocation: string;
|
backLocation: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DraftListEntry({ draft, linkTo, backLocation }: DraftEntryProps) {
|
function DraftListEntry({ permDraft, linkTo, backLocation }: DraftEntryProps) {
|
||||||
const [ _location, setLocation ] = useLocation();
|
const [ _location, setLocation ] = useLocation();
|
||||||
|
|
||||||
|
const domain = permDraft.domain;
|
||||||
|
const permType = permDraft.permission_type;
|
||||||
|
if (!permType) {
|
||||||
|
return <ErrorC error={new Error("permission_type was undefined")} />;
|
||||||
|
}
|
||||||
|
const privateComment = permDraft.private_comment ?? "[none]";
|
||||||
|
const subscriptionID = permDraft.subscription_id ?? "[none]";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`pseudolink domain-permission-draft`}
|
className={`pseudolink domain-permission-draft entry ${permType}`}
|
||||||
// aria-label={title}
|
// aria-label={title}
|
||||||
// title={title}
|
// title={title}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -186,7 +218,28 @@ function DraftListEntry({ draft, linkTo, backLocation }: DraftEntryProps) {
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<dl className="info-list">
|
<dl className="info-list">
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Domain:</dt>
|
||||||
|
<dd className="text-cutoff">{domain}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Permission type:</dt>
|
||||||
|
<dd className={`permission-type ${permType}`}>
|
||||||
|
<i
|
||||||
|
aria-hidden={true}
|
||||||
|
className={`fa fa-${permType === "allow" ? "check" : "close"}`}
|
||||||
|
></i>
|
||||||
|
{permType}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Private comment:</dt>
|
||||||
|
<dd className="text-cutoff">{privateComment}</dd>
|
||||||
|
</div>
|
||||||
|
<div className="info-list-entry">
|
||||||
|
<dt>Subscription:</dt>
|
||||||
|
<dd className="text-cutoff">{subscriptionID}</dd>
|
||||||
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,7 +18,91 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import useFormSubmit from "../../../../lib/form/submit";
|
||||||
|
import { useCreateDomainPermissionDraftMutation } from "../../../../lib/query/admin/domain-permissions/drafts";
|
||||||
|
import { useBoolInput, useRadioInput, useTextInput } from "../../../../lib/form";
|
||||||
|
import { formDomainValidator } from "../../../../lib/util/formvalidators";
|
||||||
|
import MutationButton from "../../../../components/form/mutation-button";
|
||||||
|
import { Checkbox, RadioGroup, TextArea, TextInput } from "../../../../components/form/inputs";
|
||||||
|
|
||||||
export default function DomainPermissionDraftNew() {
|
export default function DomainPermissionDraftNew() {
|
||||||
return <></>;
|
const form = {
|
||||||
|
domain: useTextInput("domain", {
|
||||||
|
validator: formDomainValidator,
|
||||||
|
}),
|
||||||
|
permission_type: useRadioInput("permission_type", {
|
||||||
|
options: {
|
||||||
|
block: "Block domain",
|
||||||
|
allow: "Allow domain",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
obfuscate: useBoolInput("obfuscate"),
|
||||||
|
public_comment: useTextInput("public_comment"),
|
||||||
|
private_comment: useTextInput("private_comment"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const [formSubmit, result] = useFormSubmit(form, useCreateDomainPermissionDraftMutation());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={formSubmit}
|
||||||
|
// Prevent password managers
|
||||||
|
// trying to fill in fields.
|
||||||
|
autoComplete="off"
|
||||||
|
>
|
||||||
|
<div className="form-section-docs">
|
||||||
|
<h2>New Domain Permission Draft</h2>
|
||||||
|
<p>
|
||||||
|
You can use the form below to create a new domain permission draft.
|
||||||
|
<br/>
|
||||||
|
Domain permission drafts are domain block or domain allow entries that are not yet in force.
|
||||||
|
<br/>
|
||||||
|
You can choose to accept or remove a draft.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
field={form.permission_type}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
field={form.domain}
|
||||||
|
label={`Domain (without "https://" prefix)`}
|
||||||
|
placeholder="example.org"
|
||||||
|
autoCapitalize="none"
|
||||||
|
spellCheck="false"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextArea
|
||||||
|
field={form.private_comment}
|
||||||
|
label={"Private comment"}
|
||||||
|
placeholder="This domain is like unto a clown car full of clowns, I suggest we block it forthwith."
|
||||||
|
autoCapitalize="sentences"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextArea
|
||||||
|
field={form.public_comment}
|
||||||
|
label={"Public comment"}
|
||||||
|
placeholder="Bad posters"
|
||||||
|
autoCapitalize="sentences"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
field={form.obfuscate}
|
||||||
|
label="Obfuscate domain in public lists"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MutationButton
|
||||||
|
label="Save"
|
||||||
|
result={result}
|
||||||
|
disabled={
|
||||||
|
!form.domain.value ||
|
||||||
|
!form.domain.valid ||
|
||||||
|
!form.permission_type.value
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import AccountsPending from "./accounts/pending";
|
||||||
import AccountDetail from "./accounts/detail";
|
import AccountDetail from "./accounts/detail";
|
||||||
import DomainPermissionDraftsSearch from "./domain-permissions/drafts";
|
import DomainPermissionDraftsSearch from "./domain-permissions/drafts";
|
||||||
import DomainPermissionDraftNew from "./domain-permissions/drafts/new";
|
import DomainPermissionDraftNew from "./domain-permissions/drafts/new";
|
||||||
|
import DomainPermissionDraftDetail from "./domain-permissions/drafts/detail";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
EXPORTED COMPONENTS
|
EXPORTED COMPONENTS
|
||||||
|
@ -143,6 +144,7 @@ function ModerationDomainPermsRouter() {
|
||||||
<Route path="/process" component={ImportExport} />
|
<Route path="/process" component={ImportExport} />
|
||||||
<Route path="/drafts/search" component={DomainPermissionDraftsSearch} />
|
<Route path="/drafts/search" component={DomainPermissionDraftsSearch} />
|
||||||
<Route path="/drafts/new" component={DomainPermissionDraftNew} />
|
<Route path="/drafts/new" component={DomainPermissionDraftNew} />
|
||||||
|
<Route path="/drafts/:permDraftId" component={DomainPermissionDraftDetail} />
|
||||||
<Route path="/:permType" component={DomainPermissionsOverview} />
|
<Route path="/:permType" component={DomainPermissionsOverview} />
|
||||||
<Route path="/:permType/:domain" component={DomainPermDetail} />
|
<Route path="/:permType/:domain" component={DomainPermDetail} />
|
||||||
<Route><Redirect to="/blocks"/></Route>
|
<Route><Redirect to="/blocks"/></Route>
|
||||||
|
|
|
@ -3213,9 +3213,9 @@ electron-to-chromium@^1.4.668:
|
||||||
integrity sha512-jeWaIta2rIG2FzHaYIhSuVWqC6KJYo7oSBX4Jv7g+aVujKztfvdpf+n6MGwZdC5hQXbax4nntykLH2juIQrfPg==
|
integrity sha512-jeWaIta2rIG2FzHaYIhSuVWqC6KJYo7oSBX4Jv7g+aVujKztfvdpf+n6MGwZdC5hQXbax4nntykLH2juIQrfPg==
|
||||||
|
|
||||||
elliptic@^6.5.3, elliptic@^6.5.4:
|
elliptic@^6.5.3, elliptic@^6.5.4:
|
||||||
version "6.5.7"
|
version "6.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
|
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210"
|
||||||
integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
|
integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==
|
||||||
dependencies:
|
dependencies:
|
||||||
bn.js "^4.11.9"
|
bn.js "^4.11.9"
|
||||||
brorand "^1.1.0"
|
brorand "^1.1.0"
|
||||||
|
|
|
@ -172,7 +172,7 @@ Polls can have up to
|
||||||
<p>
|
<p>
|
||||||
ActivityPub instances federate with other instances by exchanging data with them over the network.
|
ActivityPub instances federate with other instances by exchanging data with them over the network.
|
||||||
Exchanged data includes things like accounts, statuses, likes, boosts, and media attachments.
|
Exchanged data includes things like accounts, statuses, likes, boosts, and media attachments.
|
||||||
This exchange of data can prevented for instances on specific domains via a domain block created
|
This exchange of data can be prevented for instances on specific domains via a domain block created
|
||||||
by an instance admin. When an instance is domain blocked by another instance:
|
by an instance admin. When an instance is domain blocked by another instance:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -190,4 +190,4 @@ Polls can have up to
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
Loading…
Reference in a new issue