Skip to content

Commit

Permalink
feat: Share snippet on Ctrl/Meta+S hotkey (#451)
Browse files Browse the repository at this point in the history
* feat: track if workspace is dirty

* feat: dont allow sharing unchanged snippet or during progress

* feat: share on Ctrl/Meta+S hotkey

* fix: remove notification after share

* fix: lint
  • Loading branch information
x1unix authored Dec 14, 2024
1 parent 3c41073 commit a911391
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 3 deletions.
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

0 comments on commit a911391

Please sign in to comment.