Skip to content

Commit f80d8bc

Browse files
FalkWolskyraheeliftikhar5
FalkWolsky
authored andcommitted
Visual Control for concurrent App Edit
1 parent c40a038 commit f80d8bc

File tree

1 file changed

+47
-11
lines changed
  • client/packages/lowcoder/src/pages/common

1 file changed

+47
-11
lines changed

client/packages/lowcoder/src/pages/common/header.tsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ import { getBrandingConfig } from "../../redux/selectors/configSelectors";
5454
import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
5555
import { EditorContext } from "../../comps/editorState";
5656
import Tooltip from "antd/es/tooltip";
57+
import { LockOutlined } from '@ant-design/icons';
58+
import Avatar from 'antd/es/avatar';
59+
5760

5861
const StyledLink = styled.a`
5962
display: flex;
@@ -234,12 +237,6 @@ const DropdownStyled = styled(Dropdown)`
234237
}
235238
`;
236239

237-
const DropdownMenuStyled = styled(DropdownMenu)`
238-
.ant-dropdown-menu-item:hover {
239-
background: #edf4fa;
240-
}
241-
`;
242-
243240
const Wrapper = styled.div`
244241
.taco-edit-text-wrapper {
245242
width: fit-content;
@@ -262,6 +259,16 @@ const Prefix = styled.div`
262259
}
263260
`;
264261

262+
// Add the lock icon logic for disabled options
263+
const DropdownMenuStyled = styled(DropdownMenu)`
264+
.ant-dropdown-menu-item:hover {
265+
background: ${(props) =>
266+
props.disabled ? 'inherit' : '#edf4fa'};
267+
cursor: ${(props) =>
268+
props.disabled ? 'not-allowed' : 'pointer'};
269+
}
270+
`;
271+
265272
function HeaderProfile(props: { user: User }) {
266273
const { user } = props;
267274
const fetchingUser = useSelector(isFetchingUser);
@@ -318,6 +325,10 @@ export default function Header(props: HeaderProps) {
318325

319326
const isModule = appType === AppTypeEnum.Module;
320327

328+
// Raheel: Todo - get concurrent editing state by API
329+
// maybe via editorState.getConcurrentAppEditingState(); as a new function?
330+
const [concurrentAppEditingState, setConcurrentAppEditingState] = useState(true);
331+
321332
const editorModeOptions = [
322333
{
323334
label: trans("header.editorMode_layout"),
@@ -458,6 +469,16 @@ export default function Header(props: HeaderProps) {
458469
<HeaderProfile user={user} />
459470
) : (
460471
<>
472+
{/* Display a hint about who is editing the app */}
473+
{concurrentAppEditingState && (
474+
<div style={{ display: 'flex', alignItems: 'center', marginRight: '8px' }}>
475+
<Avatar size="small" src={user.avatarUrl} />
476+
<span style={{ marginLeft: '8px', fontSize: '12px', color: '#b8b9bf' }}>
477+
{`${user.username} is currently editing this app.`}
478+
</span>
479+
</div>
480+
)}
481+
461482
{applicationId && (
462483
<AppPermissionDialog
463484
applicationId={applicationId}
@@ -472,10 +493,11 @@ export default function Header(props: HeaderProps) {
472493
{SHARE_TITLE}
473494
</GrayBtn>
474495
)}
496+
475497
<PreviewBtn buttonType="primary" onClick={() => preview(applicationId)}>
476498
{trans("header.preview")}
477499
</PreviewBtn>
478-
500+
479501
<Dropdown
480502
className="cypress-header-dropdown"
481503
placement="bottomRight"
@@ -484,6 +506,7 @@ export default function Header(props: HeaderProps) {
484506
<DropdownMenuStyled
485507
style={{ minWidth: "110px", borderRadius: "4px" }}
486508
onClick={(e) => {
509+
if (concurrentAppEditingState) return; // Prevent clicks if the app is being edited by someone else
487510
if (e.key === "deploy") {
488511
dispatch(publishApplication({ applicationId }));
489512
} else if (e.key === "snapshot") {
@@ -494,24 +517,36 @@ export default function Header(props: HeaderProps) {
494517
{
495518
key: "deploy",
496519
label: (
497-
<CommonTextLabel>{trans("header.deploy")}</CommonTextLabel>
520+
<div style={{ display: 'flex', alignItems: 'center' }}>
521+
{concurrentAppEditingState && <LockOutlined style={{ marginRight: '8px' }} />}
522+
<CommonTextLabel style= {{color: concurrentAppEditingState ? "#ccc" : "#222"}}>
523+
{trans("header.deploy")}
524+
</CommonTextLabel>
525+
</div>
498526
),
527+
disabled: concurrentAppEditingState,
499528
},
500529
{
501530
key: "snapshot",
502531
label: (
503-
<CommonTextLabel>{trans("header.snapshot")}</CommonTextLabel>
532+
<div style={{ display: 'flex', alignItems: 'center' }}>
533+
{concurrentAppEditingState && <LockOutlined style={{ marginRight: '8px' }} />}
534+
<CommonTextLabel style= {{color: concurrentAppEditingState ? "#ccc" : "#222"}}>
535+
{trans("header.snapshot")}
536+
</CommonTextLabel>
537+
</div>
504538
),
539+
disabled: concurrentAppEditingState,
505540
},
506541
]}
507542
/>
508543
)}
509544
>
510-
<PackUpBtn buttonType="primary">
545+
<PackUpBtn buttonType="primary" disabled={concurrentAppEditingState}>
511546
<PackUpIcon />
512547
</PackUpBtn>
513548
</Dropdown>
514-
549+
515550
<HeaderProfile user={user} />
516551
</>
517552
);
@@ -520,6 +555,7 @@ export default function Header(props: HeaderProps) {
520555
showAppSnapshot,
521556
applicationId,
522557
permissionDialogVisible,
558+
concurrentAppEditingState, // Include the state in the dependency array
523559
]);
524560

525561
return (

0 commit comments

Comments
 (0)