-
Notifications
You must be signed in to change notification settings - Fork 172
Shared links frontend #2890
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shared links frontend #2890
Changes from 4 commits
635cde8
c4c0747
5b655e0
2b99c40
91b84e1
5218f9d
a67a6b7
b6f036b
2315ede
d133925
0f0fb61
76c2178
0a356ca
55c81a8
557d23a
ccb8023
91a0a1e
8518e65
433c81e
bfe97cc
b1842b4
6e7dcc4
7bd35c8
cf4c625
cc412f0
49537fc
5618a03
fe9eea2
d7333da
840e5d5
cd4760e
9b701f0
b48948a
ecb2063
bde45d1
2cf4672
59e8106
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,13 @@ type StateProps = { | |
shortURL?: string; | ||
key: string; | ||
isSicp?: boolean; | ||
programConfig: object | ||
}; | ||
|
||
type State = { | ||
keyword: string; | ||
isLoading: boolean; | ||
isSuccess: boolean; | ||
}; | ||
|
||
export class ControlBarShareButton extends React.PureComponent<ControlBarShareButtonProps, State> { | ||
|
@@ -36,7 +38,28 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
this.handleChange = this.handleChange.bind(this); | ||
this.toggleButton = this.toggleButton.bind(this); | ||
this.shareInputElem = React.createRef(); | ||
this.state = { keyword: '', isLoading: false }; | ||
this.state = { keyword: '', isLoading: false, isSuccess: false }; | ||
} | ||
|
||
componentDidMount() { | ||
document.addEventListener('keydown', this.handleKeyDown); | ||
chownces marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
componentWillUnmount() { | ||
document.removeEventListener('keydown', this.handleKeyDown); | ||
} | ||
|
||
handleKeyDown = (event: any) => { | ||
if (event.key === 'Enter' && event.ctrlKey) { | ||
// console.log('Ctrl+Enter pressed!'); | ||
this.setState({ keyword: "Test" }) | ||
this.props.handleShortenURL(this.state.keyword); | ||
this.setState({ isLoading: true }); | ||
if (this.props.shortURL || this.props.isSicp) { | ||
this.selectShareInputText(); | ||
console.log("link created.") | ||
} | ||
} | ||
} | ||
|
||
public render() { | ||
|
@@ -57,21 +80,50 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
</div> | ||
) : ( | ||
<> | ||
{!this.props.shortURL || this.props.shortURL === 'ERROR' ? ( | ||
{/* check this.props.postSuccess */} | ||
{/* {!this.props.shortURL || this.props.shortURL === 'ERROR' ? ( | ||
!this.state.isLoading || this.props.shortURL === 'ERROR' ? ( */} | ||
{!this.state.isSuccess || this.props.shortURL === 'ERROR' ? ( | ||
!this.state.isLoading || this.props.shortURL === 'ERROR' ? ( | ||
|
||
<div> | ||
{Constants.urlShortenerBase} | ||
<input | ||
placeholder={'custom string (optional)'} | ||
onChange={this.handleChange} | ||
style={{ width: 175 }} | ||
/> | ||
<>{console.log(this.props.programConfig)}</> | ||
Rachelcoll marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<ControlButton | ||
label="Get Link" | ||
icon={IconNames.SHARE} | ||
onClick={() => { | ||
this.props.handleShortenURL(this.state.keyword); | ||
this.setState({ isLoading: true }); | ||
// post request to backend, set keyword as return uuid | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please abstract this into the appropriate file together with the rest of the API callers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I abstract it out as a class function. Since it iteract closedly with class field variables I still keep it inside the class. |
||
const requestBody = { | ||
shared_program: { | ||
data: this.props.programConfig | ||
} | ||
}; | ||
const fetchOpts: RequestInit = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use our request helper/wrapper instead of manual There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I just figured out how to use this, will be updated at next push. |
||
method: 'POST', | ||
body: JSON.stringify(requestBody), | ||
headers: { | ||
'Content-Type': 'application/json' | ||
} | ||
}; | ||
fetch("http://localhost:4000/api/shared_programs", fetchOpts) | ||
.then(res => { | ||
return res.json() | ||
}) | ||
.then(resp => { | ||
this.setState({ keyword: resp.uuid }) | ||
console.log(resp) | ||
}) | ||
.catch(err => console.log("Error: ", err)); | ||
|
||
// this.props.handleShortenURL(this.state.keyword); | ||
// console.log("base", this.props.shortURL) | ||
this.setState({ isLoading: true, isSuccess: true }); | ||
}} | ||
/> | ||
</div> | ||
|
@@ -84,10 +136,13 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
</div> | ||
) | ||
) : ( | ||
<div key={this.props.shortURL}> | ||
<input defaultValue={this.props.shortURL} readOnly={true} ref={this.shareInputElem} /> | ||
// <div key={this.props.shortURL}> | ||
Rachelcoll marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<div key={this.state.keyword}> | ||
{/* <input defaultValue={this.props.shortURL} readOnly={true} ref={this.shareInputElem} /> */} | ||
<input defaultValue={this.state.keyword} readOnly={true} ref={this.shareInputElem} /> | ||
<Tooltip2 content="Copy link to clipboard"> | ||
<CopyToClipboard text={this.props.shortURL}> | ||
{/* <CopyToClipboard text={this.props.shortURL}> */} | ||
<CopyToClipboard text={this.state.keyword}> | ||
<ControlButton icon={IconNames.DUPLICATE} onClick={this.selectShareInputText} /> | ||
</CopyToClipboard> | ||
</Tooltip2> | ||
|
@@ -97,15 +152,15 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
); | ||
|
||
return ( | ||
<Popover2 | ||
popoverClassName="Popover-share" | ||
inheritDarkTheme={false} | ||
content={shareButtonPopoverContent} | ||
> | ||
<Tooltip2 content="Get shareable link" placement={Position.TOP}> | ||
<ControlButton label="Share" icon={IconNames.SHARE} onClick={() => this.toggleButton()} /> | ||
</Tooltip2> | ||
</Popover2> | ||
<Popover2 | ||
popoverClassName="Popover-share" | ||
inheritDarkTheme={false} | ||
content={shareButtonPopoverContent} | ||
> | ||
<Tooltip2 content="Get shareable link" placement={Position.TOP}> | ||
<ControlButton label="Share" icon={IconNames.SHARE} onClick={() => this.toggleButton()} /> | ||
</Tooltip2> | ||
</Popover2> | ||
); | ||
} | ||
|
||
|
@@ -121,8 +176,8 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
} | ||
|
||
// reset state | ||
this.props.handleUpdateShortURL(''); | ||
this.setState({ keyword: '', isLoading: false }); | ||
// this.props.handleUpdateShortURL(''); | ||
this.setState({ keyword: '', isLoading: false, isSuccess: false }); | ||
} | ||
|
||
private handleChange(event: React.FormEvent<HTMLInputElement>) { | ||
|
@@ -135,4 +190,7 @@ export class ControlBarShareButton extends React.PureComponent<ControlBarShareBu | |
this.shareInputElem.current.select(); | ||
} | ||
} | ||
|
||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { FSModule } from 'browserfs/dist/node/core/FS'; | ||
Rachelcoll marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { Dispatch } from 'react'; | ||
import { AnyAction } from 'redux'; | ||
import { WorkspaceLocation } from 'src/commons/workspace/WorkspaceTypes'; | ||
import { IParsedQuery, parseQuery } from '../../commons/utils/QueryHelper'; | ||
import { convertParamToBoolean, convertParamToInt } from '../../commons/utils/ParamParseHelper'; | ||
import { | ||
showFullJSWarningOnUrlLoad, | ||
showFulTSWarningOnUrlLoad, | ||
showHTMLDisclaimer | ||
} from 'src/commons/utils/WarningDialogHelper'; | ||
import { decompressFromEncodedURIComponent } from 'lz-string'; | ||
import { getDefaultFilePath, getLanguageConfig } from 'src/commons/application/ApplicationTypes'; | ||
import { overwriteFilesInWorkspace } from 'src/commons/fileSystem/utils'; | ||
import { setFolderMode, removeEditorTabsForDirectory, addEditorTab, updateActiveEditorTabIndex } from 'src/commons/workspace/WorkspaceActions'; | ||
import { playgroundConfigLanguage } from 'src/features/playground/PlaygroundActions'; | ||
import { WORKSPACE_BASE_PATHS } from '../fileSystem/createInBrowserFileSystem'; | ||
import { Chapter, Variant } from 'js-slang/dist/types'; | ||
|
||
|
||
export type programConfig = { | ||
isFolder: string | undefined, | ||
tabs: string | undefined, | ||
tabIdx: string | undefined, | ||
chap: string | undefined, | ||
variant: string | undefined, | ||
ext: string | undefined, | ||
exec: string | undefined, | ||
files: string | undefined, | ||
prgrm: string | undefined | ||
} | ||
/** | ||
* #chap=4 | ||
* exec=1000 | ||
* ext=NONE | ||
* files=KQJgYgDgNghgngcwE4HsCuA7AJqSrkwC2AdAFYDOAvEA | ||
* isFolder=false | ||
* tabIdx=0 | ||
* tabs=PQBwNghgng5gTgewK4DsAmpHwgWwHQBWAzkA | ||
* variant=default | ||
*/ | ||
export var Decoder = { | ||
decodeString: function (inputString: string) { | ||
const qs: Partial<IParsedQuery> = parseQuery(inputString); | ||
return { | ||
chap: qs.chap, | ||
exec: qs.exec, | ||
files: qs.files, | ||
isFolder: qs.isFolder, | ||
tabIdx: qs.tabIdx, | ||
tabs: qs.tabs, | ||
variant: qs.variant, | ||
prgrm: qs.prgrm, | ||
ext: qs.ext | ||
}; | ||
}, | ||
|
||
decodeJSON: function (inputJSON: string) { | ||
const jsonObject = JSON.parse(inputJSON); | ||
return jsonObject.data; | ||
} | ||
}; | ||
|
||
export async function resetConfig( | ||
configObj: programConfig, | ||
handlers: { | ||
handleChapterSelect: (chapter: Chapter, variant: Variant) => void; | ||
handleChangeExecTime: (execTime: number) => void; | ||
}, | ||
workspaceLocation: WorkspaceLocation, | ||
dispatch: Dispatch<AnyAction>, | ||
fileSystem: FSModule | null | ||
) { | ||
const chapter = convertParamToInt(configObj.chap?.toString()) ?? undefined; | ||
if (chapter === Chapter.FULL_JS) { | ||
showFullJSWarningOnUrlLoad(); | ||
} else if (chapter === Chapter.FULL_TS) { | ||
showFulTSWarningOnUrlLoad(); | ||
} else { | ||
if (chapter === Chapter.HTML) { | ||
const continueToHtml = await showHTMLDisclaimer(); | ||
if (!continueToHtml) { | ||
return; | ||
} | ||
} | ||
|
||
// For backward compatibility with old share links - 'prgrm' is no longer used. | ||
const program = configObj.prgrm === undefined ? '' : decompressFromEncodedURIComponent(configObj.prgrm); | ||
|
||
// By default, create just the default file. | ||
const defaultFilePath = getDefaultFilePath(workspaceLocation); | ||
const files: Record<string, string> = | ||
configObj.files === undefined | ||
? { | ||
[defaultFilePath]: program | ||
} | ||
: parseQuery(decompressFromEncodedURIComponent(configObj.files)); | ||
if (fileSystem !== null) { | ||
await overwriteFilesInWorkspace(workspaceLocation, fileSystem, files); | ||
} | ||
|
||
dispatch(setFolderMode(workspaceLocation, false)); | ||
const isFolderModeEnabled = convertParamToBoolean(configObj.isFolder?.toString()) ?? false; | ||
dispatch(setFolderMode(workspaceLocation, isFolderModeEnabled)); | ||
|
||
const editorTabFilePaths = configObj.tabs?.split(',').map(decompressFromEncodedURIComponent) ?? [ | ||
defaultFilePath | ||
]; | ||
|
||
dispatch( | ||
removeEditorTabsForDirectory(workspaceLocation, WORKSPACE_BASE_PATHS[workspaceLocation]) | ||
); | ||
|
||
editorTabFilePaths.forEach(filePath => | ||
dispatch(addEditorTab(workspaceLocation, filePath, files[filePath] ?? '')) | ||
); | ||
|
||
const activeEditorTabIndex = convertParamToInt(configObj.tabIdx?.toString()) ?? 0; | ||
dispatch(updateActiveEditorTabIndex(workspaceLocation, activeEditorTabIndex)); | ||
if (chapter) { | ||
const languageConfig = getLanguageConfig(chapter, configObj.variant as Variant); | ||
handlers.handleChapterSelect(chapter, languageConfig.variant); | ||
dispatch(playgroundConfigLanguage(languageConfig)); | ||
} | ||
|
||
const execTime = Math.max(convertParamToInt(configObj.exec?.toString() || '1000') || 1000, 1000); | ||
if (execTime) { | ||
handlers.handleChangeExecTime(execTime); | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.