From 65cd0e4b66c27d16a9969a1e2460496eec8e63bd Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Tue, 14 Sep 2021 16:37:54 +0100 Subject: [PATCH 1/5] build: fix import semantics for components --- .babelrc | 19 ------------ .gitignore | 1 + babel.config.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 8 +++-- project.clj | 2 +- yarn.lock | 64 +++++++++++++++++++++++++++++---------- 6 files changed, 137 insertions(+), 37 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 4dc3da3ab3..0000000000 --- a/.babelrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "presets": [ - "@babel/env", - // Compile tsx files. - "@babel/preset-typescript", - ["@babel/preset-react", {"runtime": "automatic"}] - ], - "plugins": [ - // Allow using @/ for root relative imports. - ["module-resolver", {"alias": {"@": "./src/js/components"}}], - // Our build doesn't need the {"loose": true} option, but if not included it wil - // show a lot of warnings on the storybook build. - ["@babel/proposal-class-properties", {"loose": true}], - ["@babel/proposal-object-rest-spread", {"loose": true}], - // Used only by storybook, but must be included to avoid build warnings/errors. - ["@babel/plugin-proposal-private-methods", {"loose": true}], - ["@babel/plugin-proposal-private-property-in-object", {"loose": true}] - ] -} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 954e41e4af..03c5a3a527 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ dist # design system static build /storybook-static +/src/gen \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000..26b09b143c --- /dev/null +++ b/babel.config.js @@ -0,0 +1,80 @@ +const {existsSync, lstatSync} = require("fs"); +const {resolve, dirname} = require("path"); + +function isRelativeImport(path){ + return path.startsWith("."); +} + +function isDirectory(path) { + return existsSync(path) && lstatSync(path).isDirectory(); +} + +function resolveImport (from, to) { + return resolve(dirname(from), to); +} + +function replaceDirectoryImports() { + return { + visitor: { + ImportDeclaration: (path, state) => { + const importPath = path.node.source.value; + const fileName = state.file.opts.filename; + if (isRelativeImport(importPath) && isDirectory(resolveImport(fileName, importPath))) { + path.node.source.value += "/index"; + } + } + } + } +} + + +// This config will output files to ./src/gen/components via the `yarn components` script +// See https://shadow-cljs.github.io/docs/UsersGuide.html#_javascript_dialects +module.exports = { + presets: [ + "@babel/env", + // Compile tsx files. + "@babel/preset-typescript", + // Use the react runtime import if available. + ["@babel/preset-react", {"runtime": "automatic"}] + ], + plugins: [ + // Add /index to all relative directory imports, because Shadow-CLJS does not support + // them (https://github.com/thheller/shadow-cljs/issues/841#issuecomment-777323477) + // NB: Putting these files in node_modules would have fixed the directory imports + // but broken hot reload (https://github.com/thheller/shadow-cljs/issues/764#issuecomment-663064549) + replaceDirectoryImports, + // Allow using @/ for root relative imports in the component library. + ["module-resolver", {alias: {"@": "./src/js/components"}}], + // Transform material-ui imports into deep imports for faster reload. + // material-ui is very big, and importing it all can slow down development rebuilds by a lot. + // https://material-ui.com/guides/minimizing-bundle-size/#development-environment + ["transform-imports", { + "@material-ui/core": { + transform: "@material-ui/core/esm/${member}", + preventFullImport: true + }, + "@material-ui/icons": { + transform: "@material-ui/icons/esm/${member}", + preventFullImport: true + } + }], + // Our build doesn't need the {loose: true} option, but if not included it wil + // show a lot of warnings on the storybook build. + ["@babel/proposal-class-properties", {loose: true}], + ["@babel/proposal-object-rest-spread", {loose: true}], + // Used only by storybook, but must be included to avoid build warnings/errors. + ["@babel/plugin-proposal-private-methods", {loose: true}], + ["@babel/plugin-proposal-private-property-in-object", {loose: true}], + // Import helpers from @babel/runtime instead of duplicating them everywhere. + "@babel/plugin-transform-runtime", + // Better debug information for styled components. + // https://styled-components.com/docs/tooling#babel-plugin + "babel-plugin-styled-components" + ], + // Do not apply this babel config to node_modules. + // Shadow-CLJS also runs babel over node_modules and we don't want this + // configuration to apply to it. + // We still want it to be picked up by storybook though. + exclude: ["node_modules"] +} diff --git a/package.json b/package.json index 8bae4d9724..2db64dac81 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,11 @@ "update": "standard-version -p --releaseCommitMessageFormat v{{currentTag}}", "dev": "yarn components && concurrently \"yarn components:watch\" \"yarn client:watch\"", "client:watch": "shadow-cljs watch main renderer app", - "components": "babel ./src/js/components/ --extensions \".ts,.tsx\" --out-dir ./dist/js/components/", + "components": "babel ./src/js/components/ --extensions \".ts,.tsx\" --out-dir ./src/gen/components/", "components:watch": "yarn components --watch", "compile": "yarn components && shadow-cljs compile main renderer app", "prod": "yarn components && shadow-cljs release main renderer app", - "clean": "rm -rf resources/public/**/*.js target .shadow-cljs ./src/stories/**/*.js", + "clean": "rm -rf resources/public/**/*.js target .shadow-cljs src/gen", "dist": "electron-builder -p always", "storybook:watch": "start-storybook -p 6006", "storybook": "build-storybook", @@ -59,6 +59,7 @@ } }, "dependencies": { + "@babel/runtime": "^7.15.4", "@geometricpanda/storybook-addon-badges": "^0.0.4", "@js-joda/core": "1.12.0", "@js-joda/locale_en-us": "3.1.1", @@ -100,6 +101,7 @@ "@babel/plugin-proposal-object-rest-spread": "^7.15.6", "@babel/plugin-proposal-private-methods": "^7.14.5", "@babel/plugin-proposal-private-property-in-object": "^7.15.4", + "@babel/plugin-transform-runtime": "^7.15.0", "@babel/preset-env": "^7.15.6", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.15.0", @@ -110,6 +112,8 @@ "@storybook/react": "^6.3.8", "babel-loader": "^8.2.2", "babel-plugin-module-resolver": "^4.1.0", + "babel-plugin-styled-components": "^1.13.2", + "babel-plugin-transform-imports": "^2.0.0", "concurrently": "^6.2.1", "electron": "^12.0.4", "electron-builder": "22.10", diff --git a/project.clj b/project.clj index 7fd0fc3f20..26f524b80c 100644 --- a/project.clj +++ b/project.clj @@ -57,7 +57,7 @@ :min-lein-version "2.5.3" - :source-paths ["src/clj" "src/cljs" "src/cljc" "src/js" "dist/js"] + :source-paths ["src/clj" "src/cljs" "src/cljc" "src/js" "src/gen"] :main athens.self-hosted.core :aot [athens.self-hosted.core] diff --git a/yarn.lock b/yarn.lock index 742d7b5ead..f8ff3d1fb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1134,6 +1134,18 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-runtime@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.0.tgz#d3aa650d11678ca76ce294071fda53d7804183b3" + integrity sha512-sfHYkLGjhzWTq6xsuQ01oEsUYjkHRux9fW1iUA68dC7Qd8BS1Unq4aZ8itmQp95zUzIcyR2EbNMTzAicFj+guw== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-polyfill-corejs2 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-regenerator "^0.2.2" + semver "^6.3.0" + "@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz#97f13855f1409338d8cadcbaca670ad79e091a58" @@ -1411,6 +1423,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.15.4", "@babel/runtime@^7.6.2": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" @@ -1418,13 +1437,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.6.2": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" - integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/template@^7.12.7", "@babel/template@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -1481,7 +1493,7 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@babel/types@^7.15.4", "@babel/types@^7.15.6": +"@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.4": version "7.15.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== @@ -4374,7 +4386,7 @@ babel-plugin-react-docgen@^4.2.1: lodash "^4.17.15" react-docgen "^5.0.0" -"babel-plugin-styled-components@>= 1", "babel-plugin-styled-components@>= 1.12.0": +"babel-plugin-styled-components@>= 1", "babel-plugin-styled-components@>= 1.12.0", babel-plugin-styled-components@^1.13.2: version "1.13.2" resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.2.tgz#ebe0e6deff51d7f93fceda1819e9b96aeb88278d" integrity sha512-Vb1R3d4g+MUfPQPVDMCGjm3cDocJEUTR7Xq7QS95JWWeksN1wdFRYpD2kulDgI3Huuaf1CZd+NK4KQmqUFh5dA== @@ -4389,6 +4401,14 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= +babel-plugin-transform-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-imports/-/babel-plugin-transform-imports-2.0.0.tgz#9e5f49f751a9d34ba8f4bb988c7e48ed2419c6b6" + integrity sha512-65ewumYJ85QiXdcB/jmiU0y0jg6eL6CdnDqQAqQ8JMOKh1E52VPG3NJzbVKWcgovUR5GBH8IWpCXQ7I8Q3wjgw== + dependencies: + "@babel/types" "^7.4" + is-valid-path "^0.1.1" + bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -8577,6 +8597,13 @@ is-installed-globally@^0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-invalid-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-invalid-path/-/is-invalid-path-0.1.0.tgz#307a855b3cf1a938b44ea70d2c61106053714f34" + integrity sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ= + dependencies: + is-glob "^2.0.0" + is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -8697,6 +8724,13 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-valid-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-valid-path/-/is-valid-path-0.1.1.tgz#110f9ff74c37f663e1ec7915eb451f2db93ac9df" + integrity sha1-EQ+f90w39mPh7HkV60UfLbk6yd8= + dependencies: + is-invalid-path "^0.1.0" + is-whitespace-character@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" @@ -11872,9 +11906,9 @@ shadow-cljs-jar@1.3.2: integrity sha512-XmeffAZHv8z7451kzeq9oKh8fh278Ak+UIOGGrapyqrFBB773xN8vMQ3O7J7TYLnb9BUwcqadKkmgaq7q6fhZg== shadow-cljs@^2.15.3: - version "2.15.3" - resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.15.3.tgz#11a0a04f1c31d9277838a5f649457edbb06baafb" - integrity sha512-KK7G9kSc0dwrOkN74o5yrxhrpsJ3BJCL11nGHBakzHLodc7pdiCLDeq2ChXyKl2yu9aJIzfSz8Hum38wpLQ1DA== + version "2.15.9" + resolved "https://registry.yarnpkg.com/shadow-cljs/-/shadow-cljs-2.15.9.tgz#cb256a9af12c3df1f0b2bbef344e365dab74b519" + integrity sha512-t2KrrMvJZtUFf2xIAL+OL76ahYHPT8VAKxhDic3kloTgOYfZpHRm/S/3C0iW982U3ZJdLUZxmBeluH4Q7564dg== dependencies: node-libs-browser "^2.2.1" readline-sync "^1.4.7" @@ -13541,9 +13575,9 @@ write-file-atomic@^3.0.0: typedarray-to-buffer "^3.1.5" ws@^7.4.6: - version "7.5.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" - integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== + version "7.5.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== ws@~7.4.2: version "7.4.6" From ee20473c4e7b2eb188db6e6b4103513943ea7266 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Wed, 15 Sep 2021 14:30:11 +0100 Subject: [PATCH 2/5] fix: remove duplicated button file --- src/js/components/Button/Button.tsx | 123 ---------------------------- 1 file changed, 123 deletions(-) delete mode 100644 src/js/components/Button/Button.tsx diff --git a/src/js/components/Button/Button.tsx b/src/js/components/Button/Button.tsx deleted file mode 100644 index ba564eade2..0000000000 --- a/src/js/components/Button/Button.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; - -const StyledButton = styled.button` - cursor: pointer; - padding: 0.375rem 0.625rem; - margin: 0; - font-family: inherit; - font-size: inherit; - border-radius: 0.25rem; - font-weight: 500; - border: none; - display: inline-flex; - align-items: center; - color: var(--body-text-color); - background-color: transparent; - transition-property: filter, background, color, opacity; - transition-duration: 0.075s; - transition-timing-function: ease; - - &:hover { - background: var(--body-text-color---opacity-lower); - } - - &:active, - &:hover:active, - &[aria-pressed="true"] { - color: var(--body-text-color); - background: var(--body-text-color---opacity-lower); - } - - &:active, - &:hover:active, - &:active[aria-pressed="true"] { - background: var(--body-text-color---opacity-low); - } - - &:disabled, - &:disabled:active { - color: var(--body-text-color---opacity-low); - background: var(--body-text-color---opacity-lower); - cursor: default; - } - - span { - flex: 1 0 auto; - text-align: left; - } - - kbd { - margin-inline-start: 1rem; - font-size: 85%; - } - - > svg { - margin: -0.0835em -0.325rem; - - &:not(:first-child) { - margin-left: 0.251em; - } - &:not(:last-child) { - margin-right: 0.251em; - } - } - - &.is-primary { - color: var(--link-color); - background: var(--link-color---opacity-lower); - - &:hover { - background: var(--link-color---opacity-low); - } - - &:active, - &:hover:active, - &[aria-pressed="true"] { - color: white; - background: var(--link-color); - } - - &:disabled, - &:disabled:active { - color: var(--body-text-color---opacity-low); - background: var(--body-text-color---opacity-lower); - cursor: default; - } - } -`; - -export interface ButtonProps { - /** - * Is this the principal call to action on the page? - */ - isPrimary?: boolean; - /** - * Is this the principal call to action on the page? - */ - isPressed?: boolean; -} - -/** - * Primary UI component for user interaction - */ -export const Button: React.FC = ({ - children, - isPrimary, - isPressed, - ...props -}) => { - return ( - - {children} - - ); -}; From 26d3259ca4c420c0757954518af426503fd39d2b Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Thu, 16 Sep 2021 12:15:38 +0100 Subject: [PATCH 3/5] feat: integrate page level presence --- .../athens/self_hosted/presence/events.cljs | 6 + .../athens/self_hosted/presence/subs.cljs | 14 +- .../athens/self_hosted/presence/views.cljs | 219 +++++------------- src/cljs/athens/views/app_toolbar.cljs | 7 +- src/cljs/athens/views/pages/settings.cljs | 22 +- .../PresenceDetails/PresenceDetails.tsx | 8 +- 6 files changed, 82 insertions(+), 194 deletions(-) diff --git a/src/cljs/athens/self_hosted/presence/events.cljs b/src/cljs/athens/self_hosted/presence/events.cljs index b434da079b..d1cd89d536 100644 --- a/src/cljs/athens/self_hosted/presence/events.cljs +++ b/src/cljs/athens/self_hosted/presence/events.cljs @@ -43,6 +43,12 @@ (update-in [:presence :users new-username] assoc :username new-username)))) +(rf/reg-event-db + :presence/update-color + (fn [db [_ username color]] + (assoc-in db [:presence :users username :color] color))) + + (rf/reg-event-fx :presence/send-rename (fn [_ [_ current-username new-username]] diff --git a/src/cljs/athens/self_hosted/presence/subs.cljs b/src/cljs/athens/self_hosted/presence/subs.cljs index 31e5e81450..d128640146 100644 --- a/src/cljs/athens/self_hosted/presence/subs.cljs +++ b/src/cljs/athens/self_hosted/presence/subs.cljs @@ -21,6 +21,18 @@ users)))) +(rf/reg-sub + :presence/current-user + :<- [:presence/users-with-page-data] + :<- [:settings] + (fn [[users settings] [_]] + (-> (filter (fn [[_ user]] + (= (:username settings) (:username user))) + users) + first + second))) + + (rf/reg-sub :presence/same-page :<- [:presence/users-with-page-data] @@ -34,7 +46,7 @@ (= current-route-uid (:page/uid user))) users)) - []))) + {}))) (rf/reg-sub diff --git a/src/cljs/athens/self_hosted/presence/views.cljs b/src/cljs/athens/self_hosted/presence/views.cljs index a95f33a014..04eaca6813 100644 --- a/src/cljs/athens/self_hosted/presence/views.cljs +++ b/src/cljs/athens/self_hosted/presence/views.cljs @@ -1,20 +1,14 @@ (ns athens.self-hosted.presence.views (:require - ["/components/Button/Button" :refer [Button]] - ["@material-ui/core/Popover" :as Popover] - ["@material-ui/icons/Link" :default Link] + ["/components/PresenceDetails/PresenceDetails" :refer [PresenceDetails]] [athens.self-hosted.presence.events] [athens.self-hosted.presence.fx] [athens.self-hosted.presence.subs] - [athens.style :as style] [re-frame.core :as rf] [reagent.core :as r] [stylefy.core :as stylefy :refer [use-style]])) -(def m-popover (r/adapt-react-class (.-default Popover))) - - ;; Avatar @@ -59,177 +53,68 @@ initials]]))) -(def ^:private avatar-stack-style - {:display "flex" - ::stylefy/manual [[:svg {:width "1.5rem" - :height "1.5rem"} - ;; In a stack, each sequential item sucks in the spacing - ;; from the item before it - ["&:not(:first-child)" {:margin-left "-0.8rem"}] - ;; All but the last get a slice masked out for readability - ;; - ;; I'm not clear on why 1.55rem / 1.1rem work in this case - ;; It'd be nice to have a simpler masking method - ;; or a better-constructed string with some documentation - ["&:not(:last-child)" {:mask-image "radial-gradient(1.55rem 1.1rem at 160% 50%, transparent calc(96%), #000 100%)" - :-webkit-mask-image "radial-gradient(1.55rem 1.1rem at 160% 50%, transparent calc(96%), #000 100%)"}]]]}) - - -(defn- avatar-stack-el - [& children] - [:div (use-style avatar-stack-style) - children]) +(defn user->person + [{:keys [username color]}] + ;; TODO: have a real notion of user-id, not just username. + {:personId username + :username username + :color color}) -;; List +(defn copy-host-address-to-clipboard + [host-address] + (.. js/navigator -clipboard (writeText host-address)) + (rf/dispatch [:show-snack-msg {:msg "Host address copied to clipboard" + :type :success}])) -(defn- list-el - [& children] - [:ul (use-style {:padding 0 - :margin 0 - :display "flex" - :flex-direction "column" - :list-style "none"}) - children]) +(defn go-to-user-block + [all-users js-person] + (let [{_block-uid :block/uid + page-uid :page/uid} + (->> (js->clj js-person :keywordize-keys true) + :username + (get all-users))] + (rf/dispatch (if page-uid + ;; TODO: if we support navigating to a block, it should be added here. + [:navigate :page {:id page-uid}] + [:show-snack-msg {:msg "User is not on any page" + :type :success}])))) -(defn- list-header-el - [& children] - [:header (use-style {:border-bottom "1px solid #ddd" - :padding "0.25rem 0.5rem" - :display "flex" - :justify-content "space-between" - :align-items "center"}) - children]) - -(defn- list-section-header-el - [& children] - [:li (use-style {:font-size "12px" - :font-weight "bold" - :opacity "0.5" - :padding "1rem 1rem 0.25rem"}) - children]) - - -(defn- list-header-url-el - [& children] - [:span (use-style {:font-size "12px" - :font-weight "700" - :display "inline-block" - :opacity "0.5" - :padding "0.5rem" - :user-select "all" - :margin-right "1em" - :flex "1 1 100%" - :white-space "nowrap" - :text-overflow "hidden"}) - children]) - - -(defn- list-separator-el - [] - [:li (use-style {:margin "0.5rem 0 0.5rem 1rem" - :border-bottom "1px solid #ddd"})]) - - -(def ^:private member-list-item-style - {:padding "0.375rem 1rem" - :display "flex" - :font-size "14px" - :align-items "center" - :font-weight "600" - :color (style/color :body-text-color :opacity-higher) - :transition "backdrop-filter 0.1s ease" - :cursor "default" - ::stylefy/manual [[:svg {:margin-right "0.25rem"}]]}) - - -;; turn off interactive button stylings until we implement interactions like "jump" or "follow" -;; [:&:hover {:background (style/color :body-text-color :opacity-lower)}] -;; [:&:active -;; :&:hover:active -;; :&.is-active {:color (style/color :body-text-color) -;; :background (style/color :body-text-color :opacity-lower)}] -;; [:&:active -;; :&:hover:active -;; :&:active.is-active {:background (style/color :body-text-color :opacity-low)}] -;; [:&:disabled :&:disabled:active {:color (style/color :body-text-color :opacity-low) -;; :background (style/color :body-text-color :opacity-lower) -;; :cursor "default"}]]}) - - - -(defn- member-item-el - [user props] - [:li (use-style member-list-item-style #_{:on-click #(prn user)}) - [avatar-el user props] - (:username user)]) +(defn edit-current-user + [current-username js-person] + (let [{:keys [username color]} (js->clj js-person :keywordize-keys true)] + (rf/dispatch [:settings/update :username username]) + ;; Change the color of the old name immediately, then wait for the + ;; rename to happen in the server. + (rf/dispatch [:presence/update-color current-username color]) + (rf/dispatch [:presence/send-rename current-username username]))) ;; Exports + (defn toolbar-presence-el [] - (r/with-let [ele (r/atom nil)] - (let [users (rf/subscribe [:presence/users-with-page-data]) - same-page-users (rf/subscribe [:presence/same-page]) - diff-page-users (rf/subscribe [:presence/diff-page]) - current-route-name (rf/subscribe [:current-route/name])] - [:<> - - ;; Preview - [:> Button {:on-click #(reset! ele (.-currentTarget %))} - [avatar-stack-el - (cond - - (= @current-route-name :page) - [:<> - ;; same page - (for [[username user] @same-page-users] - ^{:key username} - [avatar-el user {:filled true}]) - ;; diff page but online - (for [[username user] @diff-page-users] - ^{:key username} - [avatar-el user {:filled false}])] - - ;; TODO: capture what page user is scrolled to on Daily Notes - ;; (= @current-route-name :home) - ;; [:div "TODO"] - - ;; default to showing all users - :else (for [[username user] @users] - ^{:key username} - [avatar-el user {:filled false}]))]] - - ;; Dropdown - [m-popover - {:open (boolean @ele) - :anchorEl @ele - :onClose #(reset! ele nil) - :anchorOrigin #js{:vertical "bottom" - :horizontal "center"} - :transformOrigin #js{:vertical "top" - :horizontal "center"}} - [list-header-el - [list-header-url-el "ath.ns/34op5fds0a"] - [:> Button [:> Link]]] - - [list-el - ;; On same page - - (when-not (empty? @same-page-users) - [:<> - [list-section-header-el "On This Page"] - (for [[username user] @same-page-users] - ^{:key username} - [member-item-el user {:filled true}]) - [list-separator-el]]) - - ;; Online, different page - (for [[username user] @diff-page-users] - ^{:key username} - [member-item-el user {:filled false}])]]]))) + (r/with-let [selected-db (rf/subscribe [:db-picker/selected-db]) + current-user (rf/subscribe [:presence/current-user]) + all-users (rf/subscribe [:presence/users-with-page-data]) + same-page (rf/subscribe [:presence/same-page]) + diff-page (rf/subscribe [:presence/diff-page]) + others-seq #(->> (dissoc % (:username @current-user)) + vals + (map user->person)) + current-page-members (others-seq @same-page) + different-page-members (others-seq @diff-page)] + [:> PresenceDetails {:current-user (user->person @current-user) + :current-page-members current-page-members + :different-page-members different-page-members + :host-address (:url @selected-db) + :handle-press-host-address copy-host-address-to-clipboard + :handle-press-member #(go-to-user-block @all-users %) + :handle-update-profile #(edit-current-user (:username @current-user) %) + ;; TODO: show other states when we support them. + :connection-status "connected"}])) ;; inline diff --git a/src/cljs/athens/views/app_toolbar.cljs b/src/cljs/athens/views/app_toolbar.cljs index 8ae88cc72f..1eff2db8a8 100644 --- a/src/cljs/athens/views/app_toolbar.cljs +++ b/src/cljs/athens/views/app_toolbar.cljs @@ -16,7 +16,9 @@ ["@material-ui/icons/VerticalSplit" :default VerticalSplit] [athens.electron.db-menu.core :refer [db-menu]] [athens.electron.db-modal :as db-modal] + [athens.electron.utils :as electron.utils] [athens.router :as router] + [athens.self-hosted.presence.views :refer [toolbar-presence-el]] [athens.style :refer [color unzoom]] [athens.subs] [athens.util :as util :refer [app-classes]] @@ -181,7 +183,8 @@ win-fullscreen? (if electron? (subscribe [:win-fullscreen?]) (r/atom false)) - merge-open? (reagent.core/atom false)] + merge-open? (reagent.core/atom false) + selected-db (subscribe [:db-picker/selected-db])] (fn [] [:<> (when @merge-open? @@ -228,6 +231,8 @@ [:div (use-style app-header-secondary-controls-style) (if electron? [:<> + (when (electron.utils/remote-db? @selected-db) + [toolbar-presence-el]) [:> Button {:on-click #(swap! merge-open? not) :title "Merge Roam Database"} [:> MergeType]] diff --git a/src/cljs/athens/views/pages/settings.cljs b/src/cljs/athens/views/pages/settings.cljs index 907cb59567..65b1a689c1 100644 --- a/src/cljs/athens/views/pages/settings.cljs +++ b/src/cljs/athens/views/pages/settings.cljs @@ -4,7 +4,6 @@ ["@material-ui/icons/Check" :default Check] ["@material-ui/icons/NotInterested" :default NotInterested] [athens.db :refer [default-athens-persist]] - [athens.util :refer [js-event->val]] [athens.views.textinput :as textinput] [athens.views.toggle-switch :as toggle-switch] [cljs-http.client :as http] @@ -216,22 +215,6 @@ [:div (stylefy/use-style settings-page-styles) child]) -(defn remote-username-comp - [username update-fn] - [setting-wrapper - [:<> - [:header - [:h3 "Username"] - [:span.glance username]] - [:main - [textinput/textinput {:type "text" - :placeholder "Username" - :on-blur #(update-fn username (js-event->val %)) - :defaultValue username}] - [:aside - [:p "For now, a username is only needed if you are connected to a server."]]]]]) - - (defn reset-settings-comp [reset-fn] [setting-wrapper @@ -262,7 +245,7 @@ (defn page [] - (let [{:keys [email username monitoring backup-time]} @(subscribe [:settings])] + (let [{:keys [email monitoring backup-time]} @(subscribe [:settings])] [settings-container [:<> [:h1 "Settings"] @@ -272,7 +255,4 @@ (dispatch [:settings/update :backup-time x]) (dispatch [:fs/update-write-db]))] [remote-backups-comp] - [remote-username-comp username (fn [current-username new-username] - (dispatch [:presence/send-rename current-username new-username]) - (dispatch [:settings/update :username new-username]))] [reset-settings-comp #(dispatch [:settings/reset])]]])) diff --git a/src/js/components/PresenceDetails/PresenceDetails.tsx b/src/js/components/PresenceDetails/PresenceDetails.tsx index 6d4157750c..6264c7a275 100644 --- a/src/js/components/PresenceDetails/PresenceDetails.tsx +++ b/src/js/components/PresenceDetails/PresenceDetails.tsx @@ -154,7 +154,7 @@ export const PresenceDetails = ({ const [isUserSettingsDialogOpen, setIsUserSettingsDialogOpen] = React.useState(false); const [timeLastOnline, setTimeLastOnline] = React.useState(null); - const showablePersons = [...currentPageMembers, ...differentPageMembers]; + const showablePersons = [currentUser, ...currentPageMembers, ...differentPageMembers]; React.useEffect(() => { if (connectionStatus === 'offline') { @@ -222,7 +222,7 @@ export const PresenceDetails = ({ {hostAddress && (<> - @@ -253,7 +253,7 @@ export const PresenceDetails = ({ On this page {currentPageMembers.map(member => -