mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-18 07:36:00 +01:00
Introduce new permission for group member labels
Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
@@ -390,6 +390,7 @@ export type ConversationType = ReadonlyDeep<
|
||||
accessControlAddFromInviteLink?: number;
|
||||
accessControlAttributes?: number;
|
||||
accessControlMembers?: number;
|
||||
accessControlMemberLabel?: number;
|
||||
announcementsOnly?: boolean;
|
||||
announcementsOnlyReady?: boolean;
|
||||
expireTimer?: DurationInSeconds;
|
||||
@@ -1253,6 +1254,7 @@ export const actions = {
|
||||
setAccessControlAddFromInviteLinkSetting,
|
||||
setAccessControlAttributesSetting,
|
||||
setAccessControlMembersSetting,
|
||||
setAccessControlMemberLabelSetting,
|
||||
setAnnouncementsOnly,
|
||||
setCenterMessage,
|
||||
setComposeGroupAvatar,
|
||||
@@ -1731,6 +1733,30 @@ function setAccessControlMembersSetting(
|
||||
};
|
||||
}
|
||||
|
||||
function setAccessControlMemberLabelSetting(
|
||||
conversationId: string,
|
||||
value: number
|
||||
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
|
||||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error(
|
||||
'setAccessControlMemberLabelSetting: No conversation found'
|
||||
);
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
name: 'updateAccessControlMemberLabel',
|
||||
idForLogging: conversation.idForLogging(),
|
||||
task: async () => conversation.updateAccessControlMemberLabel(value),
|
||||
});
|
||||
dispatch({
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function setAccessControlAttributesSetting(
|
||||
conversationId: string,
|
||||
value: number
|
||||
|
||||
@@ -242,6 +242,7 @@ export const ConversationPanel = memo(function ConversationPanel({
|
||||
<PanelContainer
|
||||
key={getPanelKey(prevPanel)}
|
||||
conversationId={conversationId}
|
||||
isActive={false}
|
||||
panel={prevPanel}
|
||||
ref={animateRef}
|
||||
/>
|
||||
@@ -257,6 +258,7 @@ export const ConversationPanel = memo(function ConversationPanel({
|
||||
lastPanelDoneAnimating !== prevPanel &&
|
||||
prevPanel && (
|
||||
<PanelContainer
|
||||
isActive={false}
|
||||
conversationId={conversationId}
|
||||
panel={prevPanel}
|
||||
key={getPanelKey(prevPanel)}
|
||||
@@ -283,85 +285,94 @@ export const ConversationPanel = memo(function ConversationPanel({
|
||||
|
||||
type PanelPropsType = {
|
||||
conversationId: string;
|
||||
isActive: boolean;
|
||||
panel: PanelArgsType;
|
||||
};
|
||||
|
||||
const PanelContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
PanelPropsType & { isActive?: boolean }
|
||||
>(function PanelContainerInner(
|
||||
{ conversationId, isActive, panel },
|
||||
ref
|
||||
): React.JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const { popPanelForConversation } = useNavActions();
|
||||
const conversationTitle = getConversationTitleForPanelType(i18n, panel.type);
|
||||
const PanelContainer = forwardRef<HTMLDivElement, PanelPropsType>(
|
||||
function PanelContainerInner(
|
||||
{ conversationId, isActive, panel },
|
||||
ref
|
||||
): React.JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const { popPanelForConversation } = useNavActions();
|
||||
const conversationTitle = getConversationTitleForPanelType(
|
||||
i18n,
|
||||
panel.type
|
||||
);
|
||||
|
||||
let info: React.JSX.Element | undefined;
|
||||
if (panel.type === PanelType.AllMedia) {
|
||||
info = <SmartAllMediaHeader />;
|
||||
} else if (conversationTitle != null) {
|
||||
info = (
|
||||
<div className="ConversationPanel__header__info">
|
||||
<div className="ConversationPanel__header__info__title">
|
||||
{conversationTitle}
|
||||
let info: React.JSX.Element | undefined;
|
||||
if (panel.type === PanelType.AllMedia) {
|
||||
info = <SmartAllMediaHeader />;
|
||||
} else if (conversationTitle != null) {
|
||||
info = (
|
||||
<div className="ConversationPanel__header__info">
|
||||
<div className="ConversationPanel__header__info__title">
|
||||
{conversationTitle}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const focusRef = useRef<HTMLDivElement | null>(null);
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (panel.type === PanelType.GroupMemberLabelEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusNode = focusRef.current;
|
||||
if (!focusNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elements =
|
||||
focusNode.querySelectorAll<HTMLElement>(focusableSelector);
|
||||
if (!elements.length) {
|
||||
return;
|
||||
}
|
||||
elements[0]?.focus();
|
||||
}, [isActive, panel]);
|
||||
|
||||
return (
|
||||
<div className="ConversationPanel" ref={ref}>
|
||||
<div className="ConversationPanel__header">
|
||||
<button
|
||||
aria-label={i18n('icu:goBack')}
|
||||
className="ConversationPanel__header__back-button"
|
||||
onClick={popPanelForConversation}
|
||||
type="button"
|
||||
/>
|
||||
{info}
|
||||
</div>
|
||||
<SmartMiniPlayer shouldFlow />
|
||||
<div
|
||||
className={classNames(
|
||||
'ConversationPanel__body',
|
||||
panel.type !== PanelType.PinnedMessages &&
|
||||
panel.type !== PanelType.AllMedia &&
|
||||
panel.type !== PanelType.GroupMemberLabelEditor &&
|
||||
'ConversationPanel__body--padding'
|
||||
)}
|
||||
ref={focusRef}
|
||||
>
|
||||
<PanelElement
|
||||
isActive={isActive}
|
||||
conversationId={conversationId}
|
||||
panel={panel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const focusRef = useRef<HTMLDivElement | null>(null);
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (panel.type === PanelType.GroupMemberLabelEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusNode = focusRef.current;
|
||||
if (!focusNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = focusNode.querySelectorAll<HTMLElement>(focusableSelector);
|
||||
if (!elements.length) {
|
||||
return;
|
||||
}
|
||||
elements[0]?.focus();
|
||||
}, [isActive, panel]);
|
||||
|
||||
return (
|
||||
<div className="ConversationPanel" ref={ref}>
|
||||
<div className="ConversationPanel__header">
|
||||
<button
|
||||
aria-label={i18n('icu:goBack')}
|
||||
className="ConversationPanel__header__back-button"
|
||||
onClick={popPanelForConversation}
|
||||
type="button"
|
||||
/>
|
||||
{info}
|
||||
</div>
|
||||
<SmartMiniPlayer shouldFlow />
|
||||
<div
|
||||
className={classNames(
|
||||
'ConversationPanel__body',
|
||||
panel.type !== PanelType.PinnedMessages &&
|
||||
panel.type !== PanelType.AllMedia &&
|
||||
panel.type !== PanelType.GroupMemberLabelEditor &&
|
||||
'ConversationPanel__body--padding'
|
||||
)}
|
||||
ref={focusRef}
|
||||
>
|
||||
<PanelElement conversationId={conversationId} panel={panel} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
);
|
||||
|
||||
function PanelElement({
|
||||
conversationId,
|
||||
isActive,
|
||||
panel,
|
||||
}: PanelPropsType): React.JSX.Element | null {
|
||||
if (panel.type === PanelType.AllMedia) {
|
||||
@@ -396,7 +407,12 @@ function PanelElement({
|
||||
}
|
||||
|
||||
if (panel.type === PanelType.GroupMemberLabelEditor) {
|
||||
return <SmartGroupMemberLabelEditor conversationId={conversationId} />;
|
||||
return (
|
||||
<SmartGroupMemberLabelEditor
|
||||
conversationId={conversationId}
|
||||
isActive={isActive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (panel.type === PanelType.GroupPermissions) {
|
||||
|
||||
@@ -15,16 +15,19 @@ import { createLogger } from '../../logging/log.std.js';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges.preload.js';
|
||||
import { isNotNil } from '../../util/isNotNil.std.js';
|
||||
import { useNavActions } from '../ducks/nav.std.js';
|
||||
import { getCanAddLabel } from '../../types/GroupMemberLabels.std.js';
|
||||
|
||||
const log = createLogger('SmartGroupMemberLabelEditor');
|
||||
|
||||
export type SmartGroupMemberLabelEditorProps = Readonly<{
|
||||
conversationId: string;
|
||||
isActive: boolean;
|
||||
}>;
|
||||
|
||||
export const SmartGroupMemberLabelEditor = memo(
|
||||
function SmartGroupMemberLabelEditor({
|
||||
conversationId,
|
||||
isActive,
|
||||
}: SmartGroupMemberLabelEditorProps) {
|
||||
const i18n = useSelector(getIntl);
|
||||
const theme = useSelector(getTheme);
|
||||
@@ -47,13 +50,9 @@ export const SmartGroupMemberLabelEditor = memo(
|
||||
const ourMembership = conversation.memberships?.find(
|
||||
membership => membership?.aci === ourAci
|
||||
);
|
||||
if (!ourMembership) {
|
||||
log.warn('User was not found in group, leaving this pane!');
|
||||
popPanelForConversation();
|
||||
return null;
|
||||
}
|
||||
const { labelEmoji: existingLabelEmoji, labelString: existingLabelString } =
|
||||
ourMembership;
|
||||
ourMembership || {};
|
||||
const canAddLabel = getCanAddLabel(conversation, ourMembership);
|
||||
|
||||
const membersWithLabel = (conversation.memberships || [])
|
||||
.map(membership => {
|
||||
@@ -94,11 +93,13 @@ export const SmartGroupMemberLabelEditor = memo(
|
||||
|
||||
return (
|
||||
<GroupMemberLabelEditor
|
||||
canAddLabel={canAddLabel}
|
||||
existingLabelEmoji={existingLabelEmoji}
|
||||
existingLabelString={existingLabelString}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
group={conversation}
|
||||
i18n={i18n}
|
||||
isActive={isActive}
|
||||
me={me}
|
||||
membersWithLabel={membersWithLabel}
|
||||
ourColor={ourColor}
|
||||
|
||||
@@ -21,6 +21,7 @@ export const SmartGroupV2Permissions = memo(function SmartGroupV2Permissions({
|
||||
const {
|
||||
setAccessControlAttributesSetting,
|
||||
setAccessControlMembersSetting,
|
||||
setAccessControlMemberLabelSetting,
|
||||
setAnnouncementsOnly,
|
||||
} = useConversationsActions();
|
||||
return (
|
||||
@@ -29,6 +30,7 @@ export const SmartGroupV2Permissions = memo(function SmartGroupV2Permissions({
|
||||
conversation={conversation}
|
||||
setAccessControlAttributesSetting={setAccessControlAttributesSetting}
|
||||
setAccessControlMembersSetting={setAccessControlMembersSetting}
|
||||
setAccessControlMemberLabelSetting={setAccessControlMemberLabelSetting}
|
||||
setAnnouncementsOnly={setAnnouncementsOnly}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user