@peers-app/peers-ui 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/README.md +52 -0
- package/.github/workflows/publish.yml +45 -0
- package/babel.config.js +7 -0
- package/dist/app.d.ts +9 -0
- package/dist/app.js +54 -0
- package/dist/command-palette/command-palette-ui.d.ts +2 -0
- package/dist/command-palette/command-palette-ui.js +192 -0
- package/dist/command-palette/command-palette.d.ts +23 -0
- package/dist/command-palette/command-palette.js +371 -0
- package/dist/components/checkbox.d.ts +7 -0
- package/dist/components/checkbox.js +20 -0
- package/dist/components/group-switcher.d.ts +6 -0
- package/dist/components/group-switcher.js +301 -0
- package/dist/components/input-date.d.ts +7 -0
- package/dist/components/input-date.js +19 -0
- package/dist/components/input-datetime.d.ts +7 -0
- package/dist/components/input-datetime.js +35 -0
- package/dist/components/input-number.d.ts +9 -0
- package/dist/components/input-number.js +87 -0
- package/dist/components/input.d.ts +7 -0
- package/dist/components/input.js +20 -0
- package/dist/components/io-schema-values.d.ts +15 -0
- package/dist/components/io-schema-values.js +105 -0
- package/dist/components/io-schema.d.ts +13 -0
- package/dist/components/io-schema.js +163 -0
- package/dist/components/lazy-list.d.ts +13 -0
- package/dist/components/lazy-list.js +91 -0
- package/dist/components/lazy-sortable-list.d.ts +29 -0
- package/dist/components/lazy-sortable-list.js +12 -0
- package/dist/components/left-bar.d.ts +3 -0
- package/dist/components/left-bar.js +130 -0
- package/dist/components/list-screen.d.ts +16 -0
- package/dist/components/list-screen.js +100 -0
- package/dist/components/loading-indicator.d.ts +2 -0
- package/dist/components/loading-indicator.js +12 -0
- package/dist/components/main-content-container.d.ts +2 -0
- package/dist/components/main-content-container.js +90 -0
- package/dist/components/markdown-editor/autolink-plugin.d.ts +2 -0
- package/dist/components/markdown-editor/autolink-plugin.js +29 -0
- package/dist/components/markdown-editor/editor-inline.d.ts +3 -0
- package/dist/components/markdown-editor/editor-inline.js +13 -0
- package/dist/components/markdown-editor/editor.d.ts +18 -0
- package/dist/components/markdown-editor/editor.js +143 -0
- package/dist/components/markdown-editor/markdown-plugin.d.ts +9 -0
- package/dist/components/markdown-editor/markdown-plugin.js +194 -0
- package/dist/components/markdown-editor/mention-node.d.ts +21 -0
- package/dist/components/markdown-editor/mention-node.js +160 -0
- package/dist/components/markdown-editor/mentions-plugin.d.ts +7 -0
- package/dist/components/markdown-editor/mentions-plugin.js +268 -0
- package/dist/components/markdown-editor/theme.d.ts +46 -0
- package/dist/components/markdown-editor/theme.js +48 -0
- package/dist/components/markdown-editor/toolbar.d.ts +10 -0
- package/dist/components/markdown-editor/toolbar.js +112 -0
- package/dist/components/markdown-with-mentions.d.ts +4 -0
- package/dist/components/markdown-with-mentions.js +140 -0
- package/dist/components/message-logs/message-logs.d.ts +6 -0
- package/dist/components/message-logs/message-logs.js +307 -0
- package/dist/components/messages/avatar.d.ts +10 -0
- package/dist/components/messages/avatar.js +65 -0
- package/dist/components/messages/channel-message-list.d.ts +14 -0
- package/dist/components/messages/channel-message-list.js +158 -0
- package/dist/components/messages/channel-view.d.ts +6 -0
- package/dist/components/messages/channel-view.js +82 -0
- package/dist/components/messages/message-compose.d.ts +11 -0
- package/dist/components/messages/message-compose.js +152 -0
- package/dist/components/messages/message-display.d.ts +10 -0
- package/dist/components/messages/message-display.js +152 -0
- package/dist/components/messages/thread-message-list.d.ts +11 -0
- package/dist/components/messages/thread-message-list.js +122 -0
- package/dist/components/messages/thread-view.d.ts +6 -0
- package/dist/components/messages/thread-view.js +174 -0
- package/dist/components/off-canvas.d.ts +13 -0
- package/dist/components/off-canvas.js +89 -0
- package/dist/components/router.d.ts +6 -0
- package/dist/components/router.js +240 -0
- package/dist/components/save-button.d.ts +13 -0
- package/dist/components/save-button.js +75 -0
- package/dist/components/sortable-list.d.ts +36 -0
- package/dist/components/sortable-list.js +77 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +69 -0
- package/dist/components/text-list-editor.tsx/text-list-editor.d.ts +6 -0
- package/dist/components/text-list-editor.tsx/text-list-editor.js +13 -0
- package/dist/components/tooltip.d.ts +11 -0
- package/dist/components/tooltip.js +20 -0
- package/dist/components/top-bar.d.ts +2 -0
- package/dist/components/top-bar.js +51 -0
- package/dist/components/typeahead/mentions-plugin.d.ts +7 -0
- package/dist/components/typeahead/mentions-plugin.js +203 -0
- package/dist/components/typeahead/typeahead-editor.d.ts +15 -0
- package/dist/components/typeahead/typeahead-editor.js +134 -0
- package/dist/components/typeahead/typeahead.d.ts +12 -0
- package/dist/components/typeahead/typeahead.js +94 -0
- package/dist/components/typeahead.d.ts +22 -0
- package/dist/components/typeahead.js +270 -0
- package/dist/globals.d.ts +29 -0
- package/dist/globals.js +148 -0
- package/dist/hooks.d.ts +34 -0
- package/dist/hooks.js +137 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -0
- package/dist/layout-vars.d.ts +6 -0
- package/dist/layout-vars.js +10 -0
- package/dist/mention-configs.d.ts +18 -0
- package/dist/mention-configs.js +149 -0
- package/dist/screens/assistants/assistant-config.d.ts +5 -0
- package/dist/screens/assistants/assistant-config.js +52 -0
- package/dist/screens/assistants/assistant-details.d.ts +4 -0
- package/dist/screens/assistants/assistant-details.js +85 -0
- package/dist/screens/assistants/assistant-info.d.ts +6 -0
- package/dist/screens/assistants/assistant-info.js +28 -0
- package/dist/screens/assistants/assistant-list.d.ts +2 -0
- package/dist/screens/assistants/assistant-list.js +114 -0
- package/dist/screens/assistants/assistant-tools.d.ts +5 -0
- package/dist/screens/assistants/assistant-tools.js +38 -0
- package/dist/screens/contacts/contact-details.d.ts +6 -0
- package/dist/screens/contacts/contact-details.js +100 -0
- package/dist/screens/contacts/contact-list.d.ts +2 -0
- package/dist/screens/contacts/contact-list.js +213 -0
- package/dist/screens/contacts/index.d.ts +4 -0
- package/dist/screens/contacts/index.js +21 -0
- package/dist/screens/events/cron.d.ts +3 -0
- package/dist/screens/events/cron.js +77 -0
- package/dist/screens/events/event-details.d.ts +6 -0
- package/dist/screens/events/event-details.js +112 -0
- package/dist/screens/events/event-handlers.d.ts +7 -0
- package/dist/screens/events/event-handlers.js +84 -0
- package/dist/screens/events/event-info.d.ts +5 -0
- package/dist/screens/events/event-info.js +19 -0
- package/dist/screens/events/event-list.d.ts +2 -0
- package/dist/screens/events/event-list.js +107 -0
- package/dist/screens/events/event-schedule.d.ts +5 -0
- package/dist/screens/events/event-schedule.js +124 -0
- package/dist/screens/groups/group-details.d.ts +6 -0
- package/dist/screens/groups/group-details.js +218 -0
- package/dist/screens/groups/group-list.d.ts +2 -0
- package/dist/screens/groups/group-list.js +275 -0
- package/dist/screens/groups/group-members.d.ts +8 -0
- package/dist/screens/groups/group-members.js +315 -0
- package/dist/screens/groups/index.d.ts +6 -0
- package/dist/screens/groups/index.js +23 -0
- package/dist/screens/knowledge/knowledge-frame-details.bk.d.ts +6 -0
- package/dist/screens/knowledge/knowledge-frame-details.bk.js +84 -0
- package/dist/screens/knowledge/knowledge-frame-details.d.ts +8 -0
- package/dist/screens/knowledge/knowledge-frame-details.js +143 -0
- package/dist/screens/knowledge/knowledge-frame-list.d.ts +2 -0
- package/dist/screens/knowledge/knowledge-frame-list.js +45 -0
- package/dist/screens/knowledge/knowledge-value-details.d.ts +6 -0
- package/dist/screens/knowledge/knowledge-value-details.js +150 -0
- package/dist/screens/knowledge/knowledge-value-list-item.d.ts +5 -0
- package/dist/screens/knowledge/knowledge-value-list-item.js +39 -0
- package/dist/screens/knowledge/knowledge-value-list.d.ts +3 -0
- package/dist/screens/knowledge/knowledge-value-list.js +123 -0
- package/dist/screens/packages/package-details.d.ts +6 -0
- package/dist/screens/packages/package-details.js +82 -0
- package/dist/screens/packages/package-info.d.ts +5 -0
- package/dist/screens/packages/package-info.js +42 -0
- package/dist/screens/packages/package-list.d.ts +2 -0
- package/dist/screens/packages/package-list.js +182 -0
- package/dist/screens/packages/package-new-local.d.ts +2 -0
- package/dist/screens/packages/package-new-local.js +82 -0
- package/dist/screens/peer-types/peer-type-details.d.ts +10 -0
- package/dist/screens/peer-types/peer-type-details.js +126 -0
- package/dist/screens/peer-types/peer-type-list.d.ts +2 -0
- package/dist/screens/peer-types/peer-type-list.js +57 -0
- package/dist/screens/predicates/predicate-details.d.ts +6 -0
- package/dist/screens/predicates/predicate-details.js +103 -0
- package/dist/screens/predicates/predicate-list.d.ts +2 -0
- package/dist/screens/predicates/predicate-list.js +46 -0
- package/dist/screens/profile.d.ts +2 -0
- package/dist/screens/profile.js +66 -0
- package/dist/screens/search/global-search.d.ts +2 -0
- package/dist/screens/search/global-search.js +186 -0
- package/dist/screens/settings/color-mode-dropdown.d.ts +6 -0
- package/dist/screens/settings/color-mode-dropdown.js +63 -0
- package/dist/screens/settings/settings-page.d.ts +2 -0
- package/dist/screens/settings/settings-page.js +49 -0
- package/dist/screens/setup-user.d.ts +2 -0
- package/dist/screens/setup-user.js +270 -0
- package/dist/screens/tools/tool-code.d.ts +5 -0
- package/dist/screens/tools/tool-code.js +32 -0
- package/dist/screens/tools/tool-details.d.ts +6 -0
- package/dist/screens/tools/tool-details.js +68 -0
- package/dist/screens/tools/tool-info.d.ts +5 -0
- package/dist/screens/tools/tool-info.js +74 -0
- package/dist/screens/tools/tool-list.d.ts +2 -0
- package/dist/screens/tools/tool-list.js +123 -0
- package/dist/screens/tools/tool-schema.d.ts +5 -0
- package/dist/screens/tools/tool-schema.js +30 -0
- package/dist/screens/tools/tool-test-details.d.ts +4 -0
- package/dist/screens/tools/tool-test-details.js +54 -0
- package/dist/screens/tools/tool-test-list.d.ts +4 -0
- package/dist/screens/tools/tool-test-list.js +82 -0
- package/dist/screens/variables/variable-details.d.ts +6 -0
- package/dist/screens/variables/variable-details.js +140 -0
- package/dist/screens/variables/variable-list.d.ts +2 -0
- package/dist/screens/variables/variable-list.js +58 -0
- package/dist/screens/workflows/workflow-details.d.ts +6 -0
- package/dist/screens/workflows/workflow-details.js +122 -0
- package/dist/screens/workflows/workflow-info.d.ts +5 -0
- package/dist/screens/workflows/workflow-info.js +18 -0
- package/dist/screens/workflows/workflow-instructions.d.ts +5 -0
- package/dist/screens/workflows/workflow-instructions.js +118 -0
- package/dist/screens/workflows/workflow-list.d.ts +2 -0
- package/dist/screens/workflows/workflow-list.js +109 -0
- package/dist/screens/workflows/workflow-subscriptions.d.ts +6 -0
- package/dist/screens/workflows/workflow-subscriptions.js +81 -0
- package/dist/setupTests.d.ts +1 -0
- package/dist/setupTests.js +31 -0
- package/dist/system-apps/assistants.app.d.ts +2 -0
- package/dist/system-apps/assistants.app.js +8 -0
- package/dist/system-apps/contacts.app.d.ts +2 -0
- package/dist/system-apps/contacts.app.js +9 -0
- package/dist/system-apps/events.app.d.ts +2 -0
- package/dist/system-apps/events.app.js +8 -0
- package/dist/system-apps/groups.app.d.ts +2 -0
- package/dist/system-apps/groups.app.js +9 -0
- package/dist/system-apps/index.d.ts +19 -0
- package/dist/system-apps/index.js +90 -0
- package/dist/system-apps/knowledge-frames.app.d.ts +2 -0
- package/dist/system-apps/knowledge-frames.app.js +9 -0
- package/dist/system-apps/knowledge-values.app.d.ts +2 -0
- package/dist/system-apps/knowledge-values.app.js +9 -0
- package/dist/system-apps/packages.app.d.ts +2 -0
- package/dist/system-apps/packages.app.js +8 -0
- package/dist/system-apps/predicates.app.d.ts +2 -0
- package/dist/system-apps/predicates.app.js +8 -0
- package/dist/system-apps/profile.app.d.ts +2 -0
- package/dist/system-apps/profile.app.js +8 -0
- package/dist/system-apps/search.app.d.ts +2 -0
- package/dist/system-apps/search.app.js +9 -0
- package/dist/system-apps/settings.app.d.ts +2 -0
- package/dist/system-apps/settings.app.js +8 -0
- package/dist/system-apps/threads.app.d.ts +2 -0
- package/dist/system-apps/threads.app.js +8 -0
- package/dist/system-apps/tools.app.d.ts +2 -0
- package/dist/system-apps/tools.app.js +8 -0
- package/dist/system-apps/types.app.d.ts +2 -0
- package/dist/system-apps/types.app.js +8 -0
- package/dist/system-apps/variables.app.d.ts +2 -0
- package/dist/system-apps/variables.app.js +8 -0
- package/dist/system-apps/workflows.app.d.ts +2 -0
- package/dist/system-apps/workflows.app.js +8 -0
- package/dist/tabs-layout/tabs-layout.d.ts +5 -0
- package/dist/tabs-layout/tabs-layout.js +374 -0
- package/dist/tabs-layout/tabs-state.d.ts +26 -0
- package/dist/tabs-layout/tabs-state.js +239 -0
- package/dist/three-bar-layout/left-bar-content.d.ts +7 -0
- package/dist/three-bar-layout/left-bar-content.js +151 -0
- package/dist/three-bar-layout/right-bar-content.d.ts +2 -0
- package/dist/three-bar-layout/right-bar-content.js +64 -0
- package/dist/three-bar-layout/three-bar-layout.d.ts +5 -0
- package/dist/three-bar-layout/three-bar-layout.js +218 -0
- package/dist/ui-defaults/index.d.ts +2 -0
- package/dist/ui-defaults/index.js +4 -0
- package/dist/ui-defaults/list-screen.d.ts +6 -0
- package/dist/ui-defaults/list-screen.js +74 -0
- package/dist/ui-defaults/notes-editor.d.ts +7 -0
- package/dist/ui-defaults/notes-editor.js +41 -0
- package/dist/ui-router/routes-loader.d.ts +25 -0
- package/dist/ui-router/routes-loader.js +97 -0
- package/dist/ui-router/ui-loader.d.ts +18 -0
- package/dist/ui-router/ui-loader.js +481 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.js +250 -0
- package/docs/conversation-tab.md +201 -0
- package/docs/getting-started.md +284 -0
- package/docs/knowledge.md +187 -0
- package/docs/tabs-ui.md +696 -0
- package/docs/user-contacts-ui.md +384 -0
- package/jest.config.js +25 -0
- package/package.json +109 -0
- package/src/app.tsx +59 -0
- package/src/command-palette/command-palette-ui.tsx +264 -0
- package/src/command-palette/command-palette.ts +364 -0
- package/src/components/checkbox.tsx +22 -0
- package/src/components/group-switcher.tsx +469 -0
- package/src/components/input-date.tsx +28 -0
- package/src/components/input-datetime.tsx +41 -0
- package/src/components/input-number.tsx +67 -0
- package/src/components/input.tsx +22 -0
- package/src/components/io-schema-values.tsx +122 -0
- package/src/components/io-schema.tsx +234 -0
- package/src/components/lazy-list.tsx +98 -0
- package/src/components/lazy-sortable-list.tsx +51 -0
- package/src/components/left-bar.tsx +264 -0
- package/src/components/list-screen.tsx +105 -0
- package/src/components/loading-indicator.tsx +9 -0
- package/src/components/main-content-container.tsx +76 -0
- package/src/components/markdown-editor/autolink-plugin.tsx +36 -0
- package/src/components/markdown-editor/editor-inline.tsx +10 -0
- package/src/components/markdown-editor/editor.tsx +152 -0
- package/src/components/markdown-editor/markdown-plugin.tsx +224 -0
- package/src/components/markdown-editor/mention-node.ts +199 -0
- package/src/components/markdown-editor/mentions-plugin.tsx +356 -0
- package/src/components/markdown-editor/theme.ts +47 -0
- package/src/components/markdown-editor/toolbar.tsx +263 -0
- package/src/components/markdown-with-mentions.tsx +183 -0
- package/src/components/message-logs/message-logs.tsx +406 -0
- package/src/components/messages/avatar.tsx +95 -0
- package/src/components/messages/channel-message-list.tsx +177 -0
- package/src/components/messages/channel-view.tsx +74 -0
- package/src/components/messages/message-compose.tsx +162 -0
- package/src/components/messages/message-display.tsx +217 -0
- package/src/components/messages/thread-message-list.tsx +126 -0
- package/src/components/messages/thread-view.tsx +214 -0
- package/src/components/off-canvas.tsx +83 -0
- package/src/components/router.tsx +224 -0
- package/src/components/save-button.tsx +109 -0
- package/src/components/sortable-list.tsx +102 -0
- package/src/components/tabs.tsx +70 -0
- package/src/components/text-list-editor.tsx/text-list-editor.tsx +13 -0
- package/src/components/tooltip.tsx +50 -0
- package/src/components/top-bar.tsx +119 -0
- package/src/components/typeahead/mentions-plugin.tsx +265 -0
- package/src/components/typeahead/typeahead-editor.tsx +140 -0
- package/src/components/typeahead/typeahead.tsx +77 -0
- package/src/components/typeahead.tsx +359 -0
- package/src/globals.tsx +162 -0
- package/src/hooks.ts +144 -0
- package/src/index.tsx +8 -0
- package/src/layout-vars.ts +8 -0
- package/src/mention-configs.ts +166 -0
- package/src/screens/assistants/assistant-config.tsx +80 -0
- package/src/screens/assistants/assistant-details.tsx +77 -0
- package/src/screens/assistants/assistant-info.tsx +45 -0
- package/src/screens/assistants/assistant-list.tsx +115 -0
- package/src/screens/assistants/assistant-tools.tsx +61 -0
- package/src/screens/contacts/contact-details.tsx +175 -0
- package/src/screens/contacts/contact-list.tsx +251 -0
- package/src/screens/contacts/index.ts +6 -0
- package/src/screens/events/cron.ts +74 -0
- package/src/screens/events/event-details.tsx +117 -0
- package/src/screens/events/event-handlers.tsx +61 -0
- package/src/screens/events/event-info.tsx +29 -0
- package/src/screens/events/event-list.tsx +104 -0
- package/src/screens/events/event-schedule.tsx +130 -0
- package/src/screens/groups/group-details.tsx +306 -0
- package/src/screens/groups/group-list.tsx +366 -0
- package/src/screens/groups/group-members.tsx +455 -0
- package/src/screens/groups/index.ts +9 -0
- package/src/screens/knowledge/knowledge-frame-details.bk.tsx +160 -0
- package/src/screens/knowledge/knowledge-frame-details.tsx +176 -0
- package/src/screens/knowledge/knowledge-frame-list.tsx +49 -0
- package/src/screens/knowledge/knowledge-value-details.tsx +181 -0
- package/src/screens/knowledge/knowledge-value-list-item.tsx +48 -0
- package/src/screens/knowledge/knowledge-value-list.tsx +131 -0
- package/src/screens/packages/package-details.tsx +117 -0
- package/src/screens/packages/package-info.tsx +83 -0
- package/src/screens/packages/package-list.tsx +191 -0
- package/src/screens/packages/package-new-local.tsx +93 -0
- package/src/screens/peer-types/peer-type-details.tsx +162 -0
- package/src/screens/peer-types/peer-type-list.tsx +74 -0
- package/src/screens/predicates/predicate-details.tsx +125 -0
- package/src/screens/predicates/predicate-list.tsx +50 -0
- package/src/screens/profile.tsx +68 -0
- package/src/screens/search/global-search.tsx +274 -0
- package/src/screens/settings/color-mode-dropdown.tsx +57 -0
- package/src/screens/settings/settings-page.tsx +76 -0
- package/src/screens/setup-user.tsx +367 -0
- package/src/screens/tools/tool-code.tsx +35 -0
- package/src/screens/tools/tool-details.tsx +101 -0
- package/src/screens/tools/tool-info.tsx +60 -0
- package/src/screens/tools/tool-list.tsx +121 -0
- package/src/screens/tools/tool-schema.tsx +42 -0
- package/src/screens/tools/tool-test-details.tsx +100 -0
- package/src/screens/tools/tool-test-list.tsx +74 -0
- package/src/screens/variables/variable-details.tsx +183 -0
- package/src/screens/variables/variable-list.tsx +74 -0
- package/src/screens/workflows/workflow-details.tsx +130 -0
- package/src/screens/workflows/workflow-info.tsx +29 -0
- package/src/screens/workflows/workflow-instructions.tsx +127 -0
- package/src/screens/workflows/workflow-list.tsx +107 -0
- package/src/screens/workflows/workflow-subscriptions.tsx +58 -0
- package/src/setupTests.ts +32 -0
- package/src/system-apps/assistants.app.ts +7 -0
- package/src/system-apps/contacts.app.ts +8 -0
- package/src/system-apps/events.app.ts +7 -0
- package/src/system-apps/groups.app.ts +8 -0
- package/src/system-apps/index.ts +79 -0
- package/src/system-apps/knowledge-frames.app.ts +8 -0
- package/src/system-apps/knowledge-values.app.ts +8 -0
- package/src/system-apps/packages.app.ts +7 -0
- package/src/system-apps/predicates.app.ts +7 -0
- package/src/system-apps/profile.app.ts +7 -0
- package/src/system-apps/search.app.ts +8 -0
- package/src/system-apps/settings.app.ts +7 -0
- package/src/system-apps/threads.app.ts +7 -0
- package/src/system-apps/tools.app.ts +7 -0
- package/src/system-apps/types.app.ts +7 -0
- package/src/system-apps/variables.app.ts +7 -0
- package/src/system-apps/workflows.app.ts +7 -0
- package/src/tabs-layout/tabs-layout.tsx +672 -0
- package/src/tabs-layout/tabs-state.ts +269 -0
- package/src/three-bar-layout/left-bar-content.tsx +202 -0
- package/src/three-bar-layout/right-bar-content.tsx +67 -0
- package/src/three-bar-layout/three-bar-layout.tsx +297 -0
- package/src/ui-defaults/index.ts +3 -0
- package/src/ui-defaults/list-screen.tsx +92 -0
- package/src/ui-defaults/notes-editor.tsx +51 -0
- package/src/ui-router/routes-loader.ts +98 -0
- package/src/ui-router/ui-loader.tsx +497 -0
- package/src/utils.ts +266 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// CSS for hover effects that work with both light and dark themes
|
|
4
|
+
const typeaheadStyles = `
|
|
5
|
+
.typeahead-item:not(.bg-primary):hover {
|
|
6
|
+
background-color: var(--bs-secondary-bg) !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-bs-theme="dark"] .typeahead-item:not(.bg-primary):hover {
|
|
10
|
+
background-color: var(--bs-dark) !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.typeahead-input-container {
|
|
14
|
+
position: relative;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
align-items: center;
|
|
18
|
+
min-height: 38px;
|
|
19
|
+
border: 1px solid var(--bs-border-color);
|
|
20
|
+
border-radius: 0.375rem;
|
|
21
|
+
background-color: var(--bs-body-bg);
|
|
22
|
+
padding: 4px 8px;
|
|
23
|
+
gap: 4px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.typeahead-input-container:focus-within {
|
|
27
|
+
border-color: #86b7fe;
|
|
28
|
+
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.typeahead-input {
|
|
32
|
+
border: none;
|
|
33
|
+
outline: none;
|
|
34
|
+
background: transparent;
|
|
35
|
+
flex: 1;
|
|
36
|
+
min-width: 120px;
|
|
37
|
+
height: 30px;
|
|
38
|
+
font-size: 1rem;
|
|
39
|
+
color: var(--bs-body-color);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.typeahead-input::placeholder {
|
|
43
|
+
color: var(--bs-secondary-color);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.typeahead-badge {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
font-size: 0.8rem;
|
|
50
|
+
padding: 2px 8px;
|
|
51
|
+
margin: 1px;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
export interface TypeaheadItem {
|
|
57
|
+
id: string;
|
|
58
|
+
[key: string]: any;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface TypeaheadProps<T extends TypeaheadItem> {
|
|
62
|
+
placeholder?: string;
|
|
63
|
+
searchFn: (query: string) => Promise<T[]>;
|
|
64
|
+
onSelectionChange: (selectedItems: T[]) => void;
|
|
65
|
+
renderItem: (item: T, isSelected: boolean) => React.ReactNode;
|
|
66
|
+
renderBadge?: (item: T) => React.ReactNode;
|
|
67
|
+
selectedItems?: T[];
|
|
68
|
+
multiSelect?: boolean;
|
|
69
|
+
maxSelections?: number;
|
|
70
|
+
className?: string;
|
|
71
|
+
disabled?: boolean;
|
|
72
|
+
minSearchLength?: number;
|
|
73
|
+
debounceMs?: number;
|
|
74
|
+
maxResults?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function Typeahead<T extends TypeaheadItem>(props: TypeaheadProps<T>) {
|
|
78
|
+
const {
|
|
79
|
+
placeholder = "Search...",
|
|
80
|
+
searchFn,
|
|
81
|
+
onSelectionChange,
|
|
82
|
+
renderItem,
|
|
83
|
+
renderBadge,
|
|
84
|
+
selectedItems = [],
|
|
85
|
+
multiSelect = false,
|
|
86
|
+
maxSelections,
|
|
87
|
+
className = "form-control",
|
|
88
|
+
disabled = false,
|
|
89
|
+
minSearchLength = 1,
|
|
90
|
+
debounceMs = 300,
|
|
91
|
+
maxResults = 10
|
|
92
|
+
} = props;
|
|
93
|
+
|
|
94
|
+
const [query, setQuery] = useState('');
|
|
95
|
+
const [results, setResults] = useState<T[]>([]);
|
|
96
|
+
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
97
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
98
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
99
|
+
|
|
100
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
101
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
102
|
+
const resultsRef = useRef<HTMLDivElement>(null);
|
|
103
|
+
const debounceRef = useRef<NodeJS.Timeout>();
|
|
104
|
+
|
|
105
|
+
// Inject styles once
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const styleId = 'typeahead-styles';
|
|
108
|
+
if (!document.getElementById(styleId)) {
|
|
109
|
+
const style = document.createElement('style');
|
|
110
|
+
style.id = styleId;
|
|
111
|
+
style.textContent = typeaheadStyles;
|
|
112
|
+
document.head.appendChild(style);
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
115
|
+
|
|
116
|
+
// Debounced search
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (debounceRef.current) {
|
|
119
|
+
clearTimeout(debounceRef.current);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (query.length >= minSearchLength) {
|
|
123
|
+
debounceRef.current = setTimeout(async () => {
|
|
124
|
+
setIsLoading(true);
|
|
125
|
+
try {
|
|
126
|
+
const searchResults = await searchFn(query);
|
|
127
|
+
// Filter out already selected items
|
|
128
|
+
const selectedIds = selectedItems.map(item => item.id);
|
|
129
|
+
const filteredResults = searchResults.filter(item => !selectedIds.includes(item.id));
|
|
130
|
+
setResults(filteredResults.slice(0, maxResults));
|
|
131
|
+
setIsOpen(filteredResults.length > 0);
|
|
132
|
+
setSelectedIndex(-1);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Search error:', error);
|
|
135
|
+
setResults([]);
|
|
136
|
+
} finally {
|
|
137
|
+
setIsLoading(false);
|
|
138
|
+
}
|
|
139
|
+
}, debounceMs);
|
|
140
|
+
} else {
|
|
141
|
+
setResults([]);
|
|
142
|
+
setIsOpen(false);
|
|
143
|
+
setSelectedIndex(-1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return () => {
|
|
147
|
+
if (debounceRef.current) {
|
|
148
|
+
clearTimeout(debounceRef.current);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}, [query, searchFn, minSearchLength, debounceMs, maxResults, selectedItems]);
|
|
152
|
+
|
|
153
|
+
// Click outside to close
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
function handleClickOutside(event: MouseEvent) {
|
|
156
|
+
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
|
|
157
|
+
setIsOpen(false);
|
|
158
|
+
setSelectedIndex(-1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
163
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
164
|
+
}, []);
|
|
165
|
+
|
|
166
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
167
|
+
// Handle backspace to remove last selected item when input is empty
|
|
168
|
+
if (e.key === 'Backspace' && query === '' && selectedItems.length > 0) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
const lastItem = selectedItems[selectedItems.length - 1];
|
|
171
|
+
handleRemove(lastItem);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Handle tab to auto-select highlighted result (or first result if none highlighted)
|
|
176
|
+
if (e.key === 'Tab' && isOpen && results.length > 0) {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
const indexToSelect = selectedIndex >= 0 ? selectedIndex : 0;
|
|
179
|
+
handleSelect(results[indexToSelect]);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!isOpen || results.length === 0) return;
|
|
184
|
+
|
|
185
|
+
switch (e.key) {
|
|
186
|
+
case 'ArrowDown':
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
setSelectedIndex(prev =>
|
|
189
|
+
prev < results.length - 1 ? prev + 1 : 0
|
|
190
|
+
);
|
|
191
|
+
break;
|
|
192
|
+
case 'ArrowUp':
|
|
193
|
+
e.preventDefault();
|
|
194
|
+
setSelectedIndex(prev =>
|
|
195
|
+
prev > 0 ? prev - 1 : results.length - 1
|
|
196
|
+
);
|
|
197
|
+
break;
|
|
198
|
+
case 'Enter':
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
if (selectedIndex >= 0 && selectedIndex < results.length) {
|
|
201
|
+
handleSelect(results[selectedIndex]);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case 'Escape':
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
setIsOpen(false);
|
|
207
|
+
setSelectedIndex(-1);
|
|
208
|
+
inputRef.current?.blur();
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const handleSelect = (item: T) => {
|
|
214
|
+
if (maxSelections && selectedItems.length >= maxSelections) {
|
|
215
|
+
return; // Don't allow selection beyond max
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let newSelectedItems: T[];
|
|
219
|
+
if (multiSelect) {
|
|
220
|
+
// Add to existing selections
|
|
221
|
+
newSelectedItems = [...selectedItems, item];
|
|
222
|
+
} else {
|
|
223
|
+
// Replace existing selection
|
|
224
|
+
newSelectedItems = [item];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
onSelectionChange(newSelectedItems);
|
|
228
|
+
setQuery('');
|
|
229
|
+
setResults([]);
|
|
230
|
+
setIsOpen(false);
|
|
231
|
+
setSelectedIndex(-1);
|
|
232
|
+
inputRef.current?.focus();
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const handleRemove = (itemToRemove: T) => {
|
|
236
|
+
const newSelectedItems = selectedItems.filter(item => item.id !== itemToRemove.id);
|
|
237
|
+
onSelectionChange(newSelectedItems);
|
|
238
|
+
inputRef.current?.focus();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const defaultRenderBadge = (item: T) => {
|
|
242
|
+
// Extract display text - try common properties
|
|
243
|
+
const displayText = (item as any).name || (item as any).title || (item as any).label || item.id;
|
|
244
|
+
return displayText;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
248
|
+
setQuery(e.target.value);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const handleInputFocus = () => {
|
|
252
|
+
if (results.length > 0) {
|
|
253
|
+
setIsOpen(true);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div ref={containerRef} className="position-relative">
|
|
259
|
+
{/* Input container with badges inside */}
|
|
260
|
+
<div
|
|
261
|
+
className="typeahead-input-container"
|
|
262
|
+
onClick={() => inputRef.current?.focus()}
|
|
263
|
+
>
|
|
264
|
+
{/* Selected Items as Badges inside input */}
|
|
265
|
+
{selectedItems.map((item) => (
|
|
266
|
+
<div
|
|
267
|
+
key={item.id}
|
|
268
|
+
className="badge bg-primary typeahead-badge d-flex align-items-center position-relative pe-4"
|
|
269
|
+
>
|
|
270
|
+
<span>{renderBadge ? renderBadge(item) : defaultRenderBadge(item)}</span>
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
className="btn-close btn-close-white position-absolute"
|
|
274
|
+
style={{
|
|
275
|
+
right: '2px',
|
|
276
|
+
top: '50%',
|
|
277
|
+
transform: 'translateY(-50%)',
|
|
278
|
+
fontSize: '0.5rem',
|
|
279
|
+
width: '10px',
|
|
280
|
+
height: '10px'
|
|
281
|
+
}}
|
|
282
|
+
onClick={(e) => {
|
|
283
|
+
e.stopPropagation();
|
|
284
|
+
handleRemove(item);
|
|
285
|
+
}}
|
|
286
|
+
disabled={disabled}
|
|
287
|
+
aria-label="Remove"
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
))}
|
|
291
|
+
|
|
292
|
+
{/* Input field */}
|
|
293
|
+
<input
|
|
294
|
+
ref={inputRef}
|
|
295
|
+
type="text"
|
|
296
|
+
className="typeahead-input"
|
|
297
|
+
placeholder={selectedItems.length > 0 && !multiSelect ? "Selection made" : placeholder}
|
|
298
|
+
value={query}
|
|
299
|
+
onChange={handleInputChange}
|
|
300
|
+
onKeyDown={handleKeyDown}
|
|
301
|
+
onFocus={handleInputFocus}
|
|
302
|
+
disabled={disabled || (!multiSelect && selectedItems.length > 0)}
|
|
303
|
+
/>
|
|
304
|
+
|
|
305
|
+
{/* Loading indicator */}
|
|
306
|
+
{isLoading && (
|
|
307
|
+
<div className="position-absolute" style={{
|
|
308
|
+
right: '8px',
|
|
309
|
+
top: '50%',
|
|
310
|
+
transform: 'translateY(-50%)'
|
|
311
|
+
}}>
|
|
312
|
+
<div className="spinner-border spinner-border-sm text-secondary" role="status">
|
|
313
|
+
<span className="visually-hidden">Loading...</span>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
)}
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
{isOpen && results.length > 0 && (
|
|
320
|
+
<div
|
|
321
|
+
ref={resultsRef}
|
|
322
|
+
className="position-absolute w-100 bg-body border border-top-0 rounded-bottom shadow-lg"
|
|
323
|
+
style={{
|
|
324
|
+
zIndex: 1000,
|
|
325
|
+
maxHeight: '300px',
|
|
326
|
+
overflowY: 'auto'
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
{results.map((item, index) => (
|
|
330
|
+
<div
|
|
331
|
+
key={item.id}
|
|
332
|
+
className={`px-3 py-2 border-bottom typeahead-item ${
|
|
333
|
+
index === selectedIndex
|
|
334
|
+
? 'bg-primary text-white'
|
|
335
|
+
: 'bg-body text-body'
|
|
336
|
+
}`}
|
|
337
|
+
style={{
|
|
338
|
+
cursor: 'pointer'
|
|
339
|
+
}}
|
|
340
|
+
onClick={() => handleSelect(item)}
|
|
341
|
+
onMouseEnter={() => setSelectedIndex(index)}
|
|
342
|
+
>
|
|
343
|
+
{renderItem(item, index === selectedIndex)}
|
|
344
|
+
</div>
|
|
345
|
+
))}
|
|
346
|
+
</div>
|
|
347
|
+
)}
|
|
348
|
+
|
|
349
|
+
{isOpen && query.length >= minSearchLength && results.length === 0 && !isLoading && (
|
|
350
|
+
<div
|
|
351
|
+
className="position-absolute w-100 bg-body border border-top-0 rounded-bottom shadow-lg px-3 py-2 text-muted"
|
|
352
|
+
style={{ zIndex: 1000 }}
|
|
353
|
+
>
|
|
354
|
+
No results found
|
|
355
|
+
</div>
|
|
356
|
+
)}
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
}
|
package/src/globals.tsx
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Groups, IGroup, IMessage, IUser, Messages, computed, getMe, groupUserVar, observable, reloadPackagesOnPageRefresh, rpcClientCalls, rpcServerCalls, sleep } from "@peers-app/peers-sdk";
|
|
2
|
+
import { handleMainPathChanged, openNewTab } from "./tabs-layout/tabs-state";
|
|
3
|
+
import { stripMentions } from "./utils";
|
|
4
|
+
|
|
5
|
+
export const packageReloaded = observable(0);
|
|
6
|
+
|
|
7
|
+
export const _mainContentPath = groupUserVar('mainContentPath', { defaultValue: '' });
|
|
8
|
+
|
|
9
|
+
export const mainContentPath = computed<string>({
|
|
10
|
+
read: () => {
|
|
11
|
+
return _mainContentPath();
|
|
12
|
+
},
|
|
13
|
+
write: (newPath) => {
|
|
14
|
+
const oldPath = _mainContentPath();
|
|
15
|
+
if (newPath === oldPath) return;
|
|
16
|
+
// _mainContentPath(newPath);
|
|
17
|
+
handleMainPathChanged(oldPath, newPath, _mainContentPath);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const queryParams = computed<{ [key: string]: string }>({
|
|
22
|
+
read: () => {
|
|
23
|
+
const queryStr = mainContentPath()?.split('?')[1] || '';
|
|
24
|
+
const params = new URLSearchParams(queryStr);
|
|
25
|
+
const obj: { [key: string]: string } = {};
|
|
26
|
+
for (const [key, value] of params.entries()) {
|
|
27
|
+
obj[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return obj
|
|
30
|
+
},
|
|
31
|
+
write: (value: { [key: string]: string }) => {
|
|
32
|
+
const queryStr = mainContentPath().split('?')[1] || '';
|
|
33
|
+
const params = new URLSearchParams(queryStr);
|
|
34
|
+
for (const key in value) {
|
|
35
|
+
params.set(key, value[key]);
|
|
36
|
+
}
|
|
37
|
+
mainContentPath(mainContentPath().split('?')[0] + '?' + params.toString());
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export function queryParam(name: string, value?: string) {
|
|
42
|
+
const params = queryParams();
|
|
43
|
+
if (value !== undefined) {
|
|
44
|
+
if (params[name] === value) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (value === null || value === '') {
|
|
48
|
+
delete params[name];
|
|
49
|
+
} else {
|
|
50
|
+
params[name] = value;
|
|
51
|
+
}
|
|
52
|
+
queryParams(params);
|
|
53
|
+
}
|
|
54
|
+
return params[name];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// export const openThreads = persistentVar<(string | IMessage)[]>('openThreads', { scope: 'user', defaultValue: [] });
|
|
58
|
+
export const openThreads: ReturnType<typeof groupUserVar<(string | IMessage)[]>> =
|
|
59
|
+
groupUserVar<(string | IMessage)[]>('openThreads', { defaultValue: [] });
|
|
60
|
+
export const threadViewOpen = groupUserVar<boolean>('threadViewOpen', { defaultValue: false });
|
|
61
|
+
|
|
62
|
+
export async function openThread(thread: string | IMessage) {
|
|
63
|
+
openThreadInTab(thread);
|
|
64
|
+
// let threadId = typeof thread === 'string' ? thread : thread.messageId;
|
|
65
|
+
// const message = await Messages().get(threadId);
|
|
66
|
+
// if (message) {
|
|
67
|
+
// thread = message.messageParentId || message.messageId;
|
|
68
|
+
// }
|
|
69
|
+
// const threadsWithout = openThreads().filter(t => (typeof t === 'string' && t !== threadId) || (typeof t === 'object' && t.messageId !== threadId));
|
|
70
|
+
// openThreads([thread, ...threadsWithout]);
|
|
71
|
+
// threadViewOpen(true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Thread opening function for tabs system
|
|
75
|
+
export async function openThreadInTab(thread: string | IMessage) {
|
|
76
|
+
let threadId = typeof thread === 'string' ? thread : thread.messageId;
|
|
77
|
+
const message = await Messages().get(threadId);
|
|
78
|
+
|
|
79
|
+
if (!message) {
|
|
80
|
+
console.warn('Thread message not found:', threadId);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Use parent message ID if this is a reply, otherwise use the message itself
|
|
85
|
+
const actualThreadId = message.messageParentId || message.messageId;
|
|
86
|
+
|
|
87
|
+
// Generate thread title from message content (strip mentions and clean formatting)
|
|
88
|
+
const cleanMessage = stripMentions(message.message);
|
|
89
|
+
const messagePreview = cleanMessage.slice(0, 30);
|
|
90
|
+
const threadTitle = `${messagePreview}${cleanMessage.length > 30 ? '...' : ''}` || `Thread`;
|
|
91
|
+
|
|
92
|
+
// // Import openNewTab dynamically to avoid circular dependency
|
|
93
|
+
// const { openNewTab } = await import('./tabs-layout/tabs-layout');
|
|
94
|
+
|
|
95
|
+
openNewTab({
|
|
96
|
+
packageId: 'system-apps',
|
|
97
|
+
path: `threads/${actualThreadId}`,
|
|
98
|
+
title: threadTitle,
|
|
99
|
+
iconClassName: 'bi-chat-dots',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
rpcClientCalls.setClientPath = async (url: string) => {
|
|
104
|
+
mainContentPath(url && String(url).trim() || '');
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
rpcClientCalls.openThread = async (threadId: string) => {
|
|
108
|
+
console.warn('Opening thread:', threadId);
|
|
109
|
+
openThread(threadId);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export let me: IUser = null as unknown as IUser;
|
|
113
|
+
|
|
114
|
+
export const windowWidth = observable(window.innerWidth);//.extend({ rateLimit: 500 });
|
|
115
|
+
export const windowHeight = observable(window.innerHeight);//.extend({ rateLimit: 500 });
|
|
116
|
+
window.addEventListener("resize", (ev) => {
|
|
117
|
+
windowWidth(window.innerWidth)
|
|
118
|
+
windowHeight(window.innerHeight)
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export const isDesktop = computed(() => {
|
|
122
|
+
// return windowWidth() > 1200;
|
|
123
|
+
return windowWidth() > 992;
|
|
124
|
+
// return windowWidth() > 768;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export const groups = observable<IGroup[]>([]);
|
|
128
|
+
|
|
129
|
+
let globalsLoaded = false;
|
|
130
|
+
export async function loadGlobals() {
|
|
131
|
+
if (globalsLoaded) return true;
|
|
132
|
+
globalsLoaded = true;
|
|
133
|
+
me = await getMe();
|
|
134
|
+
await reloadPackagesOnPageRefresh.loadingPromise;
|
|
135
|
+
if (reloadPackagesOnPageRefresh()) {
|
|
136
|
+
await rpcServerCalls.installOrUpdatePackage('all');
|
|
137
|
+
}
|
|
138
|
+
await Promise.all([
|
|
139
|
+
Groups().list().then(groups),
|
|
140
|
+
_mainContentPath.loadingPromise,
|
|
141
|
+
openThreads.loadingPromise,
|
|
142
|
+
threadViewOpen.loadingPromise,
|
|
143
|
+
sleep(100), // hacky way to prevent some jank
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
function updateWindowHash() {
|
|
147
|
+
if (window.location.hash.substr(1) !== mainContentPath()) {
|
|
148
|
+
window.location.hash = mainContentPath() || '';
|
|
149
|
+
}
|
|
150
|
+
mainContentPath(mainContentPath() || '');
|
|
151
|
+
}
|
|
152
|
+
mainContentPath.subscribe(updateWindowHash);
|
|
153
|
+
updateWindowHash();
|
|
154
|
+
window.addEventListener('hashchange', () => mainContentPath(window.location.hash.substring(1)));
|
|
155
|
+
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (typeof window !== 'undefined') {
|
|
160
|
+
// @ts-ignore
|
|
161
|
+
window.globals = module.exports;
|
|
162
|
+
}
|
package/src/hooks.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { isArray, isEqual } from 'lodash';
|
|
4
|
+
import { observable, isSubscribable, unwrapObservable, Observable } from "@peers-app/peers-sdk";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Use this to subscribe to an observable or computed in a functional component.
|
|
10
|
+
* @param sub the observable or computed to subscribe to
|
|
11
|
+
* @param deps an array of dependencies to pass to useEffect which will trigger a re-render when any of the dependencies change
|
|
12
|
+
* @returns the current value of the observable or computed and a function to set the value
|
|
13
|
+
*/
|
|
14
|
+
export function useObservable<T>(sub: Observable<T> | T, deps: React.DependencyList = []): [T, (value: T) => void] {
|
|
15
|
+
const [data, setData] = useState(() => unwrapObservable(sub));
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!isSubscribable(sub)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const subscription = sub.subscribe(() => {
|
|
21
|
+
const newData = sub();
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
if (isArray(newData) && newData === data) {
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
setData([...newData])
|
|
26
|
+
} else {
|
|
27
|
+
setData(newData)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
// the data might change _after_ useObservable is called but _before_ the subscription has been created
|
|
31
|
+
// this checks for that and updates the state with the new data if necessary
|
|
32
|
+
const newData = sub();
|
|
33
|
+
if (!isEqual(data, newData)) {
|
|
34
|
+
setData(newData);
|
|
35
|
+
}
|
|
36
|
+
return () => subscription.dispose();
|
|
37
|
+
}, deps);
|
|
38
|
+
|
|
39
|
+
return [data, newData => {
|
|
40
|
+
setData(newData);
|
|
41
|
+
if (isSubscribable(sub)) {
|
|
42
|
+
sub(newData);
|
|
43
|
+
}
|
|
44
|
+
}];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Use this to easily wait for a promise in a functional component.
|
|
49
|
+
* @param p The promise to wait for
|
|
50
|
+
* @param initialValue the initial value to return before the promise resolves
|
|
51
|
+
* @param deps the dependencies to pass to useEffect which will trigger a re-render when any of the dependencies change
|
|
52
|
+
* @returns the initial value and the resolved value of the promise
|
|
53
|
+
*/
|
|
54
|
+
export function usePromise<T>(p: Promise<T> | (() => Promise<T>), initialValue?: T, deps: React.DependencyList = []): T | undefined {
|
|
55
|
+
const [data, setData] = useState(initialValue);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
let disposed = false;
|
|
58
|
+
if (typeof p === 'function') {
|
|
59
|
+
p = p();
|
|
60
|
+
}
|
|
61
|
+
p.then(newData => {
|
|
62
|
+
// if (!_.isEqual(newData, data) && !disposed) {
|
|
63
|
+
if (!disposed) {
|
|
64
|
+
setData(newData)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
return () => {
|
|
68
|
+
disposed = true;
|
|
69
|
+
}
|
|
70
|
+
}, deps);
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Use this to create an observable in a functional component.
|
|
76
|
+
* This automatically subscribes to the observable and triggers a re-render when the value changes.
|
|
77
|
+
* @param initialValue This will be the initial value of the observable
|
|
78
|
+
* @param doNotSubscribe If true, the observable will not be subscribed to automatically.
|
|
79
|
+
* This is useful if you want to create an observable that persists between rerenders but doesn't cause rerenders itself.
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
export function useObservableState<T>(initialValue?: T, doNotSubscribe?: boolean) {
|
|
83
|
+
const [obs] = useState(() => observable(initialValue));
|
|
84
|
+
if (!doNotSubscribe) {
|
|
85
|
+
useObservable(obs);
|
|
86
|
+
}
|
|
87
|
+
return obs;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Use this to register a handler for a knockout subscribable in a functional component.
|
|
92
|
+
* The handler will be called immediately and whenever the subscribable changes.
|
|
93
|
+
* @param subscribable The observable to subscribe to
|
|
94
|
+
* @param onChange The function to call with the new value when the observable changes
|
|
95
|
+
*/
|
|
96
|
+
export function useSubscription<T>(subscribable: Observable<T>, onChange: (value: T) => any, doNotCallOnChangeDuringSetup?: boolean): void {
|
|
97
|
+
if (!doNotCallOnChangeDuringSetup) {
|
|
98
|
+
useEffect(() => onChange(subscribable()), []);
|
|
99
|
+
}
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
const subscription = subscribable.subscribe(() => onChange(subscribable()));
|
|
102
|
+
return () => subscription.dispose();
|
|
103
|
+
}, [subscribable, onChange]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function useOnScreen(ref: React.RefObject<any>) {
|
|
107
|
+
const [isIntersecting, setIntersecting] = useState(false)
|
|
108
|
+
|
|
109
|
+
const observer = new IntersectionObserver(
|
|
110
|
+
([entry]) => setIntersecting(entry.isIntersecting)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
observer.observe(ref.current)
|
|
115
|
+
// Remove the observer as soon as the component is unmounted
|
|
116
|
+
return () => { observer.disconnect() }
|
|
117
|
+
}, [])
|
|
118
|
+
|
|
119
|
+
return isIntersecting
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// /**
|
|
123
|
+
// * This creates an observable that will automatically persist its value between page reloads using localStorage.
|
|
124
|
+
// * If localStorage is not available, the observable will not persist its value.
|
|
125
|
+
// * @param initialValue the initial value of the observable
|
|
126
|
+
// * @param globalName the name to use when storing the value in localStorage
|
|
127
|
+
// * @returns the observable that will persist between page reloads
|
|
128
|
+
// */
|
|
129
|
+
// export function persistentValue<T>(initialValue: T, globalName: string): Observable<T | undefined> {
|
|
130
|
+
// let q = observable<T>();
|
|
131
|
+
// if (typeof localStorage === 'undefined') {
|
|
132
|
+
// return q;
|
|
133
|
+
// }
|
|
134
|
+
// q.subscribe(newVal => {
|
|
135
|
+
// localStorage.setItem(globalName, JSON.stringify(toJSON(newVal)))
|
|
136
|
+
// })
|
|
137
|
+
// const existing = localStorage.getItem(globalName);
|
|
138
|
+
// if (existing) {
|
|
139
|
+
// q(fromJSON(JSON.parse(existing)))
|
|
140
|
+
// } else {
|
|
141
|
+
// q(initialValue);
|
|
142
|
+
// }
|
|
143
|
+
// return q;
|
|
144
|
+
// }
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { deviceVar } from "@peers-app/peers-sdk";
|
|
2
|
+
|
|
3
|
+
export const leftBarCollapsed = deviceVar('leftBarCollapsed', { defaultValue: false, });
|
|
4
|
+
export const rightBarCollapsed = deviceVar('rightBarCollapsed', { defaultValue: false, });
|
|
5
|
+
export const leftBarWidth = deviceVar('leftBarWidth', { defaultValue: 250, });
|
|
6
|
+
export const rightBarWidth = deviceVar('rightBarWidth', { defaultValue: 250, });
|
|
7
|
+
export const leftBarWidthPrev = deviceVar('leftBarWidthPrev', { defaultValue: 250, });
|
|
8
|
+
export const rightBarWidthPrev = deviceVar('rightBarWidthPrev', { defaultValue: 250, });
|