Compare commits

...
45 changed files with 319 additions and 214 deletions
-23
View File
@@ -26,26 +26,3 @@ jobs:
run: yarn android:bundle
- name: Test
run: yarn test
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish SNJS Docker image for E2E testing
run: |
yarn docker build @standardnotes/snjs -t standardnotes/snjs:${{ github.sha }}
docker push standardnotes/snjs:${{ github.sha }}
docker tag standardnotes/snjs:${{ github.sha }} standardnotes/snjs:test
docker push standardnotes/snjs:test
- name: Run E2E test suite
uses: convictional/[email protected]
with:
owner: standardnotes
repo: e2e
github_token: ${{ secrets.CI_PAT_TOKEN }}
workflow_file_name: testing-with-stable-server.yml
wait_interval: 30
client_payload: '{"image_tag": "${{ github.sha }}"}'
propagate_failure: true
trigger_workflow: true
wait_workflow: true
+51
View File
@@ -0,0 +1,51 @@
name: SNJS PR — E2E Tests
on:
pull_request:
branches:
- main
paths:
- 'packages/api/**'
- 'packages/encryption/**'
- 'packages/features/**'
- 'packages/filepicker/**'
- 'packages/files/**'
- 'packages/models/**'
- 'packages/responses/**'
- 'packages/services/**'
- 'packages/sncrypto-common/**'
- 'packages/sncrypto-web/**'
- 'packages/snjs/**'
- 'packages/utils/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies
run: yarn install --immutable
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish SNJS Docker image for E2E testing
run: |
yarn docker build @standardnotes/snjs -t standardnotes/snjs:${{ github.sha }}
docker push standardnotes/snjs:${{ github.sha }}
docker tag standardnotes/snjs:${{ github.sha }} standardnotes/snjs:test
docker push standardnotes/snjs:test
- name: Run E2E test suite
uses: convictional/[email protected]
with:
owner: standardnotes
repo: e2e
github_token: ${{ secrets.CI_PAT_TOKEN }}
workflow_file_name: testing-with-stable-server.yml
wait_interval: 30
client_payload: '{"image_tag": "${{ github.sha }}"}'
propagate_failure: true
trigger_workflow: true
wait_workflow: true
Binary file not shown.
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.23.70](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
### Bug Fixes
* **desktop:** don't shift sections on macOS if window is small ([#1235](https://github.com/standardnotes/app/issues/1235)) ([c6932ba](https://github.com/standardnotes/app/commit/c6932ba733e0a67220ce56bec72431240518f7d8))
## [3.23.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/desktop
@@ -9,12 +9,14 @@
transition: 0.15s padding ease;
}
.mac-desktop #app.collapsed-notes.collapsed-navigation #editor-column {
padding-top: 18px;
}
@media screen and (min-width: 768px) {
.mac-desktop #app.collapsed-notes.collapsed-navigation #editor-column {
padding-top: 18px;
}
.mac-desktop #app.collapsed-navigation #items-column {
padding-top: 18px;
.mac-desktop #app.collapsed-navigation #items-column {
padding-top: 18px;
}
}
panel-resizer {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.67",
"version": "3.23.70",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
### Bug Fixes
* **docs:** improve getting started self host page ([#1238](https://github.com/standardnotes/app/issues/1238)) ([4ad21c4](https://github.com/standardnotes/app/commit/4ad21c4f61e26dfe3284c5def1baebb7a497d200))
## [0.0.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-05)
**Note:** Version bump only for package @standardnotes/docs
@@ -16,28 +16,57 @@ hide_title: false
hide_table_of_contents: false
---
Our self-hosted server infrastructure consists of several different microservices that are responsible for different sets of functionality. Our self-hosted server is only intended as the backend that processes and stores your data; it does not include self-hosting the web application, which is an optional process that must be done separately. You will be able to use our existing [web](https://app.standardnotes.com) and desktop app with your self-hosted server.
Our self-hosted server infrastructure consists of different microservices responsible for varying functionality. The self-hosted server works as the _backend_ that processes and stores your data; it does not include the web application.
## Get Started
The web application is an optional process that you must spin up separately. However, you can use the [existing web app](https://app.standardnotes.com) or the official Standard Notes desktop app with your self-hosted server.
👉 **[Using our automated docker-compose setup with accompanying scripts](./docker.md)** 👈
:::tip Quick start
If you'd like to learn more about each of the particular services, head over to [Infrastructure Overview](./infrastructure-overview.md).
The fastest and easiest way to get up and running is to use our automated Docker setup. All you need is a Linux server and the latest version of [Docker](https://docs.docker.com/get-started).
> **Note** Our setup also provides a running MySQL database and a Redis cache node. You do not have to provision these services on your own. For users that have been self-hosting a legacy version of our server, we've prepared a [Migrating from Legacy guide](./legacy-migration.md).
[Check out our Docker instructions page to get started ](./docker.md)
### Recommendations
:::
We highly recommend you use our Docker setup to host your syncing server. Docker containers are isolated software environments that you can control and manage.
## Infrastructure overview
If you are new to Docker, please see the [official Docker documentation](https://docs.docker.com/get-started) on how to get started. Ensure you [install Docker-Compose](https://docs.docker.com/compose/install/) following the documentation. Your Linux distribution may not have the most up to date docker-compose and will fail to load.
### API Gateway
We recommend avoiding setting up your syncing server from scratch with Nginx unless you are proficient with Nginx. Setting up the full architecture can be challenging without full knowledge of how the syncing server and its microservices function.
The main entry point of the architecture. The API Gateway is a router and proxy for all services which are otherwise inaccessible directly. All requests from client applications go through the API Gateway to reach a target underlying service. This service is configured with your reverse proxy for public HTTPS support.
### Issues
### Syncing Server
If you have any issues with setting up your syncing server, please [open an issue on GitHub](https://github.com/standardnotes/standalone/issues).
Responsible for user data and syncing operations.
### Syncing Server Worker
Responsible for asynchronous tasks the Syncing Server may offload for background processing, including email backups, revision history, and more.
### Auth
Responsible for authorization and authentication mechanisms within Standard Notes.
### Auth Worker
Responsible for asynchronous tasks related to the domain of authentication and authorization, including account deletion requests and post-registration tasks.
### Database
The database is where data is stored.
### Cache
A Redis cache node is used to store temporary data for performance optimization and auto-expiring features. In self-hosted mode, Redis is used as a communication queue between services and workers.
## Troubleshooting
If you run into any issues setting up your server, please [open an issue on GitHub](https://github.com/standardnotes/standalone/issues) or reach out on the [Standard Notes Discord](https://standardnotes.com/discord).
## Web application
If you would like to self-host the actual Standard Notes web application, visit the [repository for the web app on GitHub](https://github.com/standardnotes/web).
If you would like to self-host the actual Standard Notes web application, visit the [repository for the Standard Notes web app on GitHub](https://github.com/standardnotes/app/tree/main/packages/web).
## Self-hosting without Docker?
Configuring the full Standard Notes architecture manually can be challenging without detailed study. We do not offer support for this method of self-hosting. [The only supported self-hosting method is to use Docker →](./docker.md)
@@ -1,53 +0,0 @@
---
slug: infrastructure-overview
id: infrastructure-overview
title: Infrastructure Overview
sidebar_label: Infrastructure Overview
description: Standard Notes Infrastructure Overview.
keywords:
- standard notes
- docs
- notes app
- end-to-end encryption
- self-hosting
- sync server
image: /img/logo.png
hide_title: false
hide_table_of_contents: false
---
## Services
The Syncing Server infrastructure consists of a few different microservices that are responsible for different sets of functionality.
### Syncing Server JS
Syncing Server JS is a TypeScript implementation of our Syncing Server. This is the core of our business logic that is responsible for all operations on user data.
### Syncing Server JS Worker
Syncing Server JS Worker is responsible for all asynchronous tasks that the Syncing Server JS may offload for background processing. This includes for example processing of email backups, resolving issues with note duplicates, sending notes to extensions server, and more.
### Auth
This server is responsible for all authorization and authentication mechanisms. Auth is where all account-related metadata is handled and processed.
### Auth Worker
Similar to Syncing Server JS Worker, Auth Worker is responsible for all asynchronous tasks related to the domain of authentication and authorization. For example, processing account deletion requests and users' post-registration tasks.
### API Gateway
This is the main "entry point" of the entire architecture. API Gateway serves as a router and proxy to all services which are inaccessible directly. All requests from client applications will have to go through API Gateway in order to reach a certain underlying service.
This service will be paired with your reverse proxy for [HTTPS support](./https-support.md)
### DB
MySQL database server. This is where all data is stored.
### Cache
Redis cache node where all temporary data is persisted for performance optimization and auto-expiring features.
In self-host mode, Redis is used by default as a communication queue between services and their workers.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/docs",
"version": "0.0.2",
"version": "0.0.3",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
-1
View File
@@ -4,7 +4,6 @@ module.exports = {
{
'Self Hosting': [
'self-hosting/getting-started',
'self-hosting/infrastructure-overview',
'self-hosting/docker',
'self-hosting/configuration-options',
'self-hosting/legacy-migration',
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.30.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
**Note:** Version bump only for package @standardnotes/mobile
## [3.30.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/mobile
## [3.30.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/mobile
## [3.30.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.30.22",
"version": "3.30.25",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
**Note:** Version bump only for package @standardnotes/releases
## [1.2.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/releases
## [1.2.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/releases
## [1.2.88](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.2.88",
"version": "1.2.91",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
**Note:** Version bump only for package @standardnotes/web-server
## [1.2.92](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/web-server
## [1.2.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/web-server
## [1.2.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
**Note:** Version bump only for package @standardnotes/web-server
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web-server",
"version": "1.2.90",
"version": "1.2.93",
"license": "AGPL-3.0-or-later",
"private": true,
"author": "Standard Notes.",
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.40.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-09)
### Features
* release Files view ([#1236](https://github.com/standardnotes/app/issues/1236)) ([5393eb7](https://github.com/standardnotes/app/commit/5393eb71fecdafab524766612d7244c64625c25e))
## [3.39.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
### Bug Fixes
* **desktop:** don't shift sections on macOS if window is small ([#1235](https://github.com/standardnotes/app/issues/1235)) ([c6932ba](https://github.com/standardnotes/app/commit/c6932ba733e0a67220ce56bec72431240518f7d8))
# [3.39.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
### Features
* **web:** mobile-friendly (responsive) preferences UI ([#1234](https://github.com/standardnotes/app/issues/1234)) ([66f97f0](https://github.com/standardnotes/app/commit/66f97f06123b1be087ea5a23140b6bd18e0c004f))
## [3.38.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-08)
### Bug Fixes
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.38.6",
"version": "3.40.0",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -25,6 +25,7 @@
"@babel/plugin-transform-react-jsx": "^7.17.3",
"@babel/preset-env": "*",
"@babel/preset-typescript": "^7.16.7",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@types/jest": "^27.5.1",
"@types/react": "^17.0.42",
"@types/react-dom": "^18.0.5",
@@ -50,6 +51,7 @@
"postcss-loader": "^7.0.0",
"prettier": "*",
"prettier-plugin-tailwindcss": "^0.1.11",
"react-refresh": "^0.14.0",
"sass-loader": "*",
"svg-jest": "^1.0.1",
"tailwindcss": "^3.1.4",
@@ -26,7 +26,6 @@ import { makeObservable, observable } from 'mobx'
import { PanelResizedData } from '@/Types/PanelResizedData'
import { WebAppEvent } from './WebAppEvent'
import { isDesktopApplication } from '@/Utils'
import { storage, StorageKey } from '@/Services/LocalStorage'
type WebServices = {
viewControllerManager: ViewControllerManager
@@ -63,7 +62,7 @@ export class WebApplication extends SNApplication {
defaultHost: defaultSyncServerHost,
appVersion: deviceInterface.appVersion,
webSocketUrl: webSocketUrl,
supportsFileNavigation: storage.get(StorageKey.FilesNavigationEnabled) || false,
supportsFileNavigation: true,
})
makeObservable(this, {
@@ -37,12 +37,20 @@ const FileListItem: FunctionComponent<DisplayableListItemProps> = ({
const openContextMenu = useCallback(
async (posX: number, posY: number) => {
const { didSelect } = await selectionController.selectItem(item.uuid)
if (didSelect) {
let shouldOpenContextMenu = selected
if (!selected) {
const { didSelect } = await selectionController.selectItem(item.uuid)
if (didSelect) {
shouldOpenContextMenu = true
}
}
if (shouldOpenContextMenu) {
openFileContextMenu(posX, posY)
}
},
[selectionController, item.uuid, openFileContextMenu],
[selected, selectionController, item.uuid, openFileContextMenu],
)
const onClick = useCallback(async () => {
@@ -41,8 +41,16 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
}
const openContextMenu = async (posX: number, posY: number) => {
const { didSelect } = await selectionController.selectItem(item.uuid, true)
if (didSelect) {
let shouldOpenContextMenu = selected
if (!selected) {
const { didSelect } = await selectionController.selectItem(item.uuid)
if (didSelect) {
shouldOpenContextMenu = true
}
}
if (shouldOpenContextMenu) {
openNoteContextMenu(posX, posY)
}
}
@@ -1,7 +1,9 @@
import { ListboxButton } from '@reach/listbox'
import styled from 'styled-components'
const StyledListboxButton = styled(ListboxButton)`
const StyledListboxButton = styled(ListboxButton).attrs(() => ({
className: 'w-full md:w-fit',
}))`
&[data-reach-listbox-button] {
background-color: var(--sn-stylekit-background-color);
border-radius: 0.25rem;
@@ -14,7 +16,6 @@ const StyledListboxButton = styled(ListboxButton)`
padding-left: 0.875rem;
padding-right: 0.875rem;
padding-top: 0.375rem;
width: fit-content;
}
`
@@ -34,6 +34,12 @@ const FileMenuOptions: FunctionComponent<Props> = ({
const { handleFileAction } = filesController
const hasProtectedFiles = useMemo(() => selectedFiles.some((file) => file.protected), [selectedFiles])
const hasSelectedMultipleFiles = useMemo(() => selectedFiles.length > 1, [selectedFiles.length])
const totalFileSize = useMemo(
() => selectedFiles.map((file) => file.decryptedSize).reduce((prev, next) => prev + next, 0),
[selectedFiles],
)
const onPreview = useCallback(() => {
void handleFileAction({
@@ -149,16 +155,17 @@ const FileMenuOptions: FunctionComponent<Props> = ({
<span className="text-danger">Delete permanently</span>
</button>
<HorizontalSeparator classes="my-2" />
{selectedFiles.length === 1 && (
<div className="px-3 pt-1 pb-0.5 text-xs font-medium text-neutral">
<div className="px-3 pt-1 pb-0.5 text-xs font-medium text-neutral">
{!hasSelectedMultipleFiles && (
<div className="mb-1">
<span className="font-semibold">File ID:</span> {selectedFiles[0].uuid}
</div>
<div>
<span className="font-semibold">Size:</span> {formatSizeToReadableString(selectedFiles[0].decryptedSize)}
</div>
)}
<div>
<span className="font-semibold">{hasSelectedMultipleFiles ? 'Total Size:' : 'Size:'}</span>{' '}
{formatSizeToReadableString(totalFileSize)}
</div>
)}
</div>
</>
)
}
@@ -28,6 +28,7 @@ const ImagePreview: FunctionComponent<Props> = ({ objectUrl }) => {
top: 0,
left: 0,
margin: 'auto',
maxWidth: 'none',
}),
}}
ref={(imgElement) => {
@@ -52,6 +52,7 @@ const Navigation: FunctionComponent<Props> = ({ application }) => {
<SearchBar
itemListController={viewControllerManager.itemListController}
searchOptionsController={viewControllerManager.searchOptionsController}
selectedViewTitle={viewControllerManager.navigationController.selected?.title}
/>
<div className="section-title-bar">
<div className="section-title-bar-header">
@@ -45,7 +45,7 @@ const NotesContextMenu = ({
return contextMenuOpen ? (
<div
ref={contextMenuRef}
className="max-h-120 fixed z-dropdown-menu flex min-w-80 max-w-xs flex-col overflow-y-auto rounded bg-default pt-2 shadow-main"
className="max-h-120 fixed z-dropdown-menu flex min-w-80 max-w-xs flex-col overflow-y-auto rounded bg-default py-2 shadow-main"
style={{
...contextMenuPosition,
maxHeight: contextMenuMaxHeight,
@@ -95,7 +95,7 @@ const NoteAttributes: FunctionComponent<{
const format = editor?.package_info?.file_type || 'txt'
return (
<div className="px-3 pt-1.5 pb-2.5 text-xs font-medium text-neutral">
<div className="px-3 py-1.5 text-xs font-medium text-neutral">
{typeof words === 'number' && (format === 'txt' || format === 'md') ? (
<>
<div className="mb-1">
@@ -159,12 +159,15 @@ const NoteSizeWarning: FunctionComponent<{
note: SNNote
}> = ({ note }) => {
return new Blob([note.text]).size > NOTE_SIZE_WARNING_THRESHOLD ? (
<div className="bg-warning-faded relative flex items-center px-3 py-3.5">
<Icon type="warning" className="mr-3 flex-shrink-0 text-accessory-tint-3" />
<div className="leading-140% max-w-80% select-none text-warning">
This note may have trouble syncing to the mobile application due to its size.
<>
<HorizontalSeparator classes="my-2" />
<div className="bg-warning-faded relative flex items-center px-3 py-3.5">
<Icon type="warning" className="mr-3 flex-shrink-0 text-accessory-tint-3" />
<div className="leading-140% max-w-80% select-none text-warning">
This note may have trouble syncing to the mobile application due to its size.
</div>
</div>
</div>
</>
) : null
}
@@ -90,7 +90,7 @@ const NotesOptionsPanel = ({
}}
className={`${
open ? 'flex' : 'hidden'
} max-h-120 slide-down-animation fixed min-w-80 max-w-xs flex-col overflow-y-auto rounded bg-default pt-2 shadow-main transition-transform duration-150`}
} max-h-120 slide-down-animation fixed min-w-80 max-w-xs flex-col overflow-y-auto rounded bg-default py-2 shadow-main transition-transform duration-150`}
onBlur={closeOnBlur}
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
>
@@ -30,7 +30,7 @@ const Authentication: FunctionComponent<Props> = ({ viewControllerManager }) =>
return (
<PreferencesGroup>
<PreferencesSegment>
<div className="flex flex-col items-center px-12">
<div className="flex flex-col items-center px-4 md:px-12">
<AccountIllustration className="mb-3" />
<Title>You're not signed in</Title>
<Text className="mb-3 text-center">
@@ -23,9 +23,8 @@ const SignOutView: FunctionComponent<Props> = observer(({ application, viewContr
<Title>Sign out</Title>
<Subtitle>Other devices</Subtitle>
<Text>Want to sign out on all devices except this one?</Text>
<div className="mt-3 flex flex-row">
<div className="mt-3 flex flex-row flex-wrap gap-3">
<Button
className="mr-3"
label="Sign out other sessions"
onClick={() => {
viewControllerManager.accountMenuController.setOtherSessionsSignOut(true)
@@ -6,7 +6,6 @@ import { usePremiumModal } from '@/Hooks/usePremiumModal'
import PreferencesGroup from '../../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../../PreferencesComponents/PreferencesSegment'
import LabsFeature from './LabsFeature'
import { StorageKey, useLocalStorageItem } from '@/Services/LocalStorage'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
type ExperimentalFeatureItem = {
@@ -25,7 +24,6 @@ type Props = {
const LabsPane: FunctionComponent<Props> = ({ application }) => {
const [experimentalFeatures, setExperimentalFeatures] = useState<ExperimentalFeatureItem[]>([])
const [isFilesNavigationEnabled, setFilesNavigation] = useLocalStorageItem(StorageKey.FilesNavigationEnabled)
const reloadExperimentalFeatures = useCallback(() => {
const experimentalFeatures = application.features.getExperimentalFeatures().map((featureIdentifier) => {
@@ -47,17 +45,6 @@ const LabsPane: FunctionComponent<Props> = ({ application }) => {
const premiumModal = usePremiumModal()
const toggleFilesNavigation = useCallback(() => {
const isEntitled = application.features.getFeatureStatus(FeatureIdentifier.Files) === FeatureStatus.Entitled
if (!isEntitled) {
premiumModal.activate('Files navigation')
return
}
setFilesNavigation(!isFilesNavigationEnabled)
}, [application.features, isFilesNavigationEnabled, premiumModal, setFilesNavigation])
return (
<PreferencesGroup>
<PreferencesSegment>
@@ -88,13 +75,6 @@ const LabsPane: FunctionComponent<Props> = ({ application }) => {
</Fragment>
)
})}
<HorizontalSeparator classes="mt-2.5 mb-3" />
<LabsFeature
name="Files navigation"
description={'Enables a "Files" view which allows for better files navigation. Requires reload.'}
toggleFeature={toggleFilesNavigation}
isEnabled={!!isFilesNavigationEnabled}
/>
{experimentalFeatures.length === 0 && (
<div className="flex items-center justify-between">
<div className="flex flex-col">
@@ -20,14 +20,15 @@ const EncryptionEnabled: FunctionComponent<Props> = ({ viewControllerManager })
const tagIcon = <Icon type="hashtag" className="min-h-5 min-w-5" />
const archiveIcon = <Icon type="archive" className="min-h-5 min-w-5" />
const trashIcon = <Icon type="trash" className="min-h-5 min-w-5" />
return (
<>
<div className="flex flex-row items-start pb-1 pt-1.5">
<div className="flex flex-row flex-wrap items-start pt-1.5 md:pb-1">
<EncryptionStatusItem status={notes} icon={noteIcon} />
<div className="min-w-3" />
<EncryptionStatusItem status={tags} icon={tagIcon} />
</div>
<div className="flex flex-row items-start">
<div className="flex flex-row flex-wrap items-start">
<EncryptionStatusItem status={archived} icon={archiveIcon} />
<div className="min-w-3" />
<EncryptionStatusItem status={deleted} icon={trashIcon} />
@@ -6,7 +6,7 @@ import PaneSelector from './PaneSelector'
import { PreferencesProps } from './PreferencesProps'
const PreferencesCanvas: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = (props) => (
<div className="flex min-h-0 flex-grow flex-row justify-between">
<div className="flex min-h-0 flex-grow flex-col-reverse md:flex-row md:justify-between">
<PreferencesMenuView menu={props.menu} />
<PaneSelector {...props} />
</div>
@@ -2,8 +2,7 @@ import { FunctionComponent } from 'react'
export const Title: FunctionComponent = ({ children }) => (
<>
<h2 className="m-0 mb-1 text-base font-bold text-info">{children}</h2>
<div className="min-h-2" />
<h2 className="m-0 mb-1 text-lg font-bold text-info md:text-base">{children}</h2>
</>
)
@@ -16,7 +15,7 @@ export const SubtitleLight: FunctionComponent<{ className?: string }> = ({ child
)
export const Text: FunctionComponent<{ className?: string }> = ({ children, className = '' }) => (
<p className={`${className} text-xs`}>{children}</p>
<p className={`${className} text-sm md:text-xs`}>{children}</p>
)
const buttonClasses =
@@ -1,9 +1,9 @@
import { FunctionComponent } from 'react'
const PreferencesPane: FunctionComponent = ({ children }) => (
<div className="flex min-h-0 flex-grow flex-row overflow-y-auto text-foreground">
<div className="flex flex-grow flex-col items-center py-6">
<div className="flex w-125 max-w-125 flex-col">
<div className="flex min-h-0 flex-grow flex-col overflow-y-auto text-foreground md:flex-row">
<div className="flex flex-grow flex-col items-center px-3 py-6 md:px-0">
<div className="flex flex-col md:w-125 md:max-w-125">
{children != undefined && Array.isArray(children) ? children.filter((child) => child != undefined) : children}
</div>
</div>
@@ -117,7 +117,7 @@ export class PreferencesMenu {
return 'account'
}
selectPane(key: PreferenceId): void {
selectPane = (key: PreferenceId) => {
this._selectedPane = key
}
@@ -1,25 +1,54 @@
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
import { FunctionComponent, useMemo } from 'react'
import Dropdown from '../Dropdown/Dropdown'
import { DropdownItem } from '../Dropdown/DropdownItem'
import PreferencesMenuItem from './PreferencesComponents/MenuItem'
import { PreferencesMenu } from './PreferencesMenu'
import { PreferenceId, PreferencesMenu } from './PreferencesMenu'
type Props = {
menu: PreferencesMenu
}
const PreferencesMenuView: FunctionComponent<Props> = ({ menu }) => (
<div className="flex min-w-55 flex-col overflow-y-auto px-3 py-6">
{menu.menuItems.map((pref) => (
<PreferencesMenuItem
key={pref.id}
iconType={pref.icon}
label={pref.label}
selected={pref.selected}
hasBubble={pref.hasBubble}
onClick={() => menu.selectPane(pref.id)}
/>
))}
</div>
)
const PreferencesMenuView: FunctionComponent<Props> = ({ menu }) => {
const { selectedPaneId, selectPane, menuItems } = menu
const dropdownMenuItems: DropdownItem[] = useMemo(
() =>
menuItems.map((menuItem) => ({
icon: menuItem.icon,
label: menuItem.label,
value: menuItem.id,
})),
[menuItems],
)
return (
<div className="px-3 py-2 md:px-0">
<div className="hidden min-w-55 flex-col overflow-y-auto px-3 py-6 md:flex">
{menuItems.map((pref) => (
<PreferencesMenuItem
key={pref.id}
iconType={pref.icon}
label={pref.label}
selected={pref.selected}
hasBubble={pref.hasBubble}
onClick={() => selectPane(pref.id)}
/>
))}
</div>
<div className="md:hidden">
<Dropdown
id="preferences-menu"
items={dropdownMenuItems}
label="Preferences Menu"
value={selectedPaneId}
onChange={(paneId) => {
selectPane(paneId as PreferenceId)
}}
/>
</div>
</div>
)
}
export default observer(PreferencesMenuView)
@@ -1,6 +1,4 @@
import RoundIconButton from '@/Components/Button/RoundIconButton'
import TitleBar from '@/Components/TitleBar/TitleBar'
import Title from '@/Components/TitleBar/Title'
import { FunctionComponent, useEffect, useMemo } from 'react'
import { observer } from 'mobx-react-lite'
import { PreferencesMenu } from './PreferencesMenu'
@@ -29,9 +27,9 @@ const PreferencesView: FunctionComponent<PreferencesProps> = (props) => {
return (
<div className="absolute top-0 left-0 z-preferences flex h-full w-full flex-col bg-contrast">
<TitleBar className="items-center justify-between">
<div className="h-8 w-8" />
<Title className="text-lg">Your preferences for Standard Notes</Title>
<div className="flex w-full flex-row items-center justify-between border-b border-solid border-border bg-default px-3 py-2 md:p-3">
<div className="hidden h-8 w-8 md:block" />
<h1 className="text-base font-bold md:text-lg">Your preferences for Standard Notes</h1>
<RoundIconButton
onClick={() => {
props.closePreferences()
@@ -39,7 +37,7 @@ const PreferencesView: FunctionComponent<PreferencesProps> = (props) => {
type="normal"
icon="close"
/>
</TitleBar>
</div>
<PreferencesCanvas {...props} menu={menu} />
</div>
)
@@ -8,11 +8,12 @@ import DecoratedInput from '../Input/DecoratedInput'
import { observer } from 'mobx-react-lite'
type Props = {
selectedViewTitle?: string
itemListController: ItemListController
searchOptionsController: SearchOptionsController
}
const SearchBar = ({ itemListController, searchOptionsController }: Props) => {
const SearchBar = ({ itemListController, searchOptionsController, selectedViewTitle = 'Notes' }: Props) => {
const searchInputRef = useRef<HTMLInputElement>(null)
const { noteFilterText, setNoteFilterText, clearFilterText, onFilterEnter } = itemListController
@@ -52,7 +53,7 @@ const SearchBar = ({ itemListController, searchOptionsController }: Props) => {
container: 'px-1',
input: 'placeholder:text-passive-0',
}}
placeholder="Search"
placeholder={`Search in ${selectedViewTitle}`}
value={noteFilterText}
ref={searchInputRef}
onBlur={onSearchBlur}
@@ -1,11 +0,0 @@
import { FunctionComponent } from 'react'
type Props = {
className?: string
}
const Title: FunctionComponent<Props> = ({ children, className }) => {
return <div className={`font-bold ${className ?? ''}`}>{children}</div>
}
export default Title
@@ -1,13 +0,0 @@
import { FunctionComponent } from 'react'
type Props = {
className?: string
}
const TitleBar: FunctionComponent<Props> = ({ children, className }) => (
<div className={`flex h-14 w-full flex-row border-b border-solid border-border bg-default p-3 ${className ?? ''}`}>
{children}
</div>
)
export default TitleBar
+2
View File
@@ -5,6 +5,8 @@ const CircularDependencyPlugin = require('circular-dependency-plugin')
const mergeWithEnvDefaults = require('./web.webpack-defaults')
require('dotenv').config()
const isDevelopment = process.env.NODE_ENV !== 'production'
module.exports = (env) => {
mergeWithEnvDefaults(env)
return {
+11 -9
View File
@@ -1,11 +1,12 @@
const { merge } = require('webpack-merge');
const config = require('./web.webpack.config.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const mergeWithEnvDefaults = require('./web.webpack-defaults.js');
const { merge } = require('webpack-merge')
const config = require('./web.webpack.config.js')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const mergeWithEnvDefaults = require('./web.webpack-defaults.js')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = (env, argv) => {
const port = argv.port || 3001;
mergeWithEnvDefaults(env);
const port = argv.port || 3001
mergeWithEnvDefaults(env)
return merge(config(env, argv), {
mode: 'development',
optimization: {
@@ -19,14 +20,15 @@ module.exports = (env, argv) => {
env: process.env,
},
}),
new ReactRefreshWebpackPlugin(),
],
devServer: {
hot: 'only',
hot: true,
static: '../web-server/public',
port,
devMiddleware: {
writeToDisk: argv.writeToDisk,
},
},
});
};
})
}
+10 -1
View File
@@ -5117,7 +5117,7 @@ __metadata:
languageName: node
linkType: hard
"@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.3":
"@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.3, @pmmmwh/react-refresh-webpack-plugin@npm:^0.5.7":
version: 0.5.7
resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.7"
dependencies:
@@ -7466,6 +7466,7 @@ __metadata:
"@babel/plugin-transform-react-jsx": ^7.17.3
"@babel/preset-env": "*"
"@babel/preset-typescript": ^7.16.7
"@pmmmwh/react-refresh-webpack-plugin": ^0.5.7
"@reach/alert": ^0.16.0
"@reach/alert-dialog": ^0.16.2
"@reach/checkbox": ^0.16.0
@@ -7515,6 +7516,7 @@ __metadata:
react-dnd-html5-backend: ^16.0.1
react-dnd-touch-backend: ^16.0.1
react-dom: ^18.1.0
react-refresh: ^0.14.0
sass-loader: "*"
styled-components: ^5.3.5
svg-jest: ^1.0.1
@@ -32588,6 +32590,13 @@ __metadata:
languageName: node
linkType: hard
"react-refresh@npm:^0.14.0":
version: 0.14.0
resolution: "react-refresh@npm:0.14.0"
checksum: dc69fa8c993df512f42dd0f1b604978ae89bd747c0ed5ec595c0cc50d535fb2696619ccd98ae28775cc01d0a7c146a532f0f7fb81dc22e1977c242a4912312f4
languageName: node
linkType: hard
"react-refresh@npm:^0.4.0":
version: 0.4.3
resolution: "react-refresh@npm:0.4.3"