Skip to content

Commit

Permalink
Merge branch 'release/3.6.6' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
arielsvg committed Apr 12, 2021
2 parents adc0441 + 9599f30 commit 4ebbcd4
Show file tree
Hide file tree
Showing 34 changed files with 1,080 additions and 791 deletions.
1 change: 1 addition & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Dev
on:
push:
branches: [ develop ]
workflow_dispatch:

jobs:

Expand Down
3 changes: 3 additions & 0 deletions app/assets/icons/ic_tune.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion app/assets/javascripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { Bridge } from './services/bridge';
import { SessionsModalDirective } from './components/SessionsModal';
import { NoAccountWarningDirective } from './components/NoAccountWarning';
import { NoProtectionsdNoteWarningDirective } from './components/NoProtectionsNoteWarning';
import { SearchOptionsDirective } from './components/SearchOptions';

function reloadHiddenFirefoxTab(): boolean {
/**
Expand Down Expand Up @@ -145,7 +146,8 @@ const startApplication: StartApplication = async function startApplication(
.directive('syncResolutionMenu', () => new SyncResolutionMenu())
.directive('sessionsModal', SessionsModalDirective)
.directive('noAccountWarning', NoAccountWarningDirective)
.directive('protectedNotePanel', NoProtectionsdNoteWarningDirective);
.directive('protectedNotePanel', NoProtectionsdNoteWarningDirective)
.directive('searchOptions', SearchOptionsDirective);

// Filters
angular.module('app').filter('trusted', ['$sce', trusted]);
Expand Down
4 changes: 3 additions & 1 deletion app/assets/javascripts/components/NoAccountWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { AppState } from '@/ui_models/app_state';
type Props = { appState: AppState };

function NoAccountWarning({ appState }: Props) {
const canShow = useAutorunValue(() => appState.noAccountWarning.show);
const canShow = useAutorunValue(() => appState.noAccountWarning.show, [
appState,
]);
if (!canShow) {
return null;
}
Expand Down
114 changes: 114 additions & 0 deletions app/assets/javascripts/components/SearchOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { AppState } from '@/ui_models/app_state';
import { toDirective, useAutorunValue } from './utils';
import { useRef, useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application';
import VisuallyHidden from '@reach/visually-hidden';
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from '@reach/disclosure';
import { FocusEvent } from 'react';
import { Switch } from './Switch';
import TuneIcon from '../../icons/ic_tune.svg';

type Props = {
appState: AppState;
application: WebApplication;
};

function SearchOptions({ appState }: Props) {
const { searchOptions } = appState;

const {
includeProtectedContents,
includeArchived,
includeTrashed,
} = useAutorunValue(
() => ({
includeProtectedContents: searchOptions.includeProtectedContents,
includeArchived: searchOptions.includeArchived,
includeTrashed: searchOptions.includeTrashed,
}),
[searchOptions]
);

const [
togglingIncludeProtectedContents,
setTogglingIncludeProtectedContents,
] = useState(false);

async function toggleIncludeProtectedContents() {
setTogglingIncludeProtectedContents(true);
try {
await searchOptions.toggleIncludeProtectedContents();
} finally {
setTogglingIncludeProtectedContents(false);
}
}

const [open, setOpen] = useState(false);
const [optionsPanelTop, setOptionsPanelTop] = useState(0);
const buttonRef = useRef<HTMLButtonElement>();
const panelRef = useRef<HTMLDivElement>();

function closeOnBlur(event: FocusEvent<HTMLElement>) {
if (
!togglingIncludeProtectedContents &&
!panelRef.current.contains(event.relatedTarget as Node)
) {
setOpen(false);
}
}

return (
<Disclosure
open={open}
onChange={() => {
const { height } = buttonRef.current.getBoundingClientRect();
setOptionsPanelTop(height);
setOpen((prevOpen) => !prevOpen);
}}
>
<DisclosureButton
ref={buttonRef}
onBlur={closeOnBlur}
className="sn-icon-button color-neutral hover:color-info"
>
<VisuallyHidden>Search options</VisuallyHidden>
<TuneIcon className="fill-current block" />
</DisclosureButton>
<DisclosurePanel
ref={panelRef}
style={{
top: optionsPanelTop,
}}
className="sn-dropdown sn-dropdown-anchor-right grid gap-2 py-2"
>
<Switch
checked={includeProtectedContents}
onChange={toggleIncludeProtectedContents}
onBlur={closeOnBlur}
>
<p className="capitalize">Include protected contents</p>
</Switch>
<Switch
checked={includeArchived}
onChange={searchOptions.toggleIncludeArchived}
onBlur={closeOnBlur}
>
<p className="capitalize">Include archived notes</p>
</Switch>
<Switch
checked={includeTrashed}
onChange={searchOptions.toggleIncludeTrashed}
onBlur={closeOnBlur}
>
<p className="capitalize">Include trashed notes</p>
</Switch>
</DisclosurePanel>
</Disclosure>
);
}

export const SearchOptionsDirective = toDirective<Props>(SearchOptions);
39 changes: 20 additions & 19 deletions app/assets/javascripts/components/SessionsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
AlertDialogDescription,
AlertDialogLabel,
} from '@reach/alert-dialog';
import { toDirective, useAutorun } from './utils';
import { toDirective, useAutorunValue } from './utils';
import { WebApplication } from '@/ui_models/application';

type Session = RemoteSession & {
Expand Down Expand Up @@ -211,22 +211,22 @@ const SessionsModal: FunctionComponent<{
<p>{SessionStrings.RevokeText}</p>
</AlertDialogDescription>
<div className="flex my-1 gap-2">
<button
className="sn-button neutral sk-label"
ref={cancelRevokeRef}
onClick={closeRevokeSessionAlert}
>
<span>{SessionStrings.RevokeCancelButton}</span>
</button>
<button
className="sn-button danger sk-label"
onClick={() => {
closeRevokeSessionAlert();
revokeSession(confirmRevokingSessionUuid);
}}
>
<span>{SessionStrings.RevokeConfirmButton}</span>
</button>
<button
className="sn-button neutral sk-label"
ref={cancelRevokeRef}
onClick={closeRevokeSessionAlert}
>
<span>{SessionStrings.RevokeCancelButton}</span>
</button>
<button
className="sn-button danger sk-label"
onClick={() => {
closeRevokeSessionAlert();
revokeSession(confirmRevokingSessionUuid);
}}
>
<span>{SessionStrings.RevokeConfirmButton}</span>
</button>
</div>
</div>
</div>
Expand All @@ -243,8 +243,9 @@ const Sessions: FunctionComponent<{
appState: AppState;
application: WebApplication;
}> = ({ appState, application }) => {
const [showModal, setShowModal] = useState(false);
useAutorun(() => setShowModal(appState.isSessionsModalVisible));
const showModal = useAutorunValue(() => appState.isSessionsModalVisible, [
appState,
]);

if (showModal) {
return <SessionsModal application={application} appState={appState} />;
Expand Down
48 changes: 48 additions & 0 deletions app/assets/javascripts/components/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ComponentChildren, FunctionalComponent } from 'preact';
import { useState } from 'preact/hooks';
import { HTMLProps } from 'react';
import {
CustomCheckboxContainer,
CustomCheckboxInput,
CustomCheckboxInputProps,
} from '@reach/checkbox';
import '@reach/checkbox/styles.css';

export type SwitchProps = HTMLProps<HTMLInputElement> & {
checked?: boolean;
onChange: (checked: boolean) => void;
children: ComponentChildren;
};

export const Switch: FunctionalComponent<SwitchProps> = (
props: SwitchProps
) => {
const [checkedState, setChecked] = useState(props.checked || false);
const checked = props.checked ?? checkedState;
return (
<label className="sn-component flex justify-between items-center cursor-pointer hover:bg-contrast py-2 px-3">
{props.children}
<CustomCheckboxContainer
checked={checked}
onChange={(event) => {
setChecked(event.target.checked);
props.onChange(event.target.checked);
}}
className={`sn-switch ${checked ? 'bg-info' : 'bg-secondary-contrast'}`}
>
<CustomCheckboxInput
{...({
...props,
children: undefined,
} as CustomCheckboxInputProps)}
/>
<span
aria-hidden
className={`sn-switch-handle ${
checked ? 'sn-switch-handle-right' : ''
}`}
/>
</CustomCheckboxContainer>
</label>
);
};
33 changes: 13 additions & 20 deletions app/assets/javascripts/components/utils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { autorun, IAutorunOptions, IReactionPublic } from 'mobx';
import { autorun } from 'mobx';
import { FunctionComponent, h, render } from 'preact';
import { useEffect } from 'preact/hooks';
import { useState } from 'react';
import { Inputs, useEffect, useState } from 'preact/hooks';

export function useAutorunValue<T>(query: () => T): T {
export function useAutorunValue<T>(query: () => T, inputs: Inputs): T {
const [value, setValue] = useState(query);
useAutorun(() => {
setValue(query());
});
useEffect(() => {
return autorun(() => {
setValue(query());
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, inputs);
return value;
}

export function useAutorun(
view: (r: IReactionPublic) => unknown,
opts?: IAutorunOptions
): void {
useEffect(() => autorun(view, opts), [view, opts]);
}

export function toDirective<Props>(
component: FunctionComponent<Props>,
scope: Record<string, '=' | '&'> = {}
Expand All @@ -32,12 +25,12 @@ export function toDirective<Props>(
'$scope',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
($element: JQLite, $scope: any) => {
if ($scope.class) {
$element.addClass($scope.class);
}
return {
$onChanges() {
render(
h(component, $scope),
$element[0]
);
render(h(component, $scope), $element[0]);
},
};
},
Expand Down
Loading

0 comments on commit 4ebbcd4

Please sign in to comment.