瀏覽代碼

Merge branch 'feature/editor-ref' into develop

oldj 3 年之前
父節點
當前提交
b6e5051e0d

+ 1 - 1
.prettierrc.json

@@ -7,7 +7,7 @@
   "insertPragma": false,
   "jsxBracketSameLine": false,
   "jsxSingleQuote": false,
-  "printWidth": 80,
+  "printWidth": 100,
   "proseWrap": "preserve",
   "quoteProps": "as-needed",
   "requirePragma": false,

+ 2 - 2
src/main/actions/hosts/refresh.ts

@@ -48,12 +48,12 @@ export default async (hosts_id: string): Promise<IOperationResult> => {
   try {
     console.log(`-> refreshHosts URL: "${url}"`)
     if (url.startsWith('file://')) {
-      new_content = await fs.promises.readFile(new URL(url) ,'utf-8')
+      new_content = await fs.promises.readFile(new URL(url), 'utf-8')
     } else {
       let resp = await GET(url)
       new_content = resp.data
     }
-  } catch (e) {
+  } catch (e: any) {
     console.error(e)
     return {
       success: false,

+ 66 - 69
src/renderer/components/Editor/HostsEditor.tsx

@@ -24,50 +24,47 @@ import styles from './HostsEditor.less'
 
 modeHosts()
 
-interface Props {
-  hosts: {
-    id: string
-    content?: string
-  }
-}
+interface Props {}
 
 const HostsEditor = (props: Props) => {
-  const { hosts } = props
-  const { hosts_data, isReadOnly } = useModel('useHostsData')
-  const [hosts_id, setHostsId] = useState(hosts.id)
-  const [content, setContent] = useState(hosts.content || '')
+  const { current_hosts, hosts_data, isReadOnly } = useModel('useHostsData')
+  const [hosts_id, setHostsId] = useState(current_hosts?.id || '0')
+  const [content, setContent] = useState('')
   const [is_read_only, setIsReadOnly] = useState(true)
-  const [cm_editor, setCMEditor] =
-    useState<CodeMirror.EditorFromTextArea | null>(null)
-  const el_ref = useRef<HTMLTextAreaElement>(null)
-  const [find_params, setFindParams] = useState<IFindShowSourceParam | null>(
-    null,
-  )
-
-  const loadContent = async () => {
-    if (!cm_editor) return
+  const [find_params, setFindParams] = useState<IFindShowSourceParam | null>(null)
+  const ref_el = useRef<HTMLTextAreaElement>(null)
+  const ref_cm = useRef<CodeMirror.EditorFromTextArea | null>(null)
+
+  const loadContent = async (is_new = false) => {
+    let cm_editor = ref_cm.current
+    if (!cm_editor) {
+      setTimeout(loadContent, 100)
+      return
+    }
 
     let content =
-      hosts.id === '0'
-        ? await actions.getSystemHosts()
-        : await actions.getHostsContent(hosts.id)
+      hosts_id === '0' ? await actions.getSystemHosts() : await actions.getHostsContent(hosts_id)
     setContent(content)
     cm_editor.setValue(content)
-    cm_editor.setOption('readOnly', isReadOnly(hosts))
+    if (is_new) {
+      cm_editor.clearHistory()
+    }
   }
 
   useEffect(() => {
-    loadContent().catch((e) => console.error(e))
-  }, [hosts_id, cm_editor])
-
-  useEffect(() => {
-    if (cm_editor && hosts_id !== hosts.id) {
-      setTimeout(() => cm_editor.clearHistory(), 300)
+    // console.log(current_hosts)
+    setHostsId(current_hosts?.id || '0')
+    let is_readonly = isReadOnly(current_hosts)
+    setIsReadOnly(is_readonly)
+    if (ref_cm.current) {
+      ref_cm.current.setOption('readOnly', is_readonly)
     }
+  }, [current_hosts])
 
-    setHostsId(hosts.id)
-    setIsReadOnly(isReadOnly(hosts))
-  }, [hosts])
+  useEffect(() => {
+    console.log(hosts_id)
+    loadContent(true).catch((e) => console.error(e))
+  }, [hosts_id])
 
   const { run: toSave } = useDebounceFn(
     (id: string, content: string) => {
@@ -81,10 +78,11 @@ const HostsEditor = (props: Props) => {
 
   const onChange = (content: string) => {
     setContent(content)
-    toSave(hosts.id, content)
+    toSave(hosts_id, content)
   }
 
   const toggleComment = () => {
+    let cm_editor = ref_cm.current
     if (is_read_only || !cm_editor) return
     cm_editor.toggleComment()
 
@@ -95,6 +93,7 @@ const HostsEditor = (props: Props) => {
   }
 
   const onGutterClick = (n: number) => {
+    let cm_editor = ref_cm.current
     if (is_read_only || !cm_editor) return
 
     let info = cm_editor.lineInfo(n)
@@ -110,22 +109,18 @@ const HostsEditor = (props: Props) => {
 
     cm_editor
       .getDoc()
-      .replaceRange(
-        new_line,
-        { line: info.line, ch: 0 },
-        { line: info.line, ch: line.length },
-      )
+      .replaceRange(new_line, { line: info.line, ch: 0 }, { line: info.line, ch: line.length })
   }
 
   useEffect(() => {
-    if (!el_ref.current) return
+    if (!ref_el.current) return
 
-    let cm = CodeMirror.fromTextArea(el_ref.current, {
+    let cm = CodeMirror.fromTextArea(ref_el.current, {
       lineNumbers: true,
       readOnly: is_read_only,
       mode: 'hosts',
     })
-    setCMEditor(cm)
+    ref_cm.current = cm
 
     cm.setSize('100%', '100%')
 
@@ -140,10 +135,10 @@ const HostsEditor = (props: Props) => {
   }, [])
 
   useEffect(() => {
-    if (find_params && find_params.item_id === hosts.id) {
-      setSelection(find_params).catch((e) => console.error(e))
+    if (find_params && find_params.item_id === hosts_id) {
+      setSelection(find_params, true).catch((e) => console.error(e))
     }
-  }, [hosts, find_params])
+  }, [hosts_id, find_params])
 
   useOnBroadcast(
     events.editor_content_change,
@@ -151,48 +146,52 @@ const HostsEditor = (props: Props) => {
       if (new_content === content) return
       onChange(new_content)
     },
-    [hosts, hosts_id, content],
+    [hosts_id, content],
   )
 
-  useOnBroadcast(events.editor_gutter_click, onGutterClick, [
-    cm_editor,
-    is_read_only,
-  ])
-
   useOnBroadcast(
     events.hosts_refreshed,
     (h: IHostsListObject) => {
-      if (hosts.id !== '0' && h.id !== hosts.id) return
+      if (hosts_id !== '0' && h.id !== hosts_id) return
       loadContent().catch((e) => console.error(e))
     },
-    [hosts, hosts_data, cm_editor],
+    [hosts_id],
   )
 
   useOnBroadcast(
     events.hosts_refreshed_by_id,
     (id: string) => {
-      if (hosts.id !== '0' && id !== hosts.id) return
+      if (hosts_id !== '0' && hosts_id !== id) return
       loadContent().catch((e) => console.error(e))
     },
-    [hosts, hosts_data, cm_editor],
+    [hosts_id, hosts_data],
   )
 
-  useOnBroadcast(events.toggle_comment, toggleComment, [
-    cm_editor,
-    is_read_only,
-  ])
-
   useOnBroadcast(
     events.set_hosts_on_status,
     () => {
-      if (hosts.id === '0') {
+      if (hosts_id === '0') {
+        loadContent().catch((e) => console.error(e))
+      }
+    },
+    [hosts_id],
+  )
+
+  useOnBroadcast(
+    events.system_hosts_updated,
+    () => {
+      if (hosts_id === '0') {
         loadContent().catch((e) => console.error(e))
       }
     },
-    [hosts, cm_editor],
+    [hosts_id],
   )
 
-  const setSelection = async (params: IFindShowSourceParam) => {
+  useOnBroadcast(events.editor_gutter_click, onGutterClick, [is_read_only])
+  useOnBroadcast(events.toggle_comment, toggleComment, [is_read_only])
+
+  const setSelection = async (params: IFindShowSourceParam, repeat = false) => {
+    let cm_editor = ref_cm.current
     if (!cm_editor) return
     let doc = cm_editor.getDoc()
 
@@ -218,9 +217,9 @@ const HostsEditor = (props: Props) => {
   useOnBroadcast(
     events.show_source,
     async (params: IFindShowSourceParam) => {
-      if (!cm_editor) return
+      if (!ref_cm.current) return
 
-      if (params.item_id !== hosts.id) {
+      if (params.item_id !== hosts_id) {
         setFindParams(params)
         setTimeout(() => {
           setFindParams(null)
@@ -230,16 +229,14 @@ const HostsEditor = (props: Props) => {
 
       setSelection(params).catch((e) => console.error(e))
     },
-    [hosts, cm_editor],
+    [hosts_id],
   )
 
   return (
     <div className={styles.root}>
-      <div
-        className={clsx(styles.editor, is_read_only && styles.read_only_tag)}
-      >
+      <div className={clsx(styles.editor, is_read_only && styles.read_only)}>
         <textarea
-          ref={el_ref}
+          ref={ref_el}
           defaultValue={content}
           // onChange={e => onChange(e.target.value)}
           // disabled={is_read_only}

+ 5 - 18
src/renderer/components/List/ListItem.tsx

@@ -29,8 +29,7 @@ interface Props {
 const ListItem = (props: Props) => {
   const { data, is_tray, selected_ids } = props
   const { lang, i18n } = useModel('useI18n')
-  const { hosts_data, setList, current_hosts, setCurrentHosts } =
-    useModel('useHostsData')
+  const { hosts_data, setList, current_hosts, setCurrentHosts } = useModel('useHostsData')
   const [is_collapsed, setIsCollapsed] = useState(!!data.is_collapsed)
   const [is_on, setIsOn] = useState(data.on)
   const el = useRef<HTMLDivElement>(null)
@@ -86,11 +85,7 @@ const ListItem = (props: Props) => {
 
   return (
     <div
-      className={clsx(
-        styles.root,
-        is_selected && styles.selected,
-        is_tray && styles.is_tray,
-      )}
+      className={clsx(styles.root, is_selected && styles.selected, is_tray && styles.is_tray)}
       // className={clsx(styles.item, is_selected && styles.selected, is_collapsed && styles.is_collapsed)}
       // style={{ paddingLeft: `${1.3 * level}em` }}
       onContextMenu={(e) => {
@@ -155,9 +150,7 @@ const ListItem = (props: Props) => {
             label:
               deal_count === 1
                 ? lang.move_to_trashcan
-                : i18n.trans('move_items_to_trashcan', [
-                    deal_count.toLocaleString(),
-                  ]),
+                : i18n.trans('move_items_to_trashcan', [deal_count.toLocaleString()]),
             click() {
               let ids = deal_count === 1 ? [data.id] : selected_ids
               agent.broadcast(events.move_to_trashcan, ids)
@@ -184,14 +177,8 @@ const ListItem = (props: Props) => {
       }}
     >
       <div className={styles.title} onClick={onSelect}>
-        <span
-          className={clsx(styles.icon, is_folder && styles.folder)}
-          onClick={toggleIsCollapsed}
-        >
-          <ItemIcon
-            type={data.is_sys ? 'system' : data.type}
-            is_collapsed={data.is_collapsed}
-          />
+        <span className={clsx(styles.icon, is_folder && styles.folder)} onClick={toggleIsCollapsed}>
+          <ItemIcon type={data.is_sys ? 'system' : data.type} is_collapsed={data.is_collapsed} />
         </span>
         {data.title || lang.untitled}
       </div>

+ 8 - 32
src/renderer/components/List/index.tsx

@@ -13,11 +13,7 @@ import { actions, agent } from '@renderer/core/agent'
 import useOnBroadcast from '@renderer/core/useOnBroadcast'
 import { IHostsListObject } from '@root/common/data'
 import events from '@root/common/events'
-import {
-  findItemById,
-  getNextSelectedItem,
-  setOnStateOfItem,
-} from '@root/common/hostsFn'
+import { findItemById, getNextSelectedItem, setOnStateOfItem } from '@root/common/hostsFn'
 import { IFindShowSourceParam } from '@root/common/types'
 import clsx from 'clsx'
 import React, { useEffect, useState } from 'react'
@@ -35,9 +31,7 @@ const List = (props: Props) => {
     useModel('useHostsData')
   const { configs } = useModel('useConfigs')
   const { lang } = useModel('useI18n')
-  const [selected_ids, setSelectedIds] = useState<string[]>([
-    current_hosts?.id || '0',
-  ])
+  const [selected_ids, setSelectedIds] = useState<string[]>([current_hosts?.id || '0'])
   const [show_list, setShowList] = useState<IHostsListObject[]>([])
   const toast = useToast()
 
@@ -104,11 +98,7 @@ const List = (props: Props) => {
       if (current_hosts) {
         let hosts = findItemById(list, current_hosts.id)
         if (hosts) {
-          agent.broadcast(
-            events.set_hosts_on_status,
-            current_hosts.id,
-            hosts.on,
-          )
+          agent.broadcast(events.set_hosts_on_status, current_hosts.id, hosts.on)
         }
       }
     } else {
@@ -143,9 +133,7 @@ const List = (props: Props) => {
 
   if (!is_tray) {
     useOnBroadcast(events.toggle_item, onToggleItem, [hosts_data, configs])
-    useOnBroadcast(events.write_hosts_to_system, writeHostsToSystem, [
-      hosts_data,
-    ])
+    useOnBroadcast(events.write_hosts_to_system, writeHostsToSystem, [hosts_data])
   } else {
     useOnBroadcast(events.tray_list_updated, loadHostsData)
   }
@@ -159,9 +147,7 @@ const List = (props: Props) => {
 
       if (current_hosts && ids.includes(current_hosts.id)) {
         // 选中删除指定节点后的兄弟节点
-        let next_item = getNextSelectedItem(hosts_data.list, (i) =>
-          ids.includes(i.id),
-        )
+        let next_item = getNextSelectedItem(hosts_data.list, (i) => ids.includes(i.id))
         setCurrentHosts(next_item || null)
         setSelectedIds(next_item ? [next_item.id] : [])
       }
@@ -214,16 +200,11 @@ const List = (props: Props) => {
           setList(list).catch((e) => console.error(e))
         }}
         onSelect={(ids: string[]) => {
-          console.log(ids)
+          // console.log(ids)
           setSelectedIds(ids)
         }}
         nodeRender={(data) => (
-          <ListItem
-            key={data.id}
-            data={data}
-            is_tray={is_tray}
-            selected_ids={selected_ids}
-          />
+          <ListItem key={data.id} data={data} is_tray={is_tray} selected_ids={selected_ids} />
         )}
         collapseArrow={
           <Center w="20px" h="20px">
@@ -241,12 +222,7 @@ const List = (props: Props) => {
         draggingNodeRender={(data) => {
           return (
             <div className={clsx(styles.for_drag)}>
-              <span
-                className={clsx(
-                  styles.icon,
-                  data.type === 'folder' && styles.folder,
-                )}
-              >
+              <span className={clsx(styles.icon, data.type === 'folder' && styles.folder)}>
                 <ItemIcon
                   type={data.is_sys ? 'system' : data.type}
                   is_collapsed={data.is_collapsed}

+ 2 - 28
src/renderer/components/MainPanel/index.tsx

@@ -6,36 +6,17 @@
 
 import { useModel } from '@@/plugin-model/useModel'
 import HostsEditor from '@renderer/components/Editor/HostsEditor'
-import { actions } from '@renderer/core/agent'
 import useOnBroadcast from '@renderer/core/useOnBroadcast'
 import events from '@root/common/events'
-import React, { useEffect, useState } from 'react'
+import React from 'react'
 import styles from './index.less'
 import { useToast } from '@chakra-ui/react'
 
 interface Props {}
 
 const MainPanel = (props: Props) => {
-  const { current_hosts } = useModel('useHostsData')
-  const [system_hosts_content, setSystemHostsContent] = useState('')
   const toast = useToast()
 
-  useEffect(() => {
-    if (!current_hosts) {
-      actions.getSystemHosts().then((value) => setSystemHostsContent(value))
-    }
-  }, [current_hosts])
-
-  useOnBroadcast(
-    events.system_hosts_updated,
-    () => {
-      if (!current_hosts) {
-        actions.getSystemHosts().then((value) => setSystemHostsContent(value))
-      }
-    },
-    [current_hosts],
-  )
-
   useOnBroadcast(events.cmd_run_result, (result) => {
     // console.log(result)
     if (!result.success) {
@@ -49,14 +30,7 @@ const MainPanel = (props: Props) => {
 
   return (
     <div className={styles.root}>
-      <HostsEditor
-        hosts={
-          current_hosts || {
-            id: '0',
-            content: system_hosts_content,
-          }
-        }
-      />
+      <HostsEditor />
     </div>
   )
 }

+ 8 - 14
src/renderer/models/useHostsData.ts

@@ -5,11 +5,7 @@
  */
 
 import { actions } from '@renderer/core/agent'
-import {
-  IHostsBasicData,
-  IHostsListObject,
-  VersionType,
-} from '@root/common/data'
+import { IHostsBasicData, IHostsListObject, VersionType } from '@root/common/data'
 import version from '@root/version.json'
 import { useState } from 'react'
 
@@ -19,9 +15,7 @@ export default function useHostsData() {
     trashcan: [],
     version: version as VersionType,
   })
-  const [current_hosts, setCurrentHosts] = useState<IHostsListObject | null>(
-    null,
-  )
+  const [current_hosts, setCurrentHosts] = useState<IHostsListObject | null>(null)
 
   const loadHostsData = async () => {
     setHostsData(await actions.getBasicData())
@@ -56,10 +50,7 @@ export default function useHostsData() {
       return true // system hosts
     }
 
-    if (
-      hosts.type &&
-      ['group', 'remote', 'folder', 'trashcan'].includes(hosts.type)
-    ) {
+    if (hosts.type && ['group', 'remote', 'folder', 'trashcan'].includes(hosts.type)) {
       return true
     }
 
@@ -75,10 +66,13 @@ export default function useHostsData() {
     hosts_data,
     setHostsData,
     loadHostsData,
+
     setList,
-    isHostsInTrashcan,
-    isReadOnly,
+
     current_hosts,
     setCurrentHosts,
+
+    isHostsInTrashcan,
+    isReadOnly,
   }
 }

+ 1 - 1
src/version.json

@@ -1 +1 @@
-[4, 1, 2, 6083]
+[4, 1, 2, 6085]