Skip to content

Commit

Permalink
feat: Purchase "Create account" & "Sign in" flows and Floating label …
Browse files Browse the repository at this point in the history
…input (#672)
  • Loading branch information
amanharwara authored Oct 19, 2021
1 parent 7f1dddf commit f9b1526
Show file tree
Hide file tree
Showing 17 changed files with 1,079 additions and 2 deletions.
4 changes: 3 additions & 1 deletion app/assets/javascripts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import { IconDirective } from './components/Icon';
import { NoteTagsContainerDirective } from './components/NoteTagsContainer';
import { PreferencesDirective } from './preferences';
import { AppVersion, IsWebPlatform } from '@/version';
import { PurchaseFlowDirective } from './purchaseFlow';
import { QuickSettingsMenuDirective } from './components/QuickSettingsMenu';

function reloadHiddenFirefoxTab(): boolean {
Expand Down Expand Up @@ -165,7 +166,8 @@ const startApplication: StartApplication = async function startApplication(
.directive('notesOptionsPanel', NotesOptionsPanelDirective)
.directive('icon', IconDirective)
.directive('noteTagsContainer', NoteTagsContainerDirective)
.directive('preferences', PreferencesDirective);
.directive('preferences', PreferencesDirective)
.directive('purchaseFlow', PurchaseFlowDirective);

// Filters
angular.module('app').filter('trusted', ['$sce', trusted]);
Expand Down
75 changes: 75 additions & 0 deletions app/assets/javascripts/components/FloatingLabelInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { FunctionComponent, Ref } from 'preact';
import { JSXInternal } from 'preact/src/jsx';
import { forwardRef } from 'preact/compat';
import { useState } from 'preact/hooks';

type Props = {
id: string;
type: 'text' | 'email' | 'password'; // Have no use cases for other types so far
label: string;
value: string;
onChange: JSXInternal.GenericEventHandler<HTMLInputElement>;
disabled?: boolean;
className?: string;
labelClassName?: string;
inputClassName?: string;
isInvalid?: boolean;
};

export const FloatingLabelInput: FunctionComponent<Props> = forwardRef(
(
{
id,
type,
label,
disabled,
value,
isInvalid,
onChange,
className = '',
labelClassName = '',
inputClassName = '',
},
ref: Ref<HTMLInputElement>
) => {
const [focused, setFocused] = useState(false);

const BASE_CLASSNAME = `relative bg-default`;

const LABEL_CLASSNAME = `hidden absolute ${
!focused ? 'color-neutral' : 'color-info'
} ${focused || value ? 'flex top-0 left-2 pt-1.5 px-1' : ''} ${
isInvalid ? 'color-dark-red' : ''
} ${labelClassName}`;

const INPUT_CLASSNAME = `w-full h-full ${
focused || value ? 'pt-6 pb-2' : 'py-2.5'
} px-3 text-input border-1 border-solid border-gray-300 rounded placeholder-medium text-input focus:ring-info ${
isInvalid ? 'border-dark-red placeholder-dark-red' : ''
} ${inputClassName}`;

const handleFocus = () => setFocused(true);

const handleBlur = () => setFocused(false);

return (
<div className={`${BASE_CLASSNAME} ${className}`}>
<label htmlFor={id} className={LABEL_CLASSNAME}>
{label}
</label>
<input
id={id}
className={INPUT_CLASSNAME}
placeholder={!focused ? label : ''}
type={type}
value={value}
onChange={onChange}
onFocus={handleFocus}
onBlur={handleBlur}
ref={ref}
disabled={disabled}
/>
</div>
);
}
);
68 changes: 68 additions & 0 deletions app/assets/javascripts/purchaseFlow/PurchaseFlowView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { PurchaseFlowPane } from '@/ui_models/app_state/purchase_flow_state';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { CreateAccount } from './panes/CreateAccount';
import { SignIn } from './panes/SignIn';
import SNLogoFull from '../../svg/ic-sn-logo-full.svg';

type PaneSelectorProps = {
currentPane: PurchaseFlowPane;
} & PurchaseFlowViewProps;

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

const PurchaseFlowPaneSelector: FunctionComponent<PaneSelectorProps> = ({
currentPane,
appState,
application,
}) => {
switch (currentPane) {
case PurchaseFlowPane.CreateAccount:
return <CreateAccount appState={appState} application={application} />;
case PurchaseFlowPane.SignIn:
return <SignIn appState={appState} application={application} />;
}
};

export const PurchaseFlowView: FunctionComponent<PurchaseFlowViewProps> =
observer(({ appState, application }) => {
const { currentPane } = appState.purchaseFlow;

return (
<div className="flex items-center justify-center h-full w-full absolute top-left-0 z-index-purchase-flow bg-grey-2">
<div className="relative fit-content">
<div className="relative p-12 mb-4 bg-default border-1 border-solid border-gray-300 rounded">
<SNLogoFull className="mb-5" />
<PurchaseFlowPaneSelector
currentPane={currentPane}
appState={appState}
application={application}
/>
</div>
<div className="flex justify-end">
<a
className="mr-3 font-medium color-grey-1"
href="https://standardnotes.com/privacy"
target="_blank"
rel="noopener noreferrer"
>
Privacy
</a>
<a
className="font-medium color-grey-1"
href="https://standardnotes.com/help"
target="_blank"
rel="noopener noreferrer"
>
Help
</a>
</div>
</div>
</div>
);
});
33 changes: 33 additions & 0 deletions app/assets/javascripts/purchaseFlow/PurchaseFlowWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { isDesktopApplication } from '@/utils';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { PurchaseFlowView } from './PurchaseFlowView';

export type PurchaseFlowWrapperProps = {
appState: AppState;
application: WebApplication;
};

export const loadPurchaseFlowUrl = async (
application: WebApplication
): Promise<void> => {
const url = await application.getPurchaseFlowUrl();
if (url) {
const currentUrl = window.location.href.split('/?')[0];
const successUrl = isDesktopApplication()
? `standardnotes://${currentUrl}`
: currentUrl;
window.location.assign(`${url}&success_url=${successUrl}`);
}
};

export const PurchaseFlowWrapper: FunctionComponent<PurchaseFlowWrapperProps> =
observer(({ appState, application }) => {
if (!appState.purchaseFlow.isOpen) {
return null;
}

return <PurchaseFlowView appState={appState} application={application} />;
});
8 changes: 8 additions & 0 deletions app/assets/javascripts/purchaseFlow/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { toDirective } from '@/components/utils';
import {
PurchaseFlowWrapper,
PurchaseFlowWrapperProps,
} from './PurchaseFlowWrapper';

export const PurchaseFlowDirective =
toDirective<PurchaseFlowWrapperProps>(PurchaseFlowWrapper);
Loading

0 comments on commit f9b1526

Please sign in to comment.