feat: implement encrypted items info (#641)
* feat: implement encrypted items info * Update app/assets/javascripts/ui_models/app_state/account_menu_state.ts Co-authored-by: Mo Bitar <mo@standardnotes.org> * Update app/assets/javascripts/ui_models/app_state/account_menu_state.ts Co-authored-by: Mo Bitar <mo@standardnotes.org> * Update app/assets/javascripts/preferences/panes/EndToEndEncryption.tsx Co-authored-by: Mo Bitar <mo@standardnotes.org> * Update app/assets/javascripts/preferences/panes/EndToEndEncryption.tsx Co-authored-by: Mo Bitar <mo@standardnotes.org> Co-authored-by: Mo Bitar <mo@standardnotes.org>
This commit is contained in:
3
app/assets/icons/ic-check-bold.svg
Normal file
3
app/assets/icons/ic-check-bold.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.49995 17.0167L2.32495 11.8417L4.68328 9.48332L7.49995 12.3083L15.7333 4.06665L18.0916 6.42498L7.49995 17.0167Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -38,13 +38,12 @@ export const DecoratedInput: FunctionalComponent<Props> = ({
|
||||
const inputStateClasses = disabled ? 'overflow-ellipsis' : '';
|
||||
return (
|
||||
<div className={`${classes} focus-within:ring-info`}>
|
||||
{left?.map((leftChild, idx, arr) => (
|
||||
{left?.map((leftChild) => (
|
||||
<>
|
||||
{leftChild}
|
||||
{idx < arr.length - 1 && <div className="min-w-3 min-h-1" />}
|
||||
<div className="min-w-2 min-h-1" />
|
||||
</>
|
||||
))}
|
||||
{left !== undefined && <div className="min-w-7 min-h-1" />}
|
||||
<div className="flex-grow">
|
||||
<input
|
||||
type={type}
|
||||
@@ -59,11 +58,10 @@ export const DecoratedInput: FunctionalComponent<Props> = ({
|
||||
autocomplete={autocomplete ? 'on' : 'off'}
|
||||
/>
|
||||
</div>
|
||||
{right !== undefined && <div className="min-w-7 min-h-1" />}
|
||||
{right?.map((rightChild, idx, arr) => (
|
||||
{right?.map((rightChild) => (
|
||||
<>
|
||||
<div className="min-w-3 min-h-1" />
|
||||
{rightChild}
|
||||
{idx < arr.length - 1 && <div className="min-w-3 min-h-1" />}
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@ import CopyIcon from '../../icons/ic-copy.svg';
|
||||
import DownloadIcon from '../../icons/ic-download.svg';
|
||||
import InfoIcon from '../../icons/ic-info.svg';
|
||||
import CheckIcon from '../../icons/ic-check.svg';
|
||||
import CheckBoldIcon from '../../icons/ic-check-bold.svg';
|
||||
|
||||
import { toDirective } from './utils';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
@@ -59,7 +60,8 @@ const ICONS = {
|
||||
copy: CopyIcon,
|
||||
download: DownloadIcon,
|
||||
info: InfoIcon,
|
||||
check: CheckIcon
|
||||
check: CheckIcon,
|
||||
"check-bold": CheckBoldIcon,
|
||||
};
|
||||
|
||||
export type IconType = keyof typeof ICONS;
|
||||
|
||||
@@ -35,6 +35,7 @@ const PaneSelector: FunctionComponent<
|
||||
<Security
|
||||
mfaProvider={props.mfaProvider}
|
||||
userProvider={props.userProvider}
|
||||
appState={props.appState}
|
||||
/>
|
||||
);
|
||||
case 'listed':
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { DecoratedInput } from "@/components/DecoratedInput";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { AppState } from "@/ui_models/app_state";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FunctionComponent } from "preact";
|
||||
import { PreferencesGroup, PreferencesSegment, Text, Title } from "../components";
|
||||
|
||||
const formatCount = (count: number, itemType: string) => `${count} / ${count} ${itemType}`;
|
||||
|
||||
export const EndToEndEncryption: FunctionComponent<{ appState: AppState }> = observer(({ appState }) => {
|
||||
const count = appState.accountMenu.structuredNotesAndTagsCount;
|
||||
const notes = formatCount(count.notes, 'notes');
|
||||
const tags = formatCount(count.tags, 'tags');
|
||||
const archived = formatCount(count.archived, 'archived notes');
|
||||
const deleted = formatCount(count.deleted, 'trashed notes');
|
||||
|
||||
const checkIcon = <Icon className="success min-w-5 min-h-5" type="check-bold" />;
|
||||
const noteIcon = <Icon type="rich-text" className="min-w-5 min-h-5" />;
|
||||
const tagIcon = <Icon type="hashtag" className="min-w-5 min-h-5" />;
|
||||
const archiveIcon = <Icon type="archive" className="min-w-5 min-h-5" />;
|
||||
const trashIcon = <Icon type="trash" className="min-w-5 min-h-5" />;
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>End-to-end encryption</Title>
|
||||
<Text>Your data is encrypted before syncing to your private account.</Text>
|
||||
<div className="flex flex-row pb-1 pt-1.5" >
|
||||
<DecoratedInput disabled={true} text={notes} right={[checkIcon]} left={[noteIcon]} />
|
||||
<div className="min-w-3" />
|
||||
<DecoratedInput disabled={true} text={tags} right={[checkIcon]} left={[tagIcon]} />
|
||||
</div>
|
||||
<div className="flex flex-row" >
|
||||
<DecoratedInput disabled={true} text={archived} right={[checkIcon]} left={[archiveIcon]} />
|
||||
<div className="min-w-3" />
|
||||
<DecoratedInput disabled={true} text={deleted} right={[checkIcon]} left={[trashIcon]} />
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
);
|
||||
});
|
||||
@@ -1,10 +1,17 @@
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { PreferencesPane } from '../components';
|
||||
import { EndToEndEncryption } from './EndToEndEncryption';
|
||||
import { TwoFactorAuthWrapper } from './two-factor-auth';
|
||||
import { MfaProps } from './two-factor-auth/MfaProps';
|
||||
|
||||
export const Security: FunctionComponent<MfaProps> = (props) => (
|
||||
interface SecurityProps extends MfaProps {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export const Security: FunctionComponent<SecurityProps> = (props) => (
|
||||
<PreferencesPane>
|
||||
<EndToEndEncryption appState={props.appState} />
|
||||
<TwoFactorAuthWrapper
|
||||
mfaProvider={props.mfaProvider}
|
||||
userProvider={props.userProvider}
|
||||
|
||||
@@ -3,6 +3,9 @@ import { ApplicationEvent, ContentType } from '@standardnotes/snjs';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { SNItem } from '@standardnotes/snjs/dist/@types/models/core/item';
|
||||
|
||||
type StructuredItemsCount =
|
||||
{ notes: number, tags: number, deleted: number, archived: number };
|
||||
|
||||
export class AccountMenuState {
|
||||
show = false;
|
||||
signingOut = false;
|
||||
@@ -116,4 +119,27 @@ export class AccountMenuState {
|
||||
get notesAndTagsCount(): number {
|
||||
return this.notesAndTags.length;
|
||||
}
|
||||
|
||||
get structuredNotesAndTagsCount(): StructuredItemsCount {
|
||||
const count: StructuredItemsCount = { notes: 0, archived: 0, deleted: 0, tags: 0 };
|
||||
for (const item of this.notesAndTags) {
|
||||
if (item.archived) {
|
||||
count.archived++;
|
||||
}
|
||||
|
||||
if (item.trashed) {
|
||||
count.deleted++;
|
||||
}
|
||||
|
||||
if (item.content_type === ContentType.Note) {
|
||||
count.notes++;
|
||||
}
|
||||
|
||||
if (item.content_type === ContentType.Tag) {
|
||||
count.tags++;
|
||||
}
|
||||
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +190,10 @@
|
||||
min-width: 1.25rem;
|
||||
}
|
||||
|
||||
.min-w-6 {
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
|
||||
.min-w-7 {
|
||||
min-width: 1.75rem;
|
||||
}
|
||||
@@ -222,10 +226,18 @@
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
|
||||
.pt-1\.5 {
|
||||
padding-top: 0.375rem;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.px-9 {
|
||||
padding-left: 2.25rem;
|
||||
padding-right: 2.25rem;
|
||||
|
||||
Reference in New Issue
Block a user