Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as monaco from 'monaco-editor'
import { runFileDispatcher, type StateDispatch } from '~/store'
import { dispatchFormatFile, dispatchResetWorkspace } from '~/store/workspace'
import { dispatchFormatFile, dispatchResetWorkspace, dispatchShareSnippet } from '~/store/workspace'

/**
* MonacoDIContainer is undocumented DI service container of monaco editor.
Expand Down Expand Up @@ -39,7 +39,21 @@ export const attachCustomCommands = (editorInstance: monaco.editor.IStandaloneCo
)
}

const debounced = <TArg>(fn: (arg: TArg) => void, delay: number) => {
let tid: ReturnType<typeof setTimeout> | undefined

return (arg: TArg) => {
if (tid) {
clearTimeout(tid)
}

tid = setTimeout(fn, delay, arg)
}
}

export const registerEditorActions = (editor: monaco.editor.IStandaloneCodeEditor, dispatcher: StateDispatch) => {
const dispatchDebounce = debounced(dispatcher, 750)

const actions = [
{
id: 'clear',
Expand Down Expand Up @@ -67,6 +81,15 @@ export const registerEditorActions = (editor: monaco.editor.IStandaloneCodeEdito
dispatcher(dispatchFormatFile())
},
},
{
id: 'share',
label: 'Share Snippet',
contextMenuGroupId: 'navigation',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS],
run: () => {
dispatchDebounce(dispatchShareSnippet())
},
},
]

actions.forEach((action) => editor.addAction(action))
Expand Down
48 changes: 46 additions & 2 deletions web/src/store/workspace/dispatchers/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { newLoadingAction, newErrorAction, newUIStateChangeAction } from '~/store/actions/ui'
import { type SnippetLoadPayload, WorkspaceAction, type BulkFileUpdatePayload } from '../actions'
import { loadWorkspaceState } from '../config'
import { getDefaultWorkspaceState } from '../state'
import { type WorkspaceState, getDefaultWorkspaceState } from '../state'

/**
* Dispatch snippet load from a predefined source.
Expand Down Expand Up @@ -104,9 +104,29 @@ export const dispatchLoadSnippet =
}
}

const workspaceHasChanges = (state: WorkspaceState) => {
if (state.snippet?.loading) {
return false
}

if (!state.snippet?.id) {
return true
}

return !!state.dirty
}

const workspaceNotChangedNotificationID = 'WS_NOT_CHANGED'

export const dispatchShareSnippet = () => async (dispatch: DispatchFn, getState: StateProvider) => {
const notificationId = newNotificationId()
const { workspace } = getState()
const { workspace, status } = getState()

if (status?.loading) {
// Prevent sharing during any kind of loading progress.
// This also prevents concurrent share process.
return
}

if (!workspace.files) {
dispatch(
Expand All @@ -121,6 +141,29 @@ export const dispatchShareSnippet = () => async (dispatch: DispatchFn, getState:
return
}

if (!workspaceHasChanges(workspace)) {
// Prevent from sharing already shared shippets
dispatch(
newAddNotificationAction({
id: workspaceNotChangedNotificationID,
type: NotificationType.Info,
canDismiss: true,
title: 'Share snippet',
description: "You haven't made any changes to a snippet. Please edit any file before sharing.",
actions: [
{
label: 'OK',
key: 'ok',
primary: true,
onClick: () => newRemoveNotificationAction(workspaceNotChangedNotificationID),
},
],
}),
)
return
}

dispatch(newRemoveNotificationAction(workspaceNotChangedNotificationID))
dispatch(newLoadingAction())
dispatch(
newAddNotificationAction({
Expand Down Expand Up @@ -149,6 +192,7 @@ export const dispatchShareSnippet = () => async (dispatch: DispatchFn, getState:
type: WorkspaceAction.WORKSPACE_IMPORT,
payload: {
...workspace,
dirty: false,
snippet: {
id: snippetID,
},
Expand Down
3 changes: 3 additions & 0 deletions web/src/store/workspace/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const reducers = mapByAction<WorkspaceState>(
const addedFiles = Object.fromEntries(items.map(({ filename, content }) => [filename, content]))
return {
...rest,
dirty: true,
selectedFile: items[0].filename,
files: {
...files,
Expand All @@ -25,6 +26,7 @@ export const reducers = mapByAction<WorkspaceState>(
const { files = {}, ...rest } = s
return {
...rest,
dirty: true,
files: {
...files,
[filename]: content,
Expand Down Expand Up @@ -69,6 +71,7 @@ export const reducers = mapByAction<WorkspaceState>(
const { [filename]: _, ...restFiles } = files
return {
...rest,
dirty: true,
selectedFile: newSelectedFile,
files: restFiles,
}
Expand Down
5 changes: 5 additions & 0 deletions web/src/store/workspace/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export interface WorkspaceState {
* Key-value pair of file names and their content.
*/
files?: Record<string, string>

/**
* Indicates whether any of workspace files were changed.
*/
dirty?: boolean
}

export const initialWorkspaceState: WorkspaceState = {
Expand Down
Loading