Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
feat(webpush): implemented webpush variant details (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
ziccardi authored and secondsun committed Sep 16, 2020
1 parent a9ea8c1 commit bd40249
Show file tree
Hide file tree
Showing 10 changed files with 407 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/application/ApplicationDetail/ApplicationDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ export class ApplicationDetail extends Component<Props> {
<Tab eventKey={0} title="Variants">
<NoVariantsPanel app={this.props.app} />
<VariantsPanel app={this.props.app} variantType="android" />
<VariantsPanel app={this.props.app} variantType="ios_token" />
<VariantsPanel app={this.props.app} variantType="ios" />
<VariantsPanel app={this.props.app} variantType="ios_token" />
<VariantsPanel app={this.props.app} variantType="web_push" />
</Tab>
</Tabs>
</Modal>
Expand Down
10 changes: 9 additions & 1 deletion src/application/ApplicationDetail/panels/CodeSnippet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import React, { Component } from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import { UpsClientFactory } from '../../../utils/UpsClientFactory';
import { AndroidVariant, Variant } from '@aerogear/unifiedpush-admin-client';
import {
AndroidVariant,
Variant,
WebPushVariant,
} from '@aerogear/unifiedpush-admin-client';

interface Props {
variant: Variant;
Expand All @@ -24,6 +28,10 @@ export class CodeSnippet extends Component<Props> {
template
.replace('__VARIANTID__', this.props.variant.variantID!)
.replace('__VARIANT_SECRET__', this.props.variant.secret!)
.replace(
'__VAPID_PUBLIC_KEY__',
(this.props.variant as WebPushVariant).publicKey || ''
)
.replace(
'__SENDERID__',
(this.props.variant as AndroidVariant).projectNumber || ''
Expand Down
51 changes: 51 additions & 0 deletions src/application/ApplicationDetail/panels/FormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { Component } from 'react';
import { FormGroup, TextArea, TextInput } from '@patternfly/react-core';

interface Props {
fieldId: string;
label?: string;
helperText?: string;
helperTextInvalid?: string;
validated?: 'success' | 'error' | 'default';
onChange?: (value: string) => void;
defaultValue?: string;
component?: 'textarea' | 'textinput';
}

export class FormField extends Component<Props> {
readonly render = () => {
const component = () => {
if (this.props.component === 'textarea') {
return (
<TextArea
type="text"
defaultValue={this.props.defaultValue}
onChange={this.props.onChange}
validated={this.props.validated}
/>
);
} else {
return (
<TextInput
type="text"
defaultValue={this.props.defaultValue}
onChange={this.props.onChange}
validated={this.props.validated}
/>
);
}
};

return (
<FormGroup
fieldId={this.props.fieldId}
label={this.props.label}
helperText={this.props.helperText}
helperTextInvalid={this.props.helperTextInvalid}
validated={this.props.validated}
>
{component()}
</FormGroup>
);
};
}
17 changes: 17 additions & 0 deletions src/application/ApplicationDetail/panels/VariantDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { IOSTokenVariantDetails } from './ios_token/iOSTokenVariantDetails';
import { IOSTokenCodeSnippets } from './ios_token/iOSTokenCodeSnippets';
import { IOSCertVariantDetails } from './ios_cert/iOSCertVariantDetails';
import { IOSCertCodeSnippets } from './ios_cert/iOSCertCodeSnippets';
import { WebPushVariantDetails } from './web_push/WebPushVariantDetails';
import { WebPushCodeSnippets } from './web_push/WebPushCodeSnippets';

interface Props {
app: PushApplication;
Expand Down Expand Up @@ -92,6 +94,12 @@ export class VariantDetails extends Component<Props, State> {
guides for push.
</Text>
);
case 'web_push':
return (
<Text component={TextVariants.small}>
Your user's browser will determine the services used.
</Text>
);
default:
return <Text component={TextVariants.small} />;
}
Expand Down Expand Up @@ -159,6 +167,11 @@ export class VariantDetails extends Component<Props, State> {
app={this.props.app}
variant={this.props.variant}
/>

<WebPushVariantDetails
app={this.props.app}
variant={this.props.variant}
/>
</TextContent>
<AndroidCodeSnippets
app={this.props.app}
Expand All @@ -172,6 +185,10 @@ export class VariantDetails extends Component<Props, State> {
app={this.props.app}
variant={this.props.variant}
/>
<WebPushCodeSnippets
app={this.props.app}
variant={this.props.variant}
/>
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/application/ApplicationDetail/panels/VariantsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export abstract class VariantsPanel extends Component<Props> {
case 'ios_token':
return 'fab fa-apple fa-3x muted';
case 'web_push':
return '';
return 'fab fa-chrome fa-3x muted';
default:
return '';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React, { Component } from 'react';
import {
PushApplication,
Variant,
WebPushVariant,
WebPushVariantDefinition,
} from '@aerogear/unifiedpush-admin-client';
import {
Button,
ButtonVariant,
Form,
Modal,
ModalVariant,
ValidatedOptions,
} from '@patternfly/react-core';
import { UpsClientFactory } from '../../../../utils/UpsClientFactory';
import {
validatorBuilder,
RuleBuilder,
EvaluationResult,
Data,
Validator,
} from 'json-data-validator';
import { FormField } from '../FormField';

interface Props {
visible: boolean;
app: PushApplication;
variant: WebPushVariant;
onCancel: () => void;
onSaved: (variant: Variant) => void;
}

interface State {
updating: boolean;
privateKey?: string;
publicKey?: string;
alias?: string;
formValidation?: EvaluationResult;
}

export class EditWebPushNetworkOptions extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
updating: false,
privateKey: this.props.variant.privateKey,
publicKey: this.props.variant.publicKey,
alias: this.props.variant.alias,
};
}

componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>) {
if (this.props.visible && prevProps.visible !== this.props.visible) {
this.setState({
updating: false,
formValidation: undefined,
});
}
}

readonly render = () => {
const update = async () => {
await this.setState({ updating: true });

const update: WebPushVariantDefinition = {
privateKey: this.state.privateKey || this.props.variant.privateKey,
publicKey: this.state.publicKey || this.props.variant.publicKey,
alias: this.state.alias || this.props.variant.alias,
};

await UpsClientFactory.getUpsClient()
.variants.web_push.update(
this.props.app.pushApplicationID,
this.props.variant.variantID
)
.withVariantDefinition(update)
.execute();

this.props.variant.publicKey =
update.publicKey ?? this.props.variant.publicKey;
this.props.variant.privateKey =
update.privateKey ?? this.props.variant.privateKey;
this.props.variant.alias = update.alias ?? this.props.variant.alias;

await this.setState({ updating: false });
this.props.onSaved(this.props.variant);
};

const validator: Validator = validatorBuilder()
.newRule()
.withField('publicKey')
.validate(RuleBuilder.required())
.withField('privateKey')
.validate(RuleBuilder.required())
.withField('alias')
.validate(RuleBuilder.required())
.build();

const validationState = (field: string) => {
const evaluationResult = this.state.formValidation?.details?.find(
value => value.field === field
);
if (evaluationResult?.valid) {
return {
valid: true,
status: ValidatedOptions.success,
};
}
if (evaluationResult?.valid === false) {
return {
valid: true,
validationResult: evaluationResult,
status: ValidatedOptions.error,
};
}
return {
valid: true,
status: ValidatedOptions.default,
};
};

const updateField = (name: string, value: string) => {
this.setState(({
[name]: value,
formValidation: validator.validate(
({ ...this.state, [name]: value } as unknown) as Data,
true
),
} as unknown) as State);
};

return (
<Modal
variant={ModalVariant.small}
title="Edit Variant"
isOpen={this.props.visible}
onClose={this.props.onCancel}
actions={[
<Button
key="confirm"
variant={ButtonVariant.primary}
onClick={() => update()}
isDisabled={!this.state.formValidation?.valid}
>
Save
</Button>,
<Button key="cancel" variant="link" onClick={this.props.onCancel}>
Cancel
</Button>,
]}
>
<Form isHorizontal>
<FormField
fieldId={'vapid-public-key'}
label={'Push Network'}
helperText={'Vapid Public Key'}
helperTextInvalid={
validationState('publicKey').validationResult?.message
}
validated={validationState('publicKey').status}
defaultValue={this.props.variant.publicKey}
onChange={(value: string) => updateField('publicKey', value)}
/>

<FormField
fieldId={'vapid-private-key'}
helperText={'Vapid Private Key'}
helperTextInvalid={
validationState('privateKey').validationResult?.message
}
validated={validationState('privateKey').status}
defaultValue={this.props.variant.privateKey}
onChange={(value: string) => updateField('privateKey', value)}
/>
<FormField
fieldId={'alias'}
helperText={'Alias'}
helperTextInvalid={
validationState('alias').validationResult?.message
}
defaultValue={this.props.variant.alias}
onChange={(value: string) => updateField('alias', value)}
validated={validationState('alias').status}
/>
</Form>
</Modal>
);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { PushApplication, Variant } from '@aerogear/unifiedpush-admin-client';
import React, { Component } from 'react';
import { Tab, Tabs } from '@patternfly/react-core';
import { CodeSnippet } from '../CodeSnippet';
import { push_config_webpush } from '../../snippets';

interface Props {
app: PushApplication;
variant: Variant;
}

interface State {
activeCodeSnippets: string;
}

export class WebPushCodeSnippets extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
activeCodeSnippets: 'push-config',
};
}

render = () => {
if (this.props.variant.type !== 'web_push') {
return null;
}

const onTabSelect = (codeSnippetTab: string) => {
this.setState({ activeCodeSnippets: codeSnippetTab });
};

return (
<Tabs
isFilled
activeKey={this.state.activeCodeSnippets}
isBox={false}
onSelect={(
event: React.MouseEvent<HTMLElement, MouseEvent>,
eventKey: number | string
) => onTabSelect(eventKey as string)}
>
<Tab eventKey={'push-config'} title="push-config.json">
<CodeSnippet
variant={this.props.variant}
language={'json'}
snippet={push_config_webpush}
/>
</Tab>
</Tabs>
);
};
}
Loading

0 comments on commit bd40249

Please sign in to comment.