From f28f837fda9babb035da29a2bbfe5c437df20a31 Mon Sep 17 00:00:00 2001
From: Kushagra Gour <chinchang457@gmail.com>
Date: Sun, 28 Apr 2024 17:29:46 +0530
Subject: [PATCH] add fork in header

---
 src/components/Icons.jsx      |  3 ++
 src/components/MainHeader.jsx | 90 +++++++++++++++++++++--------------
 src/components/app.jsx        | 14 ++++++
 src/db.js                     |  2 +-
 4 files changed, 72 insertions(+), 37 deletions(-)

diff --git a/src/components/Icons.jsx b/src/components/Icons.jsx
index 4267e09..1519d27 100644
--- a/src/components/Icons.jsx
+++ b/src/components/Icons.jsx
@@ -128,6 +128,9 @@ export function Icons() {
 				<symbol id="check-circle" viewBox="0 0 24 24">
 					<path d="M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z" />
 				</symbol>
+				<symbol id="fork" viewBox="0 0 24 24">
+					<path d="M13 14c-3.36 0-4.46 1.35-4.82 2.24C9.25 16.7 10 17.76 10 19a3 3 0 0 1-3 3 3 3 0 0 1-3-3c0-1.31.83-2.42 2-2.83V7.83A2.99 2.99 0 0 1 4 5a3 3 0 0 1 3-3 3 3 0 0 1 3 3c0 1.31-.83 2.42-2 2.83v5.29c.88-.65 2.16-1.12 4-1.12 2.67 0 3.56-1.34 3.85-2.23A3.006 3.006 0 0 1 14 7a3 3 0 0 1 3-3 3 3 0 0 1 3 3c0 1.34-.88 2.5-2.09 2.86C17.65 11.29 16.68 14 13 14m-6 4a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1M7 4a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1m10 2a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1Z" />
+				</symbol>
 				<symbol id="loader-icon" viewBox="0 0 44 44">
 					{/* By Sam Herbert (@sherb), for everyone. More http://goo.gl/7AJzbL */}
 					<g fill="none" fillRule="evenodd" strokeWidth={10}>
diff --git a/src/components/MainHeader.jsx b/src/components/MainHeader.jsx
index d6d37f8..6517c07 100644
--- a/src/components/MainHeader.jsx
+++ b/src/components/MainHeader.jsx
@@ -4,16 +4,30 @@ import { Trans, NumberFormat, t } from '@lingui/macro';
 import { I18n } from '@lingui/react';
 import { ProBadge } from './ProBadge';
 import { HStack, Stack } from './Stack';
+import { Icon } from './Icons';
 
 const DEFAULT_PROFILE_IMG =
 	"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23ccc' d='M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z'/%3E%3C/svg%3E";
 
-export function MainHeader(props) {
+export function MainHeader({
+	user,
+	currentItem,
+	titleInputBlurHandler,
+	runBtnClickHandler,
+	assetsBtnHandler,
+	isFileMode,
+	onItemFork,
+	...props
+}) {
 	const isAutoPreviewOn =
 		window.forcedSettings.autoPreview !== undefined
 			? window.forcedSettings
 			: props.isAutoPreviewOn;
 
+	const isNotMine =
+		currentItem.createdBy && user?.uid !== currentItem.createdBy;
+
+	// console.log(33, currentItem, user?.uid);
 	return (
 		<I18n>
 			{({ i18n }) => (
@@ -23,15 +37,15 @@ export function MainHeader(props) {
 						id="titleInput"
 						title="Click to edit"
 						class="item-title-input"
-						value={props.currentItem.title}
-						onBlur={props.titleInputBlurHandler}
+						value={currentItem.title}
+						onBlur={titleInputBlurHandler}
 					/>
 					<div class="main-header__btn-wrap  flex  flex-v-center">
 						{!isAutoPreviewOn && (
 							<button
 								class="btn btn btn--dark flex flex-v-center hint--rounded hint--bottom-left"
 								aria-label={i18n._(t`Run preview (Ctrl/⌘ + Shift + 5)`)}
-								onClick={props.runBtnClickHandler}
+								onClick={runBtnClickHandler}
 							>
 								<svg>
 									<use xlinkHref="#play-icon" />
@@ -39,9 +53,8 @@ export function MainHeader(props) {
 								<Trans>Run</Trans>
 							</button>
 						)}
-
 						<Button
-							onClick={props.assetsBtnHandler}
+							onClick={assetsBtnHandler}
 							data-event-category="ui"
 							data-event-action="addLibraryButtonClick"
 							data-testid="addLibraryButton"
@@ -50,8 +63,7 @@ export function MainHeader(props) {
 						>
 							<Trans>Assets</Trans>
 						</Button>
-
-						{!props.isFileMode && (
+						{!isFileMode && (
 							<Button
 								onClick={props.addLibraryBtnHandler}
 								data-event-category="ui"
@@ -72,7 +84,6 @@ export function MainHeader(props) {
 								</span>
 							</Button>
 						)}
-
 						<button
 							class="btn btn--dark hint--bottom-left"
 							aria-label={i18n._(t`Share this creation publicly`)}
@@ -82,9 +93,7 @@ export function MainHeader(props) {
 							<svg
 								viewBox="0 0 24 24"
 								style={{
-									fill: props.currentItem.isPublic
-										? 'limegreen'
-										: 'currentColor'
+									fill: currentItem.isPublic ? 'limegreen' : 'currentColor'
 								}}
 								strokeWidth="2"
 								strokeLinecap="round"
@@ -92,7 +101,17 @@ export function MainHeader(props) {
 							>
 								<path d="M18 16.08C17.24 16.08 16.56 16.38 16.04 16.85L8.91 12.7C8.96 12.47 9 12.24 9 12S8.96 11.53 8.91 11.3L15.96 7.19C16.5 7.69 17.21 8 18 8C19.66 8 21 6.66 21 5S19.66 2 18 2 15 3.34 15 5C15 5.24 15.04 5.47 15.09 5.7L8.04 9.81C7.5 9.31 6.79 9 6 9C4.34 9 3 10.34 3 12S4.34 15 6 15C6.79 15 7.5 14.69 8.04 14.19L15.16 18.34C15.11 18.55 15.08 18.77 15.08 19C15.08 20.61 16.39 21.91 18 21.91S20.92 20.61 20.92 19C20.92 17.39 19.61 16.08 18 16.08M18 4C18.55 4 19 4.45 19 5S18.55 6 18 6 17 5.55 17 5 17.45 4 18 4M6 13C5.45 13 5 12.55 5 12S5.45 11 6 11 7 11.45 7 12 6.55 13 6 13M18 20C17.45 20 17 19.55 17 19S17.45 18 18 18 19 18.45 19 19 18.55 20 18 20Z" />
 							</svg>
-							{props.currentItem.isPublic ? null : <Trans>Share</Trans>}
+							{currentItem.isPublic ? null : <Trans>Share</Trans>}
+						</button>
+
+						<button
+							class="btn btn--dark hint--bottom-left"
+							aria-label={i18n._(t`Fork this creation`)}
+							data-testid="headerForkButton"
+							onClick={onItemFork}
+						>
+							<Icon name="fork" />
+							<Trans>Fork</Trans>
 						</button>
 
 						<button
@@ -106,22 +125,25 @@ export function MainHeader(props) {
 							</svg>
 							<Trans>New</Trans>
 						</button>
-						<button
-							id="saveBtn"
-							class={`btn btn--dark hint--rounded hint--bottom-left ${
-								props.isSaving ? 'is-loading' : ''
-							} ${props.unsavedEditCount ? 'is-marked' : 0}`}
-							aria-label={i18n._(t`Save current creation (Ctrl/⌘ + S)`)}
-							onClick={props.saveBtnHandler}
-						>
-							<svg viewBox="0 0 24 24">
-								<path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
-							</svg>
-							<svg class="btn-loader" width="15" height="15" stroke="#fff">
-								<use xlinkHref="#loader-icon" />
-							</svg>
-							<Trans>Save</Trans>
-						</button>
+
+						{!isNotMine && (
+							<button
+								id="saveBtn"
+								class={`btn btn--dark hint--rounded hint--bottom-left ${
+									props.isSaving ? 'is-loading' : ''
+								} ${props.unsavedEditCount ? 'is-marked' : 0}`}
+								aria-label={i18n._(t`Save current creation (Ctrl/⌘ + S)`)}
+								onClick={props.saveBtnHandler}
+							>
+								<svg viewBox="0 0 24 24">
+									<path d="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
+								</svg>
+								<svg class="btn-loader" width="15" height="15" stroke="#fff">
+									<use xlinkHref="#loader-icon" />
+								</svg>
+								<Trans>Save</Trans>
+							</button>
+						)}
 						<button
 							id="openItemsBtn"
 							class={`btn btn--dark hint--rounded hint--bottom-left ${
@@ -138,7 +160,7 @@ export function MainHeader(props) {
 							</svg>
 							<Trans>Open</Trans>
 						</button>
-						{!props.user ? (
+						{!user ? (
 							<Button
 								onClick={props.loginBtnHandler}
 								data-event-category="ui"
@@ -160,14 +182,10 @@ export function MainHeader(props) {
 									<img
 										id="headerAvatarImg"
 										width="20"
-										src={
-											props.user
-												? props.user.photoURL || DEFAULT_PROFILE_IMG
-												: ''
-										}
+										src={user ? user.photoURL || DEFAULT_PROFILE_IMG : ''}
 										class="main-header__avatar-img"
 									/>
-									{props.user?.isPro ? <ProBadge /> : null}
+									{user?.isPro ? <ProBadge /> : null}
 								</HStack>
 							</Button>
 						)}
diff --git a/src/components/app.jsx b/src/components/app.jsx
index dd37f83..3640c9f 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -383,9 +383,11 @@ export default class App extends Component {
 		}
 		const fork = JSON.parse(JSON.stringify(sourceItem));
 		delete fork.id;
+		delete fork.createdBy;
 		fork.title = '(Forked) ' + sourceItem.title;
 		fork.updatedOn = Date.now();
 		this.setCurrentItem(fork).then(() => this.refreshEditor());
+		route('/create');
 		alertsService.add(`"${sourceItem.title}" was forked`);
 		trackEvent('fn', 'itemForked');
 	}
@@ -936,6 +938,15 @@ export default class App extends Component {
 		var isNewItem = !this.state.currentItem.id;
 		this.state.currentItem.id =
 			this.state.currentItem.id || 'item-' + generateRandomId();
+		if (
+			this.state.currentItem.createdBy &&
+			this.state.currentItem.createdBy !== this.state.user.uid
+		) {
+			alertsService.add(
+				'You cannot save this item as it was created by someone else. Fork it to save it as your own.'
+			);
+			return;
+		}
 		this.setState({
 			isSaving: true
 		});
@@ -1697,6 +1708,9 @@ export default class App extends Component {
 							isFileMode={
 								this.state.currentItem && this.state.currentItem.files
 							}
+							onItemFork={() => {
+								this.forkItem(this.state.currentItem);
+							}}
 						/>
 						{this.state.currentItem && this.state.currentItem.files ? (
 							<ContentWrapFiles
diff --git a/src/db.js b/src/db.js
index 1f56485..c9b8381 100644
--- a/src/db.js
+++ b/src/db.js
@@ -157,7 +157,7 @@ function getArrayFromQuerySnapshot(querySnapshot) {
 					return {};
 				}
 				const user = doc.data();
-				Object.assign(window.user, user);
+				window.user = { ...window.user, ...user };
 				return user;
 			});
 	}