Merge remote-tracking branch 'upstream/master' into whiteboards

pull/6345/head
Peng Xiao 2022-08-11 18:40:51 +08:00
commit d2c3b88865
10 changed files with 256 additions and 87 deletions

View File

@ -7,10 +7,14 @@ on:
workflow_dispatch:
inputs:
build-target:
description: 'Build Target ("nightly"/"beta"/"non-release")'
type: string
description: 'Build Target (Release Type)'
type: choice
required: true
default: "beta"
options:
- beta
- nightly
- non-release
default: "non-release"
git-ref:
description: "Build from Git Ref(master)"
required: true

View File

@ -9,8 +9,15 @@ import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
// The following refs
// https://stackoverflow.com/questions/29713587/how-to-get-the-real-path-with-action-open-document-tree-intent
// https://gist.github.com/asifmujteba/d89ba9074bc941de1eaa#file-asfurihelper
// with bug fixes and patches.
public class FileUtil {
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {
@ -24,24 +31,64 @@ public class FileUtil {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
// NOTE: It's not a good idea to use storage root as Graph root.
String remain = "";
if (split.length == 2) {
remain = split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + remain;
} else if ("home".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/Documents/" + remain;
} else if ("downloads".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/Download/" + remain; // No 's' here
}
File dir = null;
File[] mdirs = context.getExternalMediaDirs();
for (File mdir : mdirs) {
String extPath = mdir.getAbsolutePath();
if (extPath.contains("/" + type + "/")) {
dir = new File(extPath.substring(0, extPath.indexOf("/Android")) + "/" + remain);
if (dir.exists()) {
return dir.getAbsolutePath();
}
}
}
// FIXME: The following attempt cannot handle same directory name on different devices!
// attempt 1
dir = new File("/storage/" + type + "/" + remain);
if (dir.exists()) {
return dir.getAbsolutePath();
}
// attempt 3
dir = new File("/mnt/media_rw/" + type + "/" + remain);
if (dir.exists()) {
return dir.getAbsolutePath();
}
// attempt 3
dir = new File("/mnt/" + type + "/" + remain);
if (dir.exists()) {
return dir.getAbsolutePath();
}
// TODO: other cases
} else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
return null;
}
}
} else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
@ -56,24 +103,49 @@ public class FileUtil {
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
} else if (isTermuxDocument(uri)) {
String docId = DocumentsContract.getDocumentId(uri);
// Ref: https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxInstaller.java
if (docId.startsWith("/")) {
if (docId.contains("/com.termux/files/home/storage/")) {
String remain = docId.replaceFirst("^.*?com\\.termux/files/home/storage/[^/]+/", "");
if (docId.contains("/storage/external-1")) { // TODO: Support external-2 or more
File[] dirs = context.getExternalFilesDirs(remain);
if (dirs != null && dirs.length >= 2) {
docId = dirs[1].getAbsolutePath();
}
} else if (docId.contains("/storage/media-1")) {
File[] dirs = context.getExternalMediaDirs();
if (dirs != null && dirs.length >= 2) {
docId = dirs[1].getAbsolutePath() + "/" + remain;
}
} else if (docId.contains("/storage/downloads")) {
docId = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + remain;
} else if (docId.contains("/storage/shared")) {
docId = Environment.getExternalStorageDirectory() + "/" + remain;
}
}
File dir = new File(docId);
if (dir.exists()) {
return dir.getAbsolutePath();
}
Log.e("Logseq/FileUtil", "Handle termux content url failed: " + docId);
}
// FIXME: Are there any other cases?
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
@ -135,4 +207,8 @@ public class FileUtil {
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
public static boolean isTermuxDocument(Uri uri) {
return "com.termux.documents".equals(uri.getAuthority());
}
}

View File

@ -8,6 +8,7 @@ import android.os.Environment;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.Settings;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.documentfile.provider.DocumentFile;
@ -56,7 +57,14 @@ public class FolderPicker extends Plugin {
Uri treeUri = result.getData().getData();
Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri,
DocumentsContract.getTreeDocumentId(treeUri));
ret.put("path", FileUtil.getPath(context, docUri));
call.resolve(ret);
Log.i("Logseq/FolderPicker", "Got uri " + docUri);
String path = FileUtil.getPath(context, docUri);
Log.i("Logseq/FolderPicker", "Convert to path " + FileUtil.getPath(context, docUri));
if (path == null || path.isEmpty()) {
call.reject("Cannot support this directory type: " + docUri);
} else {
ret.put("path", path);
call.resolve(ret);
}
}
}

View File

@ -1533,16 +1533,8 @@
(util/stop e))
(route-handler/redirect-to-page! uuid)))
(defn- toggle-block-children
[_e children]
(let [block-ids (map :block/uuid children)]
(dorun
(if (some editor-handler/collapsable? block-ids)
(map editor-handler/collapse-block! block-ids)
(map editor-handler/expand-block! block-ids)))))
(rum/defc block-children < rum/reactive
[config children collapsed?]
[config block children collapsed?]
(let [ref? (:ref? config)
query? (:custom-query? config)
children (when (coll? children)
@ -1552,7 +1544,9 @@
(not collapsed?))
(let [doc-mode? (state/sub :document/mode?)]
[:div.block-children-container.flex {:style {:margin-left (if doc-mode? 18 29)}}
[:div.block-children-left-border {:on-click (fn [event] (toggle-block-children event children))}]
[:div.block-children-left-border
{:on-click (fn [_]
(editor-handler/toggle-open-block-children! (:block/uuid block)))}]
[:div.block-children.w-full {:style {:display (if collapsed? "none" "")}}
(for [child children]
(when (map? child)
@ -2469,7 +2463,7 @@
*navigating-block (get state ::navigating-block)
navigating-block (rum/react *navigating-block)
navigated? (and (not= (:block/uuid block) navigating-block) navigating-block)
block (if navigated?
block (if (or navigated? custom-query?)
(let [block (db/pull [:block/uuid navigating-block])
blocks (db/get-paginated-blocks repo (:db/id block)
{:scoped-block-id (:db/id block)})
@ -2574,7 +2568,7 @@
(when @*show-right-menu?
(block-right-menu config block edit?))]
(block-children config children collapsed?)
(block-children config block children collapsed?)
(dnd-separator-wrapper block block-id slide? false false)]))
@ -2590,7 +2584,7 @@
(or (:ref? config) (:custom-query? config))
(state/set-collapsed-block! block-id
(editor-handler/block-default-collapsed? block config))
(boolean (editor-handler/block-default-collapsed? block config)))
:else
nil)

View File

@ -261,7 +261,7 @@
(ui/menu-link
{:key "Collapse all"
:on-click (fn [_e]
(editor-handler/collapse-all! block-id))}
(editor-handler/collapse-all! block-id {}))}
"Collapse all")
(when (state/sub [:plugin/simple-commands])

View File

@ -307,10 +307,12 @@
result)))
(open-dir [_this _ok-handler]
(p/let [_ (when (= (mobile-util/platform) "android") (check-permission-android))
{:keys [path localDocumentsPath]} (p/chain
(.pickFolder mobile-util/folder-picker)
#(js->clj % :keywordize-keys true))
_ (when (and (mobile-util/native-ios?)
{:keys [path localDocumentsPath]} (-> (.pickFolder mobile-util/folder-picker)
(p/then #(js->clj % :keywordize-keys true))
(p/catch (fn [e]
(js/alert (str e))
nil))) ;; NOTE: Can not pick folder, let it crash
_ (when (and (mobile-util/native-ios?)
(not (or (local-container-path? path localDocumentsPath)
(mobile-util/iCloud-container-path? path))))
(state/pub-event! [:modal/show-instruction]))

View File

@ -13,7 +13,9 @@
[frontend.state :as state]
[frontend.util :as util]
[goog.dom :as gdom]
[logseq.graph-parser.block :as gp-block]))
[logseq.graph-parser.block :as gp-block]
[frontend.modules.instrumentation.posthog :as posthog]
[cljs-bean.core :as bean]))
;; Fns
@ -283,6 +285,13 @@
(defn get-filtered-ref-blocks
[ref-blocks filters ref-pages]
(let [ref-blocks' (mapcat second ref-blocks)
filtered-blocks (filter-blocks ref-blocks' filters ref-pages)]
(group-by :block/page filtered-blocks)))
(try
(let [ref-blocks' (doall (mapcat second ref-blocks))
filtered-blocks (filter-blocks ref-blocks' filters ref-pages)]
(group-by :block/page filtered-blocks))
(catch :default e
(js/console.error e)
(posthog/capture :bad-ref-blocks (bean/->js
{:ref-blocks ref-blocks
:filters filters
:ref-pages ref-pages})))))

View File

@ -3133,13 +3133,16 @@
(defn shortcut-left-right [direction]
(fn [e]
(when-not (auto-complete?)
(util/stop e)
(cond
(state/editing?)
(keydown-arrow-handler direction)
(do
(util/stop e)
(keydown-arrow-handler direction))
(state/selection?)
(open-selected-block! direction e)
(do
(util/stop e)
(open-selected-block! direction e))
:else
nil))))
@ -3198,14 +3201,12 @@
:or {semantic? false}}]
(when block-id
(if-let [block (db-model/query-block-by-uuid block-id)]
(and
(not (util/collapsed? block))
(or (db-model/has-children? block-id)
(and
(:outliner/block-title-collapse-enabled? (state/get-config))
(block-with-title? (:block/format block)
(:block/content block)
semantic?))))
(or (db-model/has-children? block-id)
(and
(:outliner/block-title-collapse-enabled? (state/get-config))
(block-with-title? (:block/format block)
(:block/content block)
semantic?)))
false))))
(defn all-blocks-with-level
@ -3284,14 +3285,16 @@
value (boolean value)]
(when repo
(outliner-tx/transact!
{:outliner-op :collapse-expand-blocks}
(doseq [block-id block-ids]
(when-let [block (db/entity [:block/uuid block-id])]
(let [current-value (:block/collapsed? block)]
(when-not (= current-value value)
(let [block {:block/uuid block-id
:block/collapsed? value}]
(outliner-core/save-block! block)))))))
{:outliner-op :collapse-expand-blocks}
(doseq [block-id block-ids]
(when-let [block (db/entity [:block/uuid block-id])]
(let [current-value (:block/collapsed? block)]
(when-not (= current-value value)
(let [block {:block/uuid block-id
:block/collapsed? value}]
(outliner-core/save-block! block)))))))
(doseq [block-id block-ids]
(state/set-collapsed-block! block-id value))
(let [block-id (first block-ids)
input-pos (or (state/get-edit-pos) :max)]
;; update editing input content
@ -3304,13 +3307,11 @@
(defn collapse-block! [block-id]
(when (collapsable? block-id)
(when-not (skip-collapsing-in-db?)
(set-blocks-collapsed! [block-id] true)))
(state/set-collapsed-block! block-id true))
(set-blocks-collapsed! [block-id] true))))
(defn expand-block! [block-id]
(when-not (skip-collapsing-in-db?)
(set-blocks-collapsed! [block-id] false)
(state/set-collapsed-block! block-id false)))
(set-blocks-collapsed! [block-id] false)))
(defn expand!
([e] (expand! e false))
@ -3383,12 +3384,15 @@
(defn collapse-all!
([]
(collapse-all! nil))
([block-id]
(collapse-all! nil {}))
([block-id {:keys [collapse-self?]
:or {collapse-self? true}}]
(let [blocks (all-blocks-with-level {:incremental? false
:expanded? true
:root-block block-id})
block-ids (map :block/uuid blocks)]
block-ids (cond->> (mapv :block/uuid blocks)
(not collapse-self?)
(remove #{block-id}))]
(set-blocks-collapsed! block-ids true))))
(defn expand-all!
@ -3408,6 +3412,14 @@
(collapse-all!)
(expand-all!))))
(defn toggle-open-block-children! [block-id]
(let [all-expanded? (empty? (all-blocks-with-level {:incremental? false
:collapse? true
:root-block block-id}))]
(if all-expanded?
(collapse-all! block-id {:collapse-self? false})
(expand-all! block-id))))
(defn select-all-blocks!
[]
(if-let [current-input-id (state/get-edit-input-id)]
@ -3494,8 +3506,7 @@
(or
(and
(or (:ref? config) (:custom-query? config))
(>= (inc (:block/level block))
(state/get-ref-open-blocks-level))
(>= (:block/level block) (state/get-ref-open-blocks-level))
;; has children
(first (:block/_parent (db/entity (:db/id block)))))
(util/collapsed? block)))

View File

@ -68,14 +68,15 @@
(defn- tree [flat-nodes root-id]
(let [children (group-by :block/parent flat-nodes)
nodes (fn nodes [parent-id]
(map (fn [b]
(let [children (nodes (:db/id b))]
(if (seq children)
(assoc b :block/children children)
b)))
(children {:db/id parent-id})))]
(nodes root-id)))
nodes (fn nodes [parent-id level]
(mapv (fn [b]
(let [b' (assoc b :block/level (inc level))
children (nodes (:db/id b) (inc level))]
(if (seq children)
(assoc b' :block/children children)
b')))
(get children {:db/id parent-id})))]
(nodes root-id 1)))
(defn non-consecutive-blocks->vec-tree
"`blocks` need to be in the same page."
@ -88,12 +89,15 @@
id->parent (zipmap (map :db/id blocks)
(map (comp :db/id :block/parent) blocks))
top-level-ids (set (remove #(id->parent (id->parent %)) (map :db/id blocks)))
;; Separate blocks into parent and children groups [parent-children, parent-children]
blocks' (loop [blocks blocks
result []]
(if-let [block (first blocks)]
(if (top-level-ids (:db/id block))
(recur (rest blocks) (conj result [block]))
(recur (rest blocks) (conj (vec (butlast result)) (conj (last result) block))))
(let [block' (assoc block :block/level 1)]
(recur (rest blocks) (conj result [block'])))
(recur (rest blocks) (conj (vec (butlast result))
(conj (last result) block))))
result))]
(map (fn [[parent & children]]
(if (seq children)

View File

@ -622,6 +622,67 @@
:use-cache? false})))
total))))))
(deftest test-non-consecutive-blocks->vec-tree
(let [blocks [{:block/page #:db{:id 2313},
:block/uuid #uuid "62f49b4c-f9f0-4739-9985-8bd55e4c68d4",
:block/parent #:db{:id 2313},
:db/id 2315}
{:block/page #:db{:id 2313},
:block/uuid #uuid "62f49b4c-aa84-416e-9554-b486b4e59b1b",
:block/parent #:db{:id 2315},
:db/id 2316}
{:block/page #:db{:id 2313},
:block/uuid #uuid "62f49b4c-f80c-49b4-ae83-f78c4520c071",
:block/parent #:db{:id 2316},
:db/id 2317}
{:block/page #:db{:id 2313},
:block/uuid #uuid "62f49b4c-8f5b-4a04-b749-68d34b28bcf2",
:block/parent #:db{:id 2317},
:db/id 2318}
{:block/page #:db{:id 2313},
:block/uuid #uuid "62f4b8c1-a99b-434f-84c3-011d6afc48ba",
:block/parent #:db{:id 2315},
:db/id 2333}
{:block/page #:db{:id 2313},
:block/uuid #uuid "62f4b8c6-072e-4133-90e2-0591021a7fea",
:block/parent #:db{:id 2333},
:db/id 2334}]]
(= (tree/non-consecutive-blocks->vec-tree blocks)
'({:db/id 2315,
:block/uuid #uuid "62f49b4c-f9f0-4739-9985-8bd55e4c68d4",
:block/parent #:db{:id 2313},
:block/page #:db{:id 2313},
:block/level 1,
:block/children
[{:db/id 2316,
:block/uuid #uuid "62f49b4c-aa84-416e-9554-b486b4e59b1b",
:block/parent #:db{:id 2315},
:block/page #:db{:id 2313},
:block/level 2,
:block/children
[{:db/id 2317,
:block/uuid #uuid "62f49b4c-f80c-49b4-ae83-f78c4520c071",
:block/parent #:db{:id 2316},
:block/page #:db{:id 2313},
:block/level 3,
:block/children
[{:db/id 2318,
:block/uuid #uuid "62f49b4c-8f5b-4a04-b749-68d34b28bcf2",
:block/parent #:db{:id 2317},
:block/page #:db{:id 2313},
:block/level 4}]}]}
{:db/id 2333,
:block/uuid #uuid "62f4b8c1-a99b-434f-84c3-011d6afc48ba",
:block/parent #:db{:id 2315},
:block/page #:db{:id 2313},
:block/level 2,
:block/children
[{:db/id 2334,
:block/uuid #uuid "62f4b8c6-072e-4133-90e2-0591021a7fea",
:block/parent #:db{:id 2333},
:block/page #:db{:id 2313},
:block/level 3}]}]}))))
(comment
(dotimes [i 5]
(do