浏览代码

Feature/Setups of Onboarding Process (#4580)

UX enhancement: onboarding setup graphs 

Co-authored-by: llcc <[email protected]>
Co-authored-by: Tienson Qin <[email protected]>
Charlie 3 年之前
父节点
当前提交
0d3ac91ca3
共有 34 个文件被更改,包括 798 次插入566 次删除
  1. 2 0
      .carve/ignore
  2. 5 5
      e2e-tests/basic.spec.ts
  3. 13 7
      e2e-tests/fixtures.ts
  4. 16 8
      e2e-tests/utils.ts
  5. 2 2
      resources/css/common.css
  6. 二进制
      resources/img/file-edn.png
  7. 二进制
      resources/img/folder-logo.png
  8. 二进制
      resources/img/folder.png
  9. 1 2
      shadow-cljs.edn
  10. 0 24
      src/main/frontend/components/block.cljs
  11. 7 10
      src/main/frontend/components/header.cljs
  12. 17 0
      src/main/frontend/components/header.css
  13. 28 51
      src/main/frontend/components/journal.cljs
  14. 5 165
      src/main/frontend/components/onboarding.cljs
  15. 0 35
      src/main/frontend/components/onboarding.css
  16. 401 0
      src/main/frontend/components/onboarding/index.css
  17. 208 0
      src/main/frontend/components/onboarding/setups.cljs
  18. 1 1
      src/main/frontend/components/repo.cljs
  19. 1 1
      src/main/frontend/components/right_sidebar.cljs
  20. 0 24
      src/main/frontend/components/settings.cljs
  21. 18 41
      src/main/frontend/components/sidebar.cljs
  22. 5 18
      src/main/frontend/components/svg.cljs
  23. 13 3
      src/main/frontend/components/theme.cljs
  24. 3 97
      src/main/frontend/components/widgets.cljs
  25. 12 9
      src/main/frontend/extensions/srs.cljs
  26. 14 9
      src/main/frontend/extensions/video/youtube.cljs
  27. 7 3
      src/main/frontend/handler/events.cljs
  28. 6 19
      src/main/frontend/handler/repo.cljs
  29. 1 18
      src/main/frontend/handler/user.cljs
  30. 2 1
      src/main/frontend/handler/web/nfs.cljs
  31. 3 3
      src/main/frontend/routes.cljs
  32. 4 0
      src/main/frontend/state.cljs
  33. 0 6
      src/main/frontend/util.cljc
  34. 3 4
      templates/tutorial-en.md

+ 2 - 0
.carve/ignore

@@ -58,6 +58,8 @@ frontend.ui/reset-ios-whole-page-offset!
 frontend.util/d
 ;; Future use?
 frontend.util/safe-search-normalize
+frontend.components.external/import-cp
+frontend.components.repo/add-repo
 ;; For debugging
 frontend.util/trace!
 ;; Repl fn

+ 5 - 5
e2e-tests/basic.spec.ts

@@ -1,6 +1,6 @@
 import { expect } from '@playwright/test'
-import fs from 'fs/promises'
-import path from 'path'
+import * as fs from 'fs'
+import * as path from 'path'
 import { test, graphDir } from './fixtures'
 import { randomString, createRandomPage, newBlock } from './utils'
 
@@ -87,7 +87,7 @@ test('create page and blocks', async ({ page }) => {
 
   await page.waitForTimeout(1000)
 
-  const contentOnDisk = await fs.readFile(
+  const contentOnDisk = fs.readFileSync(
     path.join(graphDir, `pages/${pageTitle}.md`),
     'utf8'
   )
@@ -281,8 +281,8 @@ test('auto completion and auto pair', async ({ page }) => {
 // FIXME: Electron with filechooser is not working
 test.skip('open directory', async ({ page }) => {
   await page.click('#left-sidebar >> text=Journals')
-  await page.waitForSelector('h1:has-text("Open a local directory")')
-  await page.click('h1:has-text("Open a local directory")')
+  await page.waitForSelector('strong:has-text("Choose a folder")')
+  await page.click('strong:has-text("Choose a folder")')
 
   // await page.waitForEvent('filechooser')
   await page.keyboard.press('Escape')

+ 13 - 7
e2e-tests/fixtures.ts

@@ -1,5 +1,5 @@
-import fs from 'fs'
-import path from 'path'
+import * as fs from 'fs'
+import * as path from 'path'
 import { test as base, expect, ConsoleMessage } from '@playwright/test';
 import { ElectronApplication, Page, BrowserContext, _electron as electron } from 'playwright'
 import { loadLocalGraph, randomString } from './utils';
@@ -19,10 +19,16 @@ export let graphDir = path.resolve(testTmpDir, "e2e-test", repoName)
 
 // NOTE: This is a console log watcher for error logs.
 const consoleLogWatcher = (msg: ConsoleMessage) => {
-  // console.log(msg.text())
-  expect(msg.text()).not.toMatch(/^Failed to/)
-  expect(msg.text()).not.toMatch(/^Error/)
-  expect(msg.text()).not.toMatch(/^Uncaught/)
+    // console.log(msg.text())
+  let msgText = msg.text()
+  expect(msgText).not.toMatch(/^Failed to/)
+
+  // youtube video
+  if (!msgText.match(/^Error with Permissions-Policy header: Unrecognized feature/)) {
+    expect(msgText).not.toMatch(/^Error/)
+  }
+
+  expect(msgText).not.toMatch(/^Uncaught/)
   // NOTE: React warnings will be logged as error.
   // expect(msg.type()).not.toBe('error')
 }
@@ -63,7 +69,7 @@ base.beforeAll(async () => {
     expect('page must not crash!').toBe('page crashed')
   })
   page.on('pageerror', (err) => {
-    console.log(err)
+    console.error("[pageerror]", err)
     expect('page must not have errors!').toBe('page has some error')
   })
 

+ 16 - 8
e2e-tests/utils.ts

@@ -1,6 +1,6 @@
 import { Page, Locator } from 'playwright'
 import { expect } from '@playwright/test'
-import process from 'process'
+import * as process from 'process'
 
 export const IsMac = process.platform === 'darwin'
 export const IsLinux = process.platform === 'linux'
@@ -138,12 +138,12 @@ export async function setMockedOpenDirPath(
 export async function loadLocalGraph(page: Page, path?: string): Promise<void> {
   await setMockedOpenDirPath(page, path);
 
-  await page.click('#left-menu.button')
-  const hasOpenButton = await page.$('#head >> .button >> text=Open')
+  const onboardingOpenButton = page.locator('strong:has-text("Choose a folder")')
 
-  if (hasOpenButton) {
-    await page.click('#head >> .button >> text=Open')
+  if (await onboardingOpenButton.isVisible()) {
+    await onboardingOpenButton.click()
   } else {
+    await page.click('#left-menu.button')
     let sidebar = page.locator('#left-sidebar')
     if (!/is-open/.test(await sidebar.getAttribute('class'))) {
       await page.click('#left-menu.button')
@@ -154,8 +154,11 @@ export async function loadLocalGraph(page: Page, path?: string): Promise<void> {
     await page.waitForSelector('#left-sidebar .dropdown-wrapper >> text="Add new graph"', { state: 'visible' })
 
     await page.click('text=Add new graph')
-    await page.waitForSelector('h1:has-text("Open a local directory")', { state: 'visible' })
-    await page.click('h1:has-text("Open a local directory")')
+    await page.waitForSelector('strong:has-text("Choose a folder")', { state: 'visible' })
+    await page.click('strong:has-text("Choose a folder")')
+
+    const skip = page.locator('a:has-text("Skip")')
+    await skip.click()
   }
 
   setMockedOpenDirPath(page, ''); // reset it
@@ -165,7 +168,12 @@ export async function loadLocalGraph(page: Page, path?: string): Promise<void> {
     timeout: 1000 * 60 * 5,
   })
 
-  await page.waitForFunction('window.document.title != "Loading"')
+  const title = await page.title()
+  if (title === "Import data into Logseq" || title === "Add another repo") {
+    await page.click('a.button >> text=Skip')
+  }
+
+  await page.waitForFunction('window.document.title === "Logseq"')
 
   console.log('Graph loaded for ' + path)
 }

+ 2 - 2
resources/css/common.css

@@ -103,8 +103,8 @@ html[data-theme='light'] {
   --ls-tertiary-background-color: #f1eee8;
   --ls-quaternary-background-color: #e8e5de;
   --ls-table-tr-even-background-color: #f7f7f7;
-  --ls-active-primary-color: rgb(4, 85, 145);
-  --ls-active-secondary-color: #003761;
+  --ls-active-primary-color: rgb(0, 105, 182);
+  --ls-active-secondary-color: #00477c;
   --ls-block-properties-background-color: #f7f7f7;
   --ls-page-properties-background-color: #f7f7f7;
   --ls-block-ref-link-text-color: #d8e1e8;

二进制
resources/img/file-edn.png


二进制
resources/img/folder-logo.png


二进制
resources/img/folder.png


+ 1 - 2
shadow-cljs.edn

@@ -39,11 +39,10 @@
         ;; NOTE: electron, browser/mobile-app use different asset-paths.
         ;;   For browser/mobile-app devs, assets are located in /static/js(via HTTP root).
         ;;   For electron devs, assets are located in ./js(via relative path).
-        ;; :dev      {:asset-path "./js"}
+        ; :dev      {:asset-path "./js"}
         :devtools {:before-load frontend.core/stop  ;; before live-reloading any code call this function
                    :after-load  frontend.core/start ;; after live-reloading finishes call this function
                    :watch-path  "/static"
-                   :watch-dir   "static"
                    :preloads    [devtools.preload
                                  shadow.remote.runtime.cljs.browser]}}
 

+ 0 - 24
src/main/frontend/components/block.cljs

@@ -742,27 +742,6 @@
                 (not (= (:id config) "contents")))
        [:span.text-gray-500 "]]"])]))
 
-(rum/defcs tutorial-video  <
-  (rum/local true)
-  [state]
-  (let [lite-mode? (:rum/local state)]
-    [:div.tutorial-video-container.relative
-     {:style {:height 367 :width 653}}
-     (if @lite-mode?
-       [:div
-        [:img.w-full.h-full.absolute
-         {:src "https://img.youtube.com/vi/Afmqowr0qEQ/maxresdefault.jpg"}]
-        [:button
-         {:class "absolute bg-red-300 w-16 h-16 -m-8 top-1/2 left-1/2 rounded-full"
-          :on-click (fn [_] (swap! lite-mode? not))}
-         (svg/play)]]
-       [:iframe.w-full.h-full
-        {:allow-full-screen "allowfullscreen"
-         :allow
-         "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
-         :frame-border "0"
-         :src "https://www.youtube.com/embed/Afmqowr0qEQ?autoplay=1"}])]))
-
 (declare custom-query)
 
 (defn- show-link?
@@ -1176,9 +1155,6 @@
           (when-let [seconds (youtube/parse-timestamp timestamp)]
             (youtube/timestamp seconds)))
 
-        (= name "tutorial-video")
-        (tutorial-video)
-
         (= name "zotero-imported-file")
         (let [[item-key filename] arguments]
           (when (and item-key filename)

+ 7 - 10
src/main/frontend/components/header.cljs

@@ -8,12 +8,10 @@
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.handler :as handler]
-            [frontend.handler.page :as page-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.web.nfs :as nfs]
-            [frontend.modules.shortcut.core :as shortcut]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -204,17 +202,16 @@
         (back-and-forward))
 
       (new-block-mode)
-      
+
       (repo/sync-status current-repo)
 
       (when show-open-folder?
-        [:a.text-sm.font-medium.button
-         {:on-click #(page-handler/ls-dir-files! shortcut/refresh!)}
-         [:div.flex.flex-row.text-center.open-button__inner.items-center
-          (ui/icon "folder-plus")
-          (when-not config/mobile?
-            [:span.ml-1 {:style {:margin-top (if electron-mac? 0 2)}}
-             (t :open)])]])
+        [:a.text-sm.font-medium.button.add-graph-btn.flex.items-center
+         {:on-click #(route-handler/redirect! {:to :repo-add})}
+         (ui/icon "folder-plus")
+         (when-not config/mobile?
+           [:strong {:style {:margin-top (if electron-mac? 0 2)}}
+            (t :on-boarding/add-graph)])])
 
       (when config/publishing?
         [:a.text-sm.font-medium.button {:href (rfe/href :graph)}

+ 17 - 0
src/main/frontend/components/header.css

@@ -109,6 +109,17 @@
       opacity: .9;
     }
   }
+
+  .add-graph-btn {
+    border: 1px dashed var(--ls-border-color);
+    padding: 0 8px;
+
+    strong {
+      padding: 0 10px;
+      position: relative;
+      top: 1px;
+    }
+  }
 }
 
 .is-electron.is-mac .cp__header {
@@ -174,14 +185,20 @@ a.button {
   opacity: 0.6;
   display: block;
   border-radius: 4px;
+  user-select: none;
 
   &:hover, &.active {
     opacity: 1;
     background: none;
+
     @screen md {
         background: var(--ls-tertiary-background-color);
     }
   }
+
+  &:active {
+    opacity: .7;
+  }
 }
 
 .is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) {

+ 28 - 51
src/main/frontend/components/journal.cljs

@@ -1,10 +1,7 @@
 (ns frontend.components.journal
   (:require [clojure.string :as string]
-            [frontend.components.onboarding :as onboarding]
             [frontend.components.page :as page]
             [frontend.components.reference :as reference]
-            [frontend.components.widgets :as widgets]
-            [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
@@ -16,10 +13,7 @@
             [frontend.util :as util]
             [goog.object :as gobj]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [frontend.mobile.util :as mobile-util]
-            [frontend.modules.shortcut.core :as shortcut]
-            [frontend.context.i18n :refer [t]]))
+            [rum.core :as rum]))
 
 (rum/defc blocks-cp < rum/reactive db-mixins/query
   {}
@@ -34,59 +28,42 @@
         repo (state/sub :git/current-repo)
         today? (= (string/lower-case title)
                   (string/lower-case (date/journal-name)))
-        intro? (and (not (state/logged?))
-                    (not (config/local-db? repo))
-                    (not config/publishing?)
-                    today?)
         page-entity (db/pull [:block/name (util/page-name-sanity-lc title)])
         data-page-tags (when (seq (:block/tags page-entity))
                          (let [page-names (model/get-page-names-by-ids (map :db/id (:block/tags page)))]
                            (text/build-data-value page-names)))]
-    (if (and (mobile-util/is-native-platform?) intro?)
-      [:div
-       [:h1.title
-        (t :new-graph)]
-
-       (ui/button
-         (t :open-a-directory)
-         :on-click #(page-handler/ls-dir-files! shortcut/refresh!))]
-      [:div.flex-1.journal.page (cond->
-                                  {:class (if intro? "logseq-intro" "")}
-                                  data-page-tags
-                                  (assoc :data-page-tags data-page-tags))
-
-       (ui/foldable
-        [:a.initial-color.title.journal-title
-         {:href     (rfe/href :page {:name page})
-          :on-mouse-down (fn [e]
-                           (when (util/right-click? e)
-                             (state/set-state! :page-title/context {:page page})))
-          :on-click (fn [e]
-                      (when (gobj/get e "shiftKey")
-                        (when-let [page page-entity]
-                          (state/sidebar-add-block!
-                           (state/get-current-repo)
-                           (:db/id page)
-                           :page
-                           {:page     page
-                            :journal? true}))
-                        (.preventDefault e)))}
-         [:h1.title
-          (util/capitalize-all title)]]
+    [:div.flex-1.journal.page (cond-> {}
+                                data-page-tags
+                                (assoc :data-page-tags data-page-tags))
 
-        (blocks-cp repo page format)
-
-        {})
+     (ui/foldable
+      [:a.initial-color.title.journal-title
+       {:href     (rfe/href :page {:name page})
+        :on-mouse-down (fn [e]
+                         (when (util/right-click? e)
+                           (state/set-state! :page-title/context {:page page})))
+        :on-click (fn [e]
+                    (when (gobj/get e "shiftKey")
+                      (when-let [page page-entity]
+                        (state/sidebar-add-block!
+                         (state/get-current-repo)
+                         (:db/id page)
+                         :page
+                         {:page     page
+                          :journal? true}))
+                      (.preventDefault e)))}
+       [:h1.title
+        (util/capitalize-all title)]]
 
-       (when intro? (widgets/add-graph))
+      (blocks-cp repo page format)
 
-       (page/today-queries repo today? false)
+      {})
 
-       (rum/with-key
-         (reference/references title)
-         (str title "-refs"))
+     (page/today-queries repo today? false)
 
-       (when intro? (onboarding/intro))])))
+     (rum/with-key
+       (reference/references title)
+       (str title "-refs"))]))
 
 (rum/defc journals < rum/reactive
   [latest-journals]

+ 5 - 165
src/main/frontend/components/onboarding.cljs

@@ -1,173 +1,13 @@
 (ns frontend.components.onboarding
-  (:require [frontend.components.svg :as svg]
-            [frontend.context.i18n :refer [t]]
+  (:require [frontend.context.i18n :refer [t]]
             [frontend.handler.route :as route-handler]
             [rum.core :as rum]
-            [frontend.ui :as ui]))
+            [frontend.ui :as ui]
+            [frontend.components.onboarding.setups :as setups]))
 
-(rum/defc ^:large-vars/cleanup-todo intro
+(rum/defc intro
   []
-  [:div#logseq-intro.pl-1
-   [:div.flex-1
-    [:div.flex.flex-col.pl-1.ls-block
-     [:hr {:style {:margin-top 200}}]
-     [:div.flex.flex-row.admonitionblock.align-items {:class "important"}
-      [:div.pr-4.admonition-icon.flex.flex-col.justify-center
-       {:title "Important"} (svg/tip)]
-      [:div.ml-4.text-lg
-       (t :on-boarding/notice)]]
-     [:p
-      {}
-      (t :on-boarding/features-desc)]
-     [:p
-      {}
-      (t :on-boarding/privacy)]
-     [:p
-      {}
-      [:strong {} "Logseq"]
-      (t :on-boarding/inspired-by)
-      [:a {:href "https://roamresearch.com/"
-           :target "_blank"} "Roam Research"]
-      ", "
-      [:a {:href "https://orgmode.org/"
-           :target "_blank"} "Org Mode"]
-      ", "
-      [:a {:href "https://tiddlywiki.com/"
-           :target "_blank"} "Tiddlywiki"]
-      " and "
-      [:a {:href "https://workflowy.com/"
-           :target "_blank"} "Workflowy"]
-      ", hats off to all of them!"]]
-
-    [:img.shadow-2xl
-     {:src
-      "https://asset.logseq.com/static/img/screenshot.png"
-      :alt "screenshot"}]
-
-    [:div.flex.flex-col.ls-block.intro-docs
-     [:h2 {} (t :on-boarding/features)]
-     [:ul
-      {}
-      [:li {} (t :on-boarding/features-backlinks)]
-      [:li {} (t :on-boarding/features-block-embed)]
-      [:li {} (t :on-boarding/features-page-embed)]
-      [:li {} (t :on-boarding/features-graph-vis)]
-      [:li {} "PDF annotations"]
-      [:li {} "Zotero integration"]
-      [:li {} "Spaced repetition cards"]
-      [:li {} (t :on-boarding/features-heading-properties)]
-      [:li
-       {}
-       (t :on-boarding/features-datalog)
-       [:a {:href "https://github.com/tonsky/datascript"
-            :target "_blank"} "Datascript"]]
-      [:li {} (t :on-boarding/features-custom-view-component)]
-      [:li
-       {}
-       [:a {:href "https://excalidraw.com/"
-            :target "_blank"} "Excalidraw"]
-       (t :on-boarding/integration)]
-      [:li
-       {}
-       [:a {:href "https://revealjs.com/"
-            :target "_blank"} "reveal.js"]
-       (t :on-boarding/slide-support)]
-      [:li
-       {}
-       (t :on-boarding/built-in-supports)
-       [:ul
-        {}
-        [:li {} (t :on-boarding/supports-code-highlights)]
-        [:li {} (t :on-boarding/supports-katex-latex)]
-        [:li
-         {}
-         (t :on-boarding/raw)
-         [:a {:href "https://github.com/weavejester/hiccup"
-              :target "_blank"} "hiccup"]]
-        [:li {} (t :on-boarding/raw-html)]]]]
-     [:h2 {} (t :on-boarding/learn-more)]
-     [:ul
-      {}
-      [:li
-       {}
-       "Twitter: "
-       [:a
-        {:href "https://twitter.com/logseq"
-         :target "_blank"}
-        "https://twitter.com/logseq"]]
-      [:li
-       {}
-       "Discord: "
-       [:a
-        {:href "https://discord.gg/KpN4eHY"
-         :target "_blank"}
-        "https://discord.gg/KpN4eHY"]
-       (t :on-boarding/discord-desc)]
-      [:li
-       {}
-       "GitHub: "
-       [:a
-        {:href "https://github.com/logseq/logseq"
-         :target "_blank"}
-        "https://github.com/logseq/logseq"]
-       (t :on-boarding/github-desc)]
-      [:li
-       {}
-       (t :on-boarding/our-blog)
-       [:a
-        {:href "https://docs.logseq.com/"
-         :target "_blank"}
-        "https://docs.logseq.com/"]]]
-     [:h2 (t :on-boarding/credits-to)]
-     [:ul
-      {}
-      [:li [:a {:href "https://roamresearch.com/"
-                :target "_blank"} "Roam Research"]]
-      [:li [:a {:href "https://orgmode.org/"
-                :target "_blank"} "Org Mode"]]
-      [:li [:a {:href "https://tiddlywiki.com/"
-                :target "_blank"} "Tiddlywiki"]]
-      [:li
-       [:a {:href "https://workflowy.com/"
-            :target "_blank"} "Workflowy"]]
-      [:li
-       [:a
-        {:href "https://clojure.org"
-         :target "_blank"}
-        "Clojure && Clojurescript"]
-       (t :on-boarding/clojure-desc)]
-      [:li
-       [:a {:href "https://github.com/tonsky/datascript"
-            :target "_blank"} "Datascript"]
-       (t :on-boarding/datascript-desc)]
-      [:li
-       [:a {:href "https://ocaml.org/"
-            :target "_blank"} "OCaml"]
-       " && "
-       [:a
-        {:href "https://github.com/inhabitedtype/angstrom"
-         :target "_blank"}
-        "Angstrom"]
-       (t :on-boarding/angstrom-desc-1)
-       [:a {:href "https://github.com/mldoc/mldoc"
-            :target "_blank"} (t :on-boarding/angstrom-desc-2)]
-       (t :on-boarding/angstrom-desc-3)]
-      [:li
-       [:a {:href "https://github.com/talex5/cuekeeper"
-            :target "_blank"} "Cuekeeper"]
-       (t :on-boarding/cuekeeper-desc)]
-      [:li
-       [:a {:href "https://github.com/borkdude/sci"
-            :target "_blank"} "sci"]
-       (t :on-boarding/sci-desc)]
-      [:li
-       [:a {:href "https://isomorphic-git.org/"
-            :target "_blank"} "isomorphic-git"]
-       (t :on-boarding/isomorphic-git-desc)]]
-
-     [:img {:src
-            "https://asset.logseq.com/static/img/credits.png"
-            :style {:margin "12px 0 0 0"}}]]]])
+  (setups/picker))
 
 (defn help
   []

+ 0 - 35
src/main/frontend/components/onboarding.css

@@ -1,38 +1,3 @@
-#logseq-intro {
-  h1,
-  h2 {
-    margin: 2.5em 0 0.5em;
-  }
-
-  h2 {
-    font-size: 1.4em;
-  }
-
-  h3 {
-    font-size: 1.275em;
-    margin: 1.5em 0 0.5em;
-  }
-
-  h4 {
-    font-size: 1.175em;
-    margin: 1em 0 0.5em;
-  }
-
-  img {
-    margin: 5em 0;
-    max-width: 100%;
-  }
-
-  p {
-    margin: 15px 0;
-  }
-
-  .content {
-    flex-direction: column;
-    align-items: center;
-  }
-}
-
 .intro-docs {
   max-width: var(--ls-main-content-max-width, 100%)
 }

+ 401 - 0
src/main/frontend/components/onboarding/index.css

@@ -0,0 +1,401 @@
+body[data-page=repo-add],
+body[data-page=import] {
+    .cp__header .add-graph-btn {
+        display: none;
+    }
+}
+
+.cp__onboarding {
+    &-setups {
+        background-color: var(--ls-primary-background-color);
+        z-index: 1;
+
+        .as-flex-center {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .inner-card {
+            background-color: var(--ls-secondary-background-color);
+            padding-top: 55px;
+
+            > h1 {
+                font-size: 30px;
+
+                strong {
+                    color: var(--ls-active-primary-color);
+                    display: inline-block;
+                    padding-right: 4px;
+                    font-weight: normal;
+
+                    &:first-child {
+                        display: block;
+                        text-align: center;
+                        padding-bottom: 15px;
+                    }
+                }
+            }
+
+            > h2 {
+                font-size: 14px;
+                margin-top: 20px;
+                margin-bottom: 40px;
+                padding: 0 30px;
+                text-align: center;
+            }
+
+            > article {
+                background-color: var(--ls-secondary-background-color);
+                border-radius: 20px;
+                flex: 1;
+
+                > section {
+                    flex: 1;
+
+                    &.a {
+                        background-color: var(--ls-tertiary-background-color);
+                        display: flex;
+                        flex-direction: column;
+                        align-items: center;
+                        padding-top: 75px;
+                        padding-bottom: 100px;
+                        border-radius: 20px 20px 0 0;
+                        min-width: 50%;
+
+                        > strong {
+                            font-size: 28px;
+                            padding-bottom: 15px;
+                            font-weight: 600;
+                        }
+
+                        > small {
+                            font-size: 18px;
+                            line-height: 30px;
+                            padding: 0 40px;
+                            text-align: center;
+                        }
+
+                        > .choose {
+                            padding-top: 48px;
+                            width: 100%;
+                            cursor: pointer;
+
+                            &:active {
+                                opacity: .8;
+                            }
+
+                            i {
+                                width: 180px;
+                                height: 120px;
+                                background-image: url("../img/folder-logo.png");
+                                background-size: contain;
+                            }
+
+                            .control {
+                                padding: 20px 20px 0 20px;
+                                width: 100%;
+                            }
+                        }
+                    }
+
+                    &.b {
+                        display: none;
+                        padding: 50px 40px;
+
+                        > p {
+                            width: 100%;
+
+                            &:first-child {
+                                i {
+                                    width: 42px;
+                                }
+
+                                strong {
+                                    font-size: 19px;
+                                    line-height: 1.5em;
+                                }
+                            }
+                        }
+
+                        ul {
+                            margin: 0;
+                            padding-top: 20px;
+                            width: 100%;
+
+                            > li {
+                                list-style: none;
+                                margin-bottom: 8px;
+                                height: 40px;
+                                display: flex;
+
+                                &.hr {
+                                    height: 1px;
+                                    border-bottom: 1px solid var(--ls-border-color);
+                                    margin: 20px 0px;
+                                    opacity: 0.6;
+                                }
+
+                                > i {
+                                    width: 48px;
+                                    background-image: url(../img/folder.png);
+                                    background-size: contain;
+                                    background-repeat: no-repeat;
+                                    background-position-y: 2px;
+                                    position: relative;
+                                    right: -2px;
+
+                                    &.is-file {
+                                        background-image: url("../img/file-edn.png");
+                                        background-size: 50%;
+                                        background-position-x: center;
+                                    }
+
+                                    > .ti {
+                                        opacity: .25;
+                                        color: black;
+                                    }
+                                }
+
+                                > span {
+                                    flex: 1;
+                                    padding-left: 15px;
+                                    display: flex;
+                                    flex-direction: column;
+
+                                    strong {
+                                        font-size: 11px;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                &.importer {
+                    background-color: var(--ls-tertiary-background-color);
+                    padding-top: 80px;
+                    position: relative;
+
+                    > section {
+                        flex: unset;
+                        padding: 0 30px;
+
+                        h1 {
+                            font-size: 26px;
+                            font-weight: 500;
+                        }
+
+                        h2 {
+                            font-size: 14px;
+                            padding-top: 15px;
+                            padding-bottom: 15px;
+                        }
+
+                        &.d {
+                            width: 100%;
+                            padding: 20px 0;
+
+                            > label {
+                                width: unset;
+                                height: 80px;
+                                flex: 1;
+                                margin: 0 15px;
+                                margin-bottom: 10px;
+
+                                > span {
+                                    &:first-child {
+                                        padding: 16px;
+
+                                        i {
+                                            height: 30px;
+                                            width: 30px;
+                                            display: flex;
+                                            align-items: center;
+                                            justify-content: center;
+                                        }
+                                    }
+                                }
+
+                                strong {
+                                    font-size: 18px;
+                                }
+
+                                small {
+                                    font-size: 11px;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+
+            @screen lg {
+                background-color: unset;
+
+                > h1 {
+                    font-size: 36px;
+
+                    strong {
+                        &:first-child {
+                            display: inline-block;
+                        }
+                    }
+                }
+
+                > h2 {
+                    font-size: 20px;
+                    padding: 0;
+                }
+
+                > article {
+                    max-width: 100vw;
+                    flex: unset;
+
+                    > section {
+                        &.a {
+                            border-radius: 20px 0 0 20px;
+
+                            > small {
+                                padding: 0 95px;
+                            }
+
+                            > .choose {
+                                .control {
+                                    padding: 30px 70px 0 70px;
+                                }
+                            }
+                        }
+
+                        &.b {
+                            display: flex;
+                        }
+                    }
+
+                    &.importer {
+                        padding-top: 150px;
+
+                        > section {
+                            padding: 0;
+
+                            h1 {
+                                font-size: 28px;
+                            }
+
+                            h2 {
+                                font-size: 19px;
+                                line-height: 3em;
+                                padding: unset;
+                            }
+
+                            &.d {
+                                padding: 40px 150px;
+                            }
+
+                            &.e {
+                                position: absolute;
+                                bottom: -50px;
+                                right: 15px;
+
+                                a.button {
+                                    padding: 4px 10px;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        label.action-input {
+            transition: none;
+            color: var(--ls-active-primary-color);
+            background-color: var(--ls-quaternary-background-color);
+            height: 68px;
+            width: 100%;
+            opacity: .8;
+            user-select: none;
+            border-radius: 12px;
+            overflow: hidden;
+            cursor: pointer;
+
+            small {
+                font-size: 11px;
+                text-align: center;
+            }
+
+            &:hover {
+                opacity: 1;
+            }
+
+            &:active {
+                opacity: .5;
+            }
+
+            &[disabled] {
+                pointer-events: none;
+            }
+        }
+
+        @screen lg {
+            justify-content: center;
+            align-items: center;
+        }
+    }
+}
+
+html.is-native-android,
+html.is-native-ipad,
+html.is-native-iphone,
+html.is-native-iphone-without-notch {
+    .cp__onboarding {
+        &-setups {
+            position: absolute;
+            width: 100%;
+            top: 0;
+            left: 0;
+            height: 100vh;
+            overflow-y: auto;
+
+            .inner-card {
+                padding-top: 30px;
+                min-height: 100vh;
+                width: 100%;
+
+                > h2 {
+                    margin-bottom: 25px;
+                }
+
+                > article {
+                    > section {
+                        &.a {
+                            padding-top: 25px;
+
+                            small {
+                                text-align: left;
+                                padding: 0 40px;
+                            }
+                            .mobile-intro {
+                                margin-top: 10px;
+                                line-height: 1.5rem;
+                                font-size: 1rem;
+                            }
+
+                            > .choose {
+                                padding-top: 25px;
+                            }
+                        }
+                    }
+                }
+
+                @screen lg {
+                    > article {
+                        width: 1040px;
+                        height: 510px;
+                    }
+                }
+            }
+        }
+    }
+}

+ 208 - 0
src/main/frontend/components/onboarding/setups.cljs

@@ -0,0 +1,208 @@
+(ns frontend.components.onboarding.setups
+  (:require [frontend.state :as state]
+            [rum.core :as rum]
+            [frontend.ui :as ui]
+            [frontend.components.svg :as svg]
+            [frontend.handler.page :as page-handler]
+            [frontend.handler.route :as route-handler]
+            [frontend.util :as util]
+            [frontend.handler.web.nfs :as nfs]
+            [frontend.mobile.util :as mobile-util]
+            [frontend.handler.notification :as notification]
+            [frontend.handler.external :as external-handler]
+            [frontend.modules.shortcut.core :as shortcut]
+            [clojure.string :as string]
+            [goog.object :as gobj]))
+
+(defonce DEVICE (if (util/mobile?) "phone" "computer"))
+
+(rum/defc setups-container
+  [flag content]
+
+  [:div.cp__onboarding-setups.flex.flex-1
+   (let [picker? (= flag :picker)]
+     [:div.inner-card.flex.flex-col.items-center
+
+      [:h1.text-xl
+       (if picker?
+         [:span [:strong (ui/icon "heart")] "Welcome to " [:strong "Logseq!"]]
+         [:span [:strong (ui/icon "file-import")] "Import existing notes"])]
+
+      [:h2
+       (if picker?
+         "First you need to choose a folder where logseq will store your thoughts, ideas, notes."
+         "You can also do this later in the app.")]
+
+      content])])
+
+(rum/defc mobile-intro
+  []
+  [:div.mobile-intro
+   (cond
+     (mobile-util/native-ios?)
+     [:div
+      [:ul
+       [:li "Save them in " [:span.font-bold "iCloud Drive's Logseq directory"] ", and sync them across devices using iCloud."]
+       [:li "Save them in Logseq's directory of your device's local storage."]]]
+
+     (mobile-util/native-android?)
+     [:div
+      "You can save them in your local storage, and use any third-party sync service to keep your notes sync with other devices. "
+      "If you prefer to use Dropbox to sync your notes, you can use "
+      [:a {:href "https://play.google.com/store/apps/details?id=com.ttxapps.dropsync"
+           :target "_blank"}
+       "Dropsync"]
+      ". Or you can use "
+      [:a {:href "https://play.google.com/store/apps/details?id=dk.tacit.android.foldersync.lite"
+           :target "_blank"}
+       "FolderSync"]
+      "."]
+
+     :else
+     nil)])
+
+(rum/defcs picker < rum/reactive
+  [_state]
+  (let [parsing? (state/sub :repo/parsing-files?)]
+
+    (setups-container
+     :picker
+     [:article.flex
+      [:section.a
+       [:strong "Let’s get you set up."]
+       [:small (str "Where on your " DEVICE " do you want to save your work?")
+        (when (mobile-util/is-native-platform?)
+          (mobile-intro))]
+
+       (if (or (nfs/supported?) (mobile-util/is-native-platform?))
+         [:div.choose.flex.flex-col.items-center
+          {:on-click #(page-handler/ls-dir-files!
+                       (fn []
+                         (shortcut/refresh!)))}
+          [:i]
+          [:div.control
+           [:label.action-input.flex.items-center.justify-center.flex-col
+            {:disabled parsing?}
+
+            (if parsing?
+              (ui/loading "")
+              [[:strong "Choose a folder"]
+               [:small "Open existing directory or Create a new one"]])]]]
+         [:div.px-5
+          (ui/admonition :warning
+                         [:p "It seems that your browser doesn't support the "
+                          [:a {:href   "https://web.dev/file-system-access/"
+                               :target "_blank"}
+                           "new native filesystem API"]
+                          [:span ", please use any Chromium 86+ based browser like Chrome, Vivaldi, Edge, etc. Notice that the API doesn't support mobile browsers at the moment."]])])]
+      [:section.b.flex.items-center.flex-col
+       [:p.flex
+        [:i.as-flex-center (ui/icon "zoom-question" {:style {:fontSize "22px"}})]
+        [:span.flex-1.flex.flex-col
+         [:strong "How logseq saves your work"]
+         [:small.opacity-60 "Inside the directory you choose, logseq will create 4 folders."]]]
+
+       [:p.text-sm.pt-5.tracking-wide
+        [:span (str "Each page is a file stored only on your " DEVICE ".")]
+        [:br]
+        [:span "You may choose to sync it later."]]
+
+       [:ul
+        (for [[title label icon]
+              [["Graphics & Documents" "/assets" "artboard"]
+               ["Daily notes" "/journals" "calendar-plus"]
+               ["PAGES" "/pages" "file-text"]
+               []
+               ["APP Internal" "/logseq" "tool"]
+               ["Configs File" "/logseq/config.edn"]]]
+          (if-not title
+            [:li.hr]
+            [:li
+             {:key title}
+             [:i.as-flex-center
+              {:class (when (string/ends-with? label ".edn") "is-file")}
+              (when icon (ui/icon icon))]
+             [:span
+              [:strong.uppercase title]
+              [:small.opacity-50 label]]]))]]])))
+
+(defonce *roam-importing? (atom nil))
+(defonce *opml-importing? (atom nil))
+(defonce *opml-imported-pages (atom nil))
+
+(rum/defc importer < rum/reactive
+  [{:keys [query-params]}]
+  (let [roam-importing? (rum/react *roam-importing?)
+        opml-importing? (rum/react *opml-importing?)
+        finished-cb     (fn []
+                          (notification/show! "Finished!" :success)
+                          (route-handler/redirect-to-home!))]
+
+    (setups-container
+     :importer
+     [:article.flex.flex-col.items-center.importer
+      [:section.c.text-center
+       [:h1 "Do you already have notes that you want to import?"]
+       [:h2 "If they are in a JSON or Markdown format logseq can work with them."]]
+      [:section.d.md:flex
+       [:label.action-input.flex.items-center
+        {:disabled (or roam-importing? opml-importing?)}
+        [:span.as-flex-center [:i (svg/roam-research 28)]]
+        [:span.flex.flex-col
+         (if roam-importing?
+           (ui/loading "Importing ...")
+           [[:strong "RoamResearch"]
+            [:small "Import a JSON Export of your Roam graph"]])]
+        [:input.absolute.hidden
+         {:id        "import-roam"
+          :type      "file"
+          :on-change (fn [e]
+                       (let [file      (first (array-seq (.-files (.-target e))))
+                             file-name (gobj/get file "name")]
+                         (if (string/ends-with? file-name ".json")
+                           (do
+                             (reset! *roam-importing? true)
+                             (let [reader (js/FileReader.)]
+                               (set! (.-onload reader)
+                                     (fn [e]
+                                       (let [text (.. e -target -result)]
+                                         (external-handler/import-from-roam-json! text
+                                                                                  #(do (reset! *roam-importing? false) (finished-cb))))))
+                               (.readAsText reader file)))
+                           (notification/show! "Please choose a JSON file."
+                                               :error))))}]]
+
+       [:label.action-input.flex.items-center
+        {:disabled (or roam-importing? opml-importing?)}
+        [:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
+        [:span.flex.flex-col
+         (if opml-importing?
+           (ui/loading "Importing ...")
+           [[:strong "OPML"]
+            [:small " Import OPML files"]])]
+
+        [:input.absolute.hidden
+         {:id        "import-opml"
+          :type      "file"
+          :on-change (fn [e]
+                       (let [file      (first (array-seq (.-files (.-target e))))
+                             file-name (gobj/get file "name")]
+                         (if (string/ends-with? file-name ".opml")
+                           (do
+                             (reset! *opml-importing? true)
+                             (let [reader (js/FileReader.)]
+                               (set! (.-onload reader)
+                                     (fn [e]
+                                       (let [text (.. e -target -result)]
+                                         (external-handler/import-from-opml! text
+                                                                             (fn [pages]
+                                                                               (reset! *opml-imported-pages pages)
+                                                                               (reset! *opml-importing? false)
+                                                                               (finished-cb))))))
+                               (.readAsText reader file)))
+                           (notification/show! "Please choose a OPML file."
+                                               :error))))}]]]
+
+      (when (= "picker" (:from query-params))
+        [:section.e
+         [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])])))

+ 1 - 1
src/main/frontend/components/repo.cljs

@@ -257,7 +257,7 @@
     (->>
      (concat repo-links
              [(when (seq repo-links) {:hr true})
-              {:title (t :new-graph) :options {:href (rfe/href :repo-add)}}
+              {:title (t :new-graph) :options {:on-click #(page-handler/ls-dir-files! shortcut/refresh!)}}
               {:title (t :all-graphs) :options {:href (rfe/href :repos)}}
               refresh-link
               reindex-link

+ 1 - 1
src/main/frontend/components/right_sidebar.cljs

@@ -24,7 +24,7 @@
   []
   (when-not (util/mobile?)
     (ui/with-shortcut :ui/toggle-right-sidebar "left"
-      [:a.button.fade-link.toggle
+      [:a.button.fade-link.toggle-right-sidebar
        {:on-click ui-handler/toggle-right-sidebar!}
        (ui/icon "layout-sidebar-right" {:style {:fontSize "20px"}})])))
 

+ 0 - 24
src/main/frontend/components/settings.cljs

@@ -27,30 +27,6 @@
             [rum.core :as rum]
             [frontend.mobile.util :as mobile-util]))
 
-(rum/defcs set-email < (rum/local "" ::email)
-  [state]
-  (let [email (get state ::email)]
-    [:div.p-8.flex.items-center.justify-center
-     [:div.w-full.mx-auto
-      [:div
-       [:div
-        [:h1.title.mb-1
-         "Your email address:"]
-        [:div.mt-2.mb-4.relative.rounded-md.max-w-xs
-         [:input#.form-input.is-small
-          {:autoFocus true
-           :on-change (fn [e]
-                        (reset! email (util/evalue e)))}]]]]
-      (ui/button
-        "Submit"
-        :on-click
-        (fn []
-          (user-handler/set-email! @email)))
-
-      [:hr]
-
-      [:span.pl-1.opacity-70 "Git commit requires the email address."]]]))
-
 (defn toggle
   [label-for name state on-toggle & [detail-text]]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start

+ 18 - 41
src/main/frontend/components/sidebar.cljs

@@ -6,7 +6,6 @@
             [frontend.components.journal :as journal]
             [frontend.components.repo :as repo]
             [frontend.components.right-sidebar :as right-sidebar]
-            [frontend.components.settings :as settings]
             [frontend.components.theme :as theme]
             [frontend.components.widgets :as widgets]
             [frontend.components.plugins :as plugins]
@@ -32,7 +31,8 @@
             [frontend.extensions.srs :as srs]
             [frontend.extensions.pdf.assets :as pdf-assets]
             [frontend.mobile.util :as mobile-util]
-            [frontend.handler.mobile.swipe :as swipe]))
+            [frontend.handler.mobile.swipe :as swipe]
+            [frontend.components.onboarding :as onboarding]))
 
 (rum/defc nav-content-item
   [name {:keys [class]} child]
@@ -279,8 +279,9 @@
                                 (editor-handler/upload-asset id files format editor-handler/*asset-uploading? true))))}))
                 state)}
   [{:keys [route-match global-graph-pages? route-name indexeddb-support? db-restoring? main-content]}]
-
-  (let [left-sidebar-open? (state/sub :ui/left-sidebar-open?)]
+  (let [left-sidebar-open? (state/sub :ui/left-sidebar-open?)
+        onboarding-and-home? (and (or (nil? (state/get-current-repo)) (config/demo-graph?))
+                                  (= :home route-name))]
     [:div#main-container.cp__sidebar-main-layout.flex-1.flex
      {:class (util/classnames [{:is-left-sidebar-open left-sidebar-open?}])}
 
@@ -294,11 +295,10 @@
         :data-is-full-width         (or global-graph-pages?
                                         (contains? #{:all-files :all-pages :my-publishing} route-name))}
 
-       (when-not (mobile-util/is-native-platform?)
+       (when (and (not (mobile-util/is-native-platform?))
+                  (contains? #{:page :home} route-name))
          (widgets/demo-graph-alert))
 
-       (widgets/github-integration-soon-deprecated-alert)
-
        (cond
          (not indexeddb-support?)
          nil
@@ -310,14 +310,16 @@
 
          :else
          [:div {:class (if global-graph-pages? "" (util/hiccup->class "max-w-7xl.mx-auto.pb-24"))
-                :style {:margin-bottom (if global-graph-pages? 0 120)
+                :style {:margin-bottom (cond
+                                         global-graph-pages? 0
+                                         onboarding-and-home? -48
+                                         :else 120)
                         :padding-bottom (when (mobile-util/native-iphone?) "7rem")}}
-          main-content])]]]))
+          main-content])
 
-(rum/defc footer
-  []
-  (when-let [user-footer (and config/publishing? (get-in (state/get-config) [:publish-common-footer]))]
-    [:div.p-6 user-footer]))
+       (when onboarding-and-home?
+         [:div {:style {:padding-bottom 200}}
+          (onboarding/intro)])]]]))
 
 (defonce sidebar-inited? (atom false))
 ;; TODO: simplify logic
@@ -340,16 +342,12 @@
                  (reset! sidebar-inited? true))))
            state)}
   []
-  (let [cloning? (state/sub :repo/cloning?)
-        default-home (get-default-home-if-valid)
+  (let [default-home (get-default-home-if-valid)
         parsing? (state/sub :repo/parsing-files?)
         current-repo (state/sub :git/current-repo)
         loading-files? (when current-repo (state/sub [:repo/loading-files? current-repo]))
-        me (state/sub :me)
         journals-length (state/sub :journals-length)
-        latest-journals (db/get-latest-journals (state/get-current-repo) journals-length)
-        preferred-format (state/sub [:me :preferred_format])
-        logged? (:name me)]
+        latest-journals (db/get-latest-journals (state/get-current-repo) journals-length)]
     [:div
      (cond
        (and default-home
@@ -369,28 +367,9 @@
        loading-files?
        (ui/loading (t :loading-files))
 
-       (and (not logged?) latest-journals)
-       (journal/journals latest-journals)
-
-       (and logged? (not preferred-format))
-       (widgets/choose-preferred-format)
-
-       ;; TODO: delay this
-       (and logged? (nil? (:email me)))
-       (settings/set-email)
-
-       cloning?
-       (ui/loading (t :cloning))
-
        (seq latest-journals)
        (journal/journals latest-journals)
 
-       (or
-        (and (mobile-util/is-native-platform?)
-             (nil? (state/get-current-repo)))
-        (and logged? (empty? (:repos me))))
-       (widgets/add-graph)
-
        ;; FIXME: why will this happen?
        :else
        [:div])]))
@@ -521,9 +500,7 @@
                :indexeddb-support?  indexeddb-support?
                :light?              light?
                :db-restoring?       db-restoring?
-               :main-content        main-content})
-
-        (footer)]
+               :main-content        main-content})]
        (right-sidebar/sidebar)
 
        [:div#app-single-container]]

文件差异内容过多而无法显示
+ 5 - 18
src/main/frontend/components/svg.cljs


+ 13 - 3
src/main/frontend/components/theme.cljs

@@ -54,9 +54,19 @@
      [nfs-granted? db-restoring? route])
 
     (rum/use-effect!
-     #(when-not db-restoring?
-        (ui-handler/restore-right-sidebar-state!)
-        (set-restored-sidebar? true))
+     (fn []
+       (when-not db-restoring?
+         (let [repos (state/get-repos)]
+           (if-not (or
+                    ;; demo graph only
+                    (and (= 1 (count repos)) (:example? (first repos))
+                         (not (util/mobile?)))
+                    ;; other graphs exists
+                    (seq repos))
+            (route-handler/redirect! {:to :repo-add})
+            (do
+              (ui-handler/restore-right-sidebar-state!)
+              (set-restored-sidebar? true))))))
      [db-restoring?])
 
     (rum/use-effect!

+ 3 - 97
src/main/frontend/components/widgets.cljs

@@ -1,84 +1,13 @@
 (ns frontend.components.widgets
-  (:require [clojure.string :as string]
-            [frontend.context.i18n :refer [t]]
-            [frontend.handler.notification :as notification]
+  (:require [frontend.context.i18n :refer [t]]
             [frontend.handler.page :as page-handler]
-            [frontend.handler.repo :as repo-handler]
-            [frontend.handler.user :as user-handler]
             [frontend.handler.web.nfs :as nfs]
             [frontend.modules.shortcut.core :as shortcut]
-            [frontend.state :as state]
             [frontend.ui :as ui]
-            [frontend.util :as util]
             [rum.core :as rum]
             [frontend.config :as config]
             [frontend.mobile.util :as mobile-util]))
 
-(rum/defc choose-preferred-format
-  []
-  [:div
-   [:h1.title {:style {:margin-bottom "0.25rem"}}
-    (t :format/preferred-mode)]
-
-   [:div.mt-4.ml-1
-    (ui/button
-      "Markdown"
-      :on-click
-      #(user-handler/set-preferred-format! :markdown))
-
-    [:span.ml-2.mr-2 "-OR-"]
-
-    (ui/button
-      "Org Mode"
-      :on-click
-      #(user-handler/set-preferred-format! :org))]])
-
-(rum/defcs add-github-repo <
-  (rum/local "" ::repo)
-  (rum/local "" ::branch)
-  [state]
-  (let [repo (get state ::repo)
-        branch (get state ::branch)]
-    [:div.flex.flex-col
-     [:div.w-full.mx-auto
-      [:div
-       [:div
-        [:h1.title
-         (t :git/add-repo-prompt)]
-        [:div.mt-4.mb-4.relative.rounded-md.shadow-sm.max-w-xs
-         [:input#repo.form-input.block.w-full.sm:text-sm.sm:leading-5
-          {:autoFocus true
-           :placeholder "https://github.com/username/repo"
-           :on-change (fn [e]
-                        (reset! repo (util/evalue e)))}]]
-        [:label.font-medium "Default Branch (make sure it's matched with your setting on GitHub): "]
-        [:div.mt-2.mb-4.relative.rounded-md.shadow-sm.max-w-xs
-         [:input#branch.form-input.block.w-full.sm:text-sm.sm:leading-5
-          {:value @branch
-           :placeholder "e.g. main"
-           :on-change (fn [e]
-                        (reset! branch (util/evalue e)))}]]]]
-
-      (ui/button
-        (t :git/add-repo-prompt-confirm)
-        :on-click
-        (fn []
-          (let [branch (string/trim @branch)]
-            (if (string/blank? branch)
-              (notification/show!
-               [:p.text-gray-700.dark:text-gray-300 "Please input a branch, make sure it's matched with your setting on GitHub."]
-               :error
-               false)
-              (let [repo (util/lowercase-first @repo)]
-                (if (util/starts-with? repo "https://github.com/")
-                  (let [repo (string/replace repo ".git" "")]
-                    (repo-handler/create-repo! repo branch))
-
-                  (notification/show!
-                   [:p.text-gray-700.dark:text-gray-300 "Please input a valid repo url, e.g. https://github.com/username/repo"]
-                   :error
-                   false)))))))]]))
-
 (rum/defc add-local-directory
   []
   [:div.flex.flex-col
@@ -157,15 +86,9 @@
 
 (rum/defcs add-graph <
   [state & {:keys [graph-types]
-            :or {graph-types [:local :github]}}]
-  (let [github-authed? (state/github-authed?)
-        generate-f (fn [x]
+            :or {graph-types [:local]}}]
+  (let [generate-f (fn [x]
                      (case x
-                       :github
-                       (when (and github-authed? (not (util/electron?)))
-                         (rum/with-key (add-github-repo)
-                           "add-github-repo"))
-
                        :local
                        [(rum/with-key (android-permission-alert)
                           "android-permission-alert")
@@ -186,20 +109,3 @@
     (ui/admonition
      :warning
      [:p (t :on-boarding/demo-graph)])))
-
-(rum/defc github-integration-soon-deprecated-alert
-  []
-  (when-let [repo (state/get-current-repo)]
-    (when (string/starts-with? repo "https://github.com")
-      [:div.github-alert
-       (ui/admonition
-        :warning
-        [:p "We're going to deprecate the GitHub integration when the mobile app is out, you can switch to the latest "
-         [:a {:href "https://github.com/logseq/logseq/releases"
-              :target "_blank"}
-          "desktop app"]
-         [:span ", see more details at "]
-         [:a {:href "https://discord.com/channels/725182569297215569/735735090784632913/861656585578086400"
-              :target "_blank"}
-          "here"]
-         [:span "."]])])))

+ 12 - 9
src/main/frontend/extensions/srs.cljs

@@ -543,15 +543,18 @@
 
 (defn get-srs-cards-total
   []
-  (let [repo (state/get-current-repo)
-        query-string ""
-        blocks (query repo query-string {:use-cache? false
-                                         :disable-reactive? true})]
-    (when (seq blocks)
-      (let [{:keys [result]} (query-scheduled repo blocks (tl/local-now))
-            count (count result)]
-        (reset! cards-total count)
-        count))))
+  (try
+    (let [repo (state/get-current-repo)
+          query-string ""
+          blocks (query repo query-string {:use-cache?        false
+                                           :disable-reactive? true})]
+      (when (seq blocks)
+        (let [{:keys [result]} (query-scheduled repo blocks (tl/local-now))
+              count (count result)]
+          (reset! cards-total count)
+          count)))
+    (catch js/Error e
+      (js/console.error e) 0)))
 
 ;;; register cards macro
 (rum/defcs ^:large-vars/cleanup-todo cards < rum/reactive db-mixins/query

+ 14 - 9
src/main/frontend/extensions/video/youtube.cljs

@@ -27,15 +27,20 @@
     c))
 
 (defn register-player [state]
-  (let [id (first (:rum/args state))
-        player (js/window.YT.Player.
-                (rum/dom-node state)
-                (clj->js
-                 {:events
-                  {"onReady" (fn [_e] (js/console.log id " ready"))}}))]
-    (state/update-state! [:youtube/players]
-                         (fn [players]
-                           (assoc players id player)))))
+  (try
+    (let [id (first (:rum/args state))
+         node (rum/dom-node state)]
+     (when node
+       (let [player (js/window.YT.Player.
+                     node
+                     (clj->js
+                      {:events
+                       {"onReady" (fn [_e] (js/console.log id " ready"))}}))]
+         (state/update-state! [:youtube/players]
+                              (fn [players]
+                                (assoc players id player))))))
+    (catch :default _e
+      nil)))
 
 (rum/defcs youtube-video <
   rum/reactive

+ 7 - 3
src/main/frontend/handler/events.cljs

@@ -75,10 +75,14 @@
     db-encrypted-secret
     close-fn)))
 
-(defmethod handle :graph/added [[_ repo]]
+(defmethod handle :graph/added [[_ repo {:keys [empty-graph?]}]]
   (db/set-key-value repo :ast/version db-schema/ast-version)
   (search-handler/rebuild-indices!)
-  (db/persist! repo))
+  (db/persist! repo)
+  (when (state/setups-picker?)
+    (if empty-graph?
+      (route-handler/redirect! {:to :import :query-params {:from "picker"}})
+      (route-handler/redirect-to-home!))))
 
 (defn- graph-switch [graph]
   (repo-handler/push-if-auto-enabled! (state/get-current-repo))
@@ -105,7 +109,7 @@
   "Logic for keeping db sync when switching graphs
    Only works for electron"
   [graph]
-  (p/let [current-repo (state/get-current-repo) 
+  (p/let [current-repo (state/get-current-repo)
           _ (repo-handler/persist-db! current-repo persist-db-noti-m)
           _ (repo-handler/persist-otherwindow-db! graph)
           _ (repo-handler/restore-and-setup-repo! graph)]

+ 6 - 19
src/main/frontend/handler/repo.cljs

@@ -211,8 +211,8 @@
         (create-default-files! repo-url db-encrypted?)))
     (when re-render?
       (ui-handler/re-render-root! re-render-opts))
-    (state/set-parsing-files! false)
-    (state/pub-event! [:graph/added repo-url])))
+    (state/pub-event! [:graph/added repo-url opts])
+    (state/set-parsing-files! false)))
 
 (defn- parse-files-and-create-default-files!
   [repo-url files delete-files delete-blocks file-paths first-clone? db-encrypted? re-render? re-render-opts metadata opts]
@@ -256,7 +256,7 @@
       (js/setTimeout f 100))))
 
 (defn load-repo-to-db!
-  [repo-url {:keys [first-clone? diffs nfs-files refresh? new-graph?]}]
+  [repo-url {:keys [first-clone? diffs nfs-files refresh? new-graph? empty-graph?]}]
   (spec/validate :repos/url repo-url)
   (when (= :repos (state/get-current-route))
     (route-handler/redirect-to-home!))
@@ -279,7 +279,8 @@
     (cond
       (and (not (seq diffs)) nfs-files)
       (parse-files-and-load-to-db! repo-url nfs-files {:first-clone? true
-                                                       :new-graph? new-graph?})
+                                                       :new-graph? new-graph?
+                                                       :empty-graph? empty-graph?})
 
       (and first-clone? (not nfs-files))
       (->
@@ -545,7 +546,7 @@
                 (let [dummy-notes (t :tutorial/dummy-notes)]
                  (create-dummy-notes-page repo dummy-notes)))
              (when-not config/publishing?
-                (let [tutorial (t :tutorial/text)
+               (let [tutorial (t :tutorial/text)
                      tutorial (string/replace-first tutorial "$today" (date/today))]
                  (create-today-journal-if-not-exists repo {:content tutorial})))
              (create-config-file-if-not-exists repo)
@@ -585,20 +586,6 @@
   (js/setInterval #(push-if-auto-enabled! (state/get-current-repo))
                   (* (config/git-push-secs) 1000)))
 
-(defn create-repo!
-  [repo-url branch]
-  (spec/validate :repos/url repo-url)
-  (util/post (str config/api "repos")
-             {:url repo-url
-              :branch branch}
-             (fn [result]
-               (if (:installation_id result)
-                 (set! (.-href js/window.location) config/website)
-                 (set! (.-href js/window.location) (str "https://github.com/apps/" config/github-app-name "/installations/new"))))
-             (fn [error]
-               (println "Something wrong!")
-               (js/console.dir error))))
-
 (defn- clone-and-load-db
   [repo-url]
   (spec/validate :repos/url repo-url)

+ 1 - 18
src/main/frontend/handler/user.cljs

@@ -7,24 +7,7 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
-            [promesa.core :as p])
-  (:import [goog.format EmailAddress]))
-
-(defn email? [v]
-  (and v
-       (.isValid (EmailAddress. v))))
-
-(defn set-email!
-  [email]
-  (when (email? email)
-    (util/post (str config/api "email")
-               {:email email}
-               (fn [_result]
-                 (db/transact! [{:me/email email}])
-                 (swap! state/state assoc-in [:me :email] email))
-               (fn [_error]
-                 (notification/show! "Email already exists!"
-                                     :error)))))
+            [promesa.core :as p]))
 
 (defn set-cors!
   [cors-proxy]

+ 2 - 1
src/main/frontend/handler/web/nfs.cljs

@@ -134,7 +134,7 @@
                         root-handle)
              repo (str config/local-db-prefix dir-name)
              _ (state/set-loading-files! repo true)
-             _ (when-not (state/home?)
+             _ (when-not (or (state/home?) (state/setups-picker?))
                  (route-handler/redirect-to-home! false))]
        (reset! *repo repo)
        (when-not (string/blank? dir-name)
@@ -175,6 +175,7 @@
                            (p/let [_ (repo-handler/load-repo-to-db! repo
                                                                     {:first-clone? true
                                                                      :new-graph?   true
+                                                                     :empty-graph? (nil? (seq markup-files))
                                                                      :nfs-files    files})]
                              (state/add-repo! {:url repo :nfs? true})
                              (state/set-loading-files! repo false)

+ 3 - 3
src/main/frontend/routes.cljs

@@ -8,8 +8,8 @@
             [frontend.components.journal :as journal]
             [frontend.components.search :as search]
             [frontend.components.settings :as settings]
-            [frontend.components.external :as external]
             [frontend.components.shortcut :as shortcut]
+            [frontend.components.onboarding.setups :as setups]
             [frontend.extensions.zotero :as zotero]))
 
 ;; http://localhost:3000/#?anchor=fn.1
@@ -24,7 +24,7 @@
 
    ["/repo/add"
     {:name :repo-add
-     :view repo/add-repo}]
+     :view setups/picker}]
 
    ["/all-files"
     {:name :all-files
@@ -68,7 +68,7 @@
 
    ["/import"
     {:name :import
-     :view external/import-cp}]
+     :view setups/importer}]
 
    ["/all-journals"
     {:name :all-journals

+ 4 - 0
src/main/frontend/state.cljs

@@ -244,6 +244,10 @@
   []
   (= :home (get-current-route)))
 
+(defn setups-picker?
+  []
+  (= :repo-add (get-current-route)))
+
 (defn get-current-page
   []
   (when (= :page (get-current-route))

+ 0 - 6
src/main/frontend/util.cljc

@@ -1056,12 +1056,6 @@
   (or (:block/original-name page)
       (:block/name page)))
 
-(defn lowercase-first
-  [s]
-  (when s
-    (str (string/lower-case (.charAt s 0))
-         (subs s 1))))
-
 #?(:cljs
    (defn add-style!
      [style]

+ 3 - 4
templates/tutorial-en.md

@@ -1,5 +1,5 @@
 ## Hi, welcome to Logseq!
-- Logseq is a _privacy-first_, _open-source_ platform for _knowledge_ management and collaboration.
+- Logseq is a _privacy-first_, [open-source](https://github.com/logseq/logseq) platform for _knowledge_ management and collaboration.
 - This is a 3 minute tutorial on how to use Logseq. Let's get started!
 - Here are some tips that might be useful.
 #+BEGIN_TIP
@@ -18,9 +18,8 @@ some changes on the right sidebar, those referenced blocks will be changed too!
 - 4. Do you support tasks like todo/doing/done and priorities?
     - Yes, type `/` and pick your favorite todo keyword or priority (A/B/C).
     - NOW [#A] A dummy tutorial on "How to take dummy notes?"
-    - LATER [#A] Check out this awesome video by [:a {:href "https://twitter.com/TechWithEd" :target "_blank"} "@TechWithEd"], which shows how to use logseq to open your local directory.
-
-    {{tutorial-video}}
+    - LATER [#A] Check out this awesome video by [:a {:href "https://twitter.com/shuomi3" :target "_blank"} "@shuomi3"] on how to use Logseq to take notes and organize your life!
+    {{youtube https://www.youtube.com/watch?v=BhHfF0P9A80&ab_channel=ShuOmi}}
 
     - DONE Create a page
     - CANCELED [#C] Write a page with more than 1000 blocks

部分文件因为文件数量过多而无法显示