1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-07-09 16:06:21 +02:00

add fork in header

This commit is contained in:
Kushagra Gour
2024-04-28 17:29:46 +05:30
parent 3e9a947527
commit f28f837fda
4 changed files with 72 additions and 37 deletions

View File

@ -128,6 +128,9 @@ export function Icons() {
<symbol id="check-circle" viewBox="0 0 24 24"> <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" /> <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>
<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"> <symbol id="loader-icon" viewBox="0 0 44 44">
{/* By Sam Herbert (@sherb), for everyone. More http://goo.gl/7AJzbL */} {/* By Sam Herbert (@sherb), for everyone. More http://goo.gl/7AJzbL */}
<g fill="none" fillRule="evenodd" strokeWidth={10}> <g fill="none" fillRule="evenodd" strokeWidth={10}>

View File

@ -4,16 +4,30 @@ import { Trans, NumberFormat, t } from '@lingui/macro';
import { I18n } from '@lingui/react'; import { I18n } from '@lingui/react';
import { ProBadge } from './ProBadge'; import { ProBadge } from './ProBadge';
import { HStack, Stack } from './Stack'; import { HStack, Stack } from './Stack';
import { Icon } from './Icons';
const DEFAULT_PROFILE_IMG = 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"; "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 = const isAutoPreviewOn =
window.forcedSettings.autoPreview !== undefined window.forcedSettings.autoPreview !== undefined
? window.forcedSettings ? window.forcedSettings
: props.isAutoPreviewOn; : props.isAutoPreviewOn;
const isNotMine =
currentItem.createdBy && user?.uid !== currentItem.createdBy;
// console.log(33, currentItem, user?.uid);
return ( return (
<I18n> <I18n>
{({ i18n }) => ( {({ i18n }) => (
@ -23,15 +37,15 @@ export function MainHeader(props) {
id="titleInput" id="titleInput"
title="Click to edit" title="Click to edit"
class="item-title-input" class="item-title-input"
value={props.currentItem.title} value={currentItem.title}
onBlur={props.titleInputBlurHandler} onBlur={titleInputBlurHandler}
/> />
<div class="main-header__btn-wrap flex flex-v-center"> <div class="main-header__btn-wrap flex flex-v-center">
{!isAutoPreviewOn && ( {!isAutoPreviewOn && (
<button <button
class="btn btn btn--dark flex flex-v-center hint--rounded hint--bottom-left" class="btn btn btn--dark flex flex-v-center hint--rounded hint--bottom-left"
aria-label={i18n._(t`Run preview (Ctrl/⌘ + Shift + 5)`)} aria-label={i18n._(t`Run preview (Ctrl/⌘ + Shift + 5)`)}
onClick={props.runBtnClickHandler} onClick={runBtnClickHandler}
> >
<svg> <svg>
<use xlinkHref="#play-icon" /> <use xlinkHref="#play-icon" />
@ -39,9 +53,8 @@ export function MainHeader(props) {
<Trans>Run</Trans> <Trans>Run</Trans>
</button> </button>
)} )}
<Button <Button
onClick={props.assetsBtnHandler} onClick={assetsBtnHandler}
data-event-category="ui" data-event-category="ui"
data-event-action="addLibraryButtonClick" data-event-action="addLibraryButtonClick"
data-testid="addLibraryButton" data-testid="addLibraryButton"
@ -50,8 +63,7 @@ export function MainHeader(props) {
> >
<Trans>Assets</Trans> <Trans>Assets</Trans>
</Button> </Button>
{!isFileMode && (
{!props.isFileMode && (
<Button <Button
onClick={props.addLibraryBtnHandler} onClick={props.addLibraryBtnHandler}
data-event-category="ui" data-event-category="ui"
@ -72,7 +84,6 @@ export function MainHeader(props) {
</span> </span>
</Button> </Button>
)} )}
<button <button
class="btn btn--dark hint--bottom-left" class="btn btn--dark hint--bottom-left"
aria-label={i18n._(t`Share this creation publicly`)} aria-label={i18n._(t`Share this creation publicly`)}
@ -82,9 +93,7 @@ export function MainHeader(props) {
<svg <svg
viewBox="0 0 24 24" viewBox="0 0 24 24"
style={{ style={{
fill: props.currentItem.isPublic fill: currentItem.isPublic ? 'limegreen' : 'currentColor'
? 'limegreen'
: 'currentColor'
}} }}
strokeWidth="2" strokeWidth="2"
strokeLinecap="round" 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" /> <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> </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>
<button <button
@ -106,22 +125,25 @@ export function MainHeader(props) {
</svg> </svg>
<Trans>New</Trans> <Trans>New</Trans>
</button> </button>
<button
id="saveBtn" {!isNotMine && (
class={`btn btn--dark hint--rounded hint--bottom-left ${ <button
props.isSaving ? 'is-loading' : '' id="saveBtn"
} ${props.unsavedEditCount ? 'is-marked' : 0}`} class={`btn btn--dark hint--rounded hint--bottom-left ${
aria-label={i18n._(t`Save current creation (Ctrl/⌘ + S)`)} props.isSaving ? 'is-loading' : ''
onClick={props.saveBtnHandler} } ${props.unsavedEditCount ? 'is-marked' : 0}`}
> aria-label={i18n._(t`Save current creation (Ctrl/⌘ + S)`)}
<svg viewBox="0 0 24 24"> onClick={props.saveBtnHandler}
<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 viewBox="0 0 24 24">
<svg class="btn-loader" width="15" height="15" stroke="#fff"> <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" />
<use xlinkHref="#loader-icon" /> </svg>
</svg> <svg class="btn-loader" width="15" height="15" stroke="#fff">
<Trans>Save</Trans> <use xlinkHref="#loader-icon" />
</button> </svg>
<Trans>Save</Trans>
</button>
)}
<button <button
id="openItemsBtn" id="openItemsBtn"
class={`btn btn--dark hint--rounded hint--bottom-left ${ class={`btn btn--dark hint--rounded hint--bottom-left ${
@ -138,7 +160,7 @@ export function MainHeader(props) {
</svg> </svg>
<Trans>Open</Trans> <Trans>Open</Trans>
</button> </button>
{!props.user ? ( {!user ? (
<Button <Button
onClick={props.loginBtnHandler} onClick={props.loginBtnHandler}
data-event-category="ui" data-event-category="ui"
@ -160,14 +182,10 @@ export function MainHeader(props) {
<img <img
id="headerAvatarImg" id="headerAvatarImg"
width="20" width="20"
src={ src={user ? user.photoURL || DEFAULT_PROFILE_IMG : ''}
props.user
? props.user.photoURL || DEFAULT_PROFILE_IMG
: ''
}
class="main-header__avatar-img" class="main-header__avatar-img"
/> />
{props.user?.isPro ? <ProBadge /> : null} {user?.isPro ? <ProBadge /> : null}
</HStack> </HStack>
</Button> </Button>
)} )}

View File

@ -383,9 +383,11 @@ export default class App extends Component {
} }
const fork = JSON.parse(JSON.stringify(sourceItem)); const fork = JSON.parse(JSON.stringify(sourceItem));
delete fork.id; delete fork.id;
delete fork.createdBy;
fork.title = '(Forked) ' + sourceItem.title; fork.title = '(Forked) ' + sourceItem.title;
fork.updatedOn = Date.now(); fork.updatedOn = Date.now();
this.setCurrentItem(fork).then(() => this.refreshEditor()); this.setCurrentItem(fork).then(() => this.refreshEditor());
route('/create');
alertsService.add(`"${sourceItem.title}" was forked`); alertsService.add(`"${sourceItem.title}" was forked`);
trackEvent('fn', 'itemForked'); trackEvent('fn', 'itemForked');
} }
@ -936,6 +938,15 @@ export default class App extends Component {
var isNewItem = !this.state.currentItem.id; var isNewItem = !this.state.currentItem.id;
this.state.currentItem.id = this.state.currentItem.id =
this.state.currentItem.id || 'item-' + generateRandomId(); 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({ this.setState({
isSaving: true isSaving: true
}); });
@ -1697,6 +1708,9 @@ export default class App extends Component {
isFileMode={ isFileMode={
this.state.currentItem && this.state.currentItem.files this.state.currentItem && this.state.currentItem.files
} }
onItemFork={() => {
this.forkItem(this.state.currentItem);
}}
/> />
{this.state.currentItem && this.state.currentItem.files ? ( {this.state.currentItem && this.state.currentItem.files ? (
<ContentWrapFiles <ContentWrapFiles

View File

@ -157,7 +157,7 @@ function getArrayFromQuerySnapshot(querySnapshot) {
return {}; return {};
} }
const user = doc.data(); const user = doc.data();
Object.assign(window.user, user); window.user = { ...window.user, ...user };
return user; return user;
}); });
} }