From 444c86cb3cc0a3dbb808fad94f94d49fff425497 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 5 Sep 2018 18:20:28 +0200 Subject: [PATCH] Focus RTE when opening reply --- package.json | 1 + src/core/client/framework/helpers/getMe.ts | 4 +- .../framework/lib/bootstrap/TalkContext.tsx | 4 ++ .../framework/lib/bootstrap/createContext.tsx | 2 + src/core/client/framework/lib/browserInfo.ts | 11 ++++++ src/core/client/stream/components/RTE.tsx | 6 ++- .../stream/components/ReplyCommentForm.tsx | 10 ++++- .../ReplyCommentFormContainer.spec.tsx | 5 +++ .../containers/ReplyCommentFormContainer.tsx | 13 ++++++- .../CommentContainer.spec.tsx.snap | 4 +- .../ReplyCommentFormContainer.spec.tsx.snap | 4 ++ .../test/__snapshots__/loadMore.spec.tsx.snap | 10 ++--- .../__snapshots__/permalinkView.spec.tsx.snap | 4 +- ...permalinkViewCommentNotFound.spec.tsx.snap | 2 +- .../__snapshots__/postComment.spec.tsx.snap | 16 ++++---- .../__snapshots__/postReply.spec.tsx.snap | 2 +- .../__snapshots__/renderReplies.spec.tsx.snap | 8 ++-- .../__snapshots__/renderStream.spec.tsx.snap | 4 +- .../showAllReplies.spec.tsx.snap | 10 ++--- src/core/client/stream/test/create.tsx | 1 + src/core/client/stream/test/createNodeMock.ts | 2 + .../client/stream/test/postComment.spec.tsx | 7 ++++ .../client/stream/test/postReply.spec.tsx | 7 ++++ src/core/client/test/mocks.ts | 38 +++++++++++++++++++ src/core/client/test/setup.ts | 23 +---------- src/core/client/tsconfig.json | 1 + 26 files changed, 143 insertions(+), 56 deletions(-) create mode 100644 src/core/client/framework/lib/browserInfo.ts create mode 100644 src/core/client/test/mocks.ts diff --git a/package.json b/package.json index c7eea6abb..15b7f3f58 100644 --- a/package.json +++ b/package.json @@ -151,6 +151,7 @@ "babel-plugin-module-resolver": "^3.1.1", "babel-plugin-relay": "^1.7.0-rc.1", "babel-preset-react-optimize": "^1.0.1", + "bowser": "^1.9.4", "case-sensitive-paths-webpack-plugin": "^2.1.2", "chalk": "^2.4.1", "chokidar": "^2.0.4", diff --git a/src/core/client/framework/helpers/getMe.ts b/src/core/client/framework/helpers/getMe.ts index 07eeb5952..fb5bafcf9 100644 --- a/src/core/client/framework/helpers/getMe.ts +++ b/src/core/client/framework/helpers/getMe.ts @@ -3,7 +3,9 @@ import { Environment, ROOT_ID } from "relay-runtime"; export default function getMe(environment: Environment) { const source = environment.getStore().getSource(); const root = source.get(ROOT_ID)!; - const meKey = Object.keys(root).find(s => s.startsWith("me("))!; + const meKey = Object.keys(root) + .reverse() + .find(s => s.startsWith("me("))!; if (!root[meKey]) { return null; } diff --git a/src/core/client/framework/lib/bootstrap/TalkContext.tsx b/src/core/client/framework/lib/bootstrap/TalkContext.tsx index 81306aadc..7119940ba 100644 --- a/src/core/client/framework/lib/bootstrap/TalkContext.tsx +++ b/src/core/client/framework/lib/bootstrap/TalkContext.tsx @@ -6,6 +6,7 @@ import { MediaQueryMatchers } from "react-responsive"; import { Formatter } from "react-timeago"; import { Environment } from "relay-runtime"; +import { BrowserInfo } from "talk-framework/lib/browserInfo"; import { PostMessageService } from "talk-framework/lib/postMessage"; import { RestClient } from "talk-framework/lib/rest"; import { PymStorage } from "talk-framework/lib/storage"; @@ -51,6 +52,9 @@ export interface TalkContext { /** A pym child that interacts with the pym parent. */ pym?: PymChild; + + /** Browser detection. */ + browserInfo: BrowserInfo; } const { Provider, Consumer } = React.createContext({} as any); diff --git a/src/core/client/framework/lib/bootstrap/createContext.tsx b/src/core/client/framework/lib/bootstrap/createContext.tsx index 3fce90cd3..a36201e12 100644 --- a/src/core/client/framework/lib/bootstrap/createContext.tsx +++ b/src/core/client/framework/lib/bootstrap/createContext.tsx @@ -5,6 +5,7 @@ import { Child as PymChild } from "pym.js"; import React from "react"; import { Formatter } from "react-timeago"; import { Environment, Network, RecordSource, Store } from "relay-runtime"; +import { getBrowserInfo } from "talk-framework/lib/browserInfo"; import { LOCAL_ID } from "talk-framework/lib/relay"; import { createLocalStorage, @@ -121,6 +122,7 @@ export default async function createContext({ sessionStorage: createSessionStorage(), pymLocalStorage: pym && createPymStorage(pym, "localStorage"), pymSessionStorage: pym && createPymStorage(pym, "sessionStorage"), + browserInfo: getBrowserInfo(), }; // Run custom initializations. diff --git a/src/core/client/framework/lib/browserInfo.ts b/src/core/client/framework/lib/browserInfo.ts new file mode 100644 index 000000000..f7f3c9493 --- /dev/null +++ b/src/core/client/framework/lib/browserInfo.ts @@ -0,0 +1,11 @@ +import bowser from "bowser"; + +export interface BrowserInfo { + ios: boolean; +} + +export function getBrowserInfo(): BrowserInfo { + return { + ios: bowser.ios, + }; +} diff --git a/src/core/client/stream/components/RTE.tsx b/src/core/client/stream/components/RTE.tsx index 81b371a35..0feac5db9 100644 --- a/src/core/client/stream/components/RTE.tsx +++ b/src/core/client/stream/components/RTE.tsx @@ -1,6 +1,6 @@ import { Blockquote, Bold, CoralRTE, Italic } from "@coralproject/rte"; import { Localized as LocalizedOriginal } from "fluent-react/compat"; -import React, { StatelessComponent } from "react"; +import React, { Ref, StatelessComponent } from "react"; import { Icon } from "talk-ui/components"; @@ -47,6 +47,8 @@ export interface RTEProps { onChange?: (data: { html: string; text: string }) => void; disabled?: boolean; + + forwardRef?: Ref; } // tslint:disable:jsx-wrap-multiline @@ -83,6 +85,7 @@ const RTE: StatelessComponent = props => { onChange, disabled, defaultValue, + forwardRef, ...rest } = props; return ( @@ -97,6 +100,7 @@ const RTE: StatelessComponent = props => { disabled={disabled} placeholder={placeholder} features={features} + ref={forwardRef} {...rest} /> diff --git a/src/core/client/stream/components/ReplyCommentForm.tsx b/src/core/client/stream/components/ReplyCommentForm.tsx index 000fd45b2..92ae6e9fa 100644 --- a/src/core/client/stream/components/ReplyCommentForm.tsx +++ b/src/core/client/stream/components/ReplyCommentForm.tsx @@ -1,6 +1,12 @@ +import { CoralRTE } from "@coralproject/rte"; import { FormState } from "final-form"; import { Localized } from "fluent-react/compat"; -import React, { EventHandler, MouseEvent, StatelessComponent } from "react"; +import React, { + EventHandler, + MouseEvent, + Ref, + StatelessComponent, +} from "react"; import { Field, Form, FormSpy } from "react-final-form"; import { OnSubmit } from "talk-framework/lib/form"; @@ -26,6 +32,7 @@ export interface ReplyCommentFormProps { onCancel?: EventHandler>; onChange?: (state: FormState) => void; initialValues?: FormProps; + rteRef?: Ref; } const ReplyCommentForm: StatelessComponent = props => { @@ -58,6 +65,7 @@ const ReplyCommentForm: StatelessComponent = props => { onChange={({ html }) => input.onChange(html)} value={input.value} placeholder="Write a reply" + forwardRef={props.rteRef} /> {meta.touched && diff --git a/src/core/client/stream/containers/ReplyCommentFormContainer.spec.tsx b/src/core/client/stream/containers/ReplyCommentFormContainer.spec.tsx index 0f437cb17..ed6c42b7c 100644 --- a/src/core/client/stream/containers/ReplyCommentFormContainer.spec.tsx +++ b/src/core/client/stream/containers/ReplyCommentFormContainer.spec.tsx @@ -31,6 +31,7 @@ it("renders correctly", async () => { id: "comment-id", }, pymSessionStorage: createFakePymStorage(), + autofocus: false, }; const wrapper = shallow(); @@ -50,6 +51,7 @@ it("renders with initialValues", async () => { id: "comment-id", }, pymSessionStorage: createFakePymStorage(), + autofocus: false, }; await props.pymSessionStorage.setItem( @@ -74,6 +76,7 @@ it("save values", async () => { id: "comment-id", }, pymSessionStorage: createFakePymStorage(), + autofocus: false, }; await props.pymSessionStorage.setItem( @@ -111,6 +114,7 @@ it("creates a comment", async () => { }, pymSessionStorage: createFakePymStorage(), onClose: onCloseStub, + autofocus: false, }; await props.pymSessionStorage.setItem( @@ -149,6 +153,7 @@ it("closes on cancel", async () => { }, pymSessionStorage: createFakePymStorage(), onClose: onCloseStub, + autofocus: false, }; await props.pymSessionStorage.setItem( diff --git a/src/core/client/stream/containers/ReplyCommentFormContainer.tsx b/src/core/client/stream/containers/ReplyCommentFormContainer.tsx index 84425c1c8..4bd5f311b 100644 --- a/src/core/client/stream/containers/ReplyCommentFormContainer.tsx +++ b/src/core/client/stream/containers/ReplyCommentFormContainer.tsx @@ -1,3 +1,4 @@ +import { CoralRTE } from "@coralproject/rte"; import React, { Component } from "react"; import { graphql } from "react-relay"; @@ -20,6 +21,7 @@ interface InnerProps { comment: CommentData; asset: AssetData; onClose?: () => void; + autofocus: boolean; } interface State { @@ -36,6 +38,12 @@ export class ReplyCommentFormContainer extends Component { this.init(); } + private handleRTERef = (rte: CoralRTE | null) => { + if (rte && this.props.autofocus) { + rte.focus(); + } + }; + private async init() { const body = await this.props.pymSessionStorage.getItem(this.contextKey); if (body) { @@ -101,12 +109,15 @@ export class ReplyCommentFormContainer extends Component { onChange={this.handleOnChange} initialValues={this.state.initialValues} onCancel={this.handleOnCancel} + rteRef={this.handleRTERef} /> ); } } -const enhanced = withContext(({ pymSessionStorage }) => ({ +const enhanced = withContext(({ pymSessionStorage, browserInfo }) => ({ pymSessionStorage, + // Disable autofocus on ios and enable for the rest. + autofocus: !browserInfo.ios, }))( withCreateCommentMutation( withFragmentContainer({ diff --git a/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap index f7bd9ae74..1d763eb7a 100644 --- a/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap +++ b/src/core/client/stream/containers/__snapshots__/CommentContainer.spec.tsx.snap @@ -14,7 +14,7 @@ exports[`renders body only 1`] = ` `; exports[`renders with initialValues 1`] = ` `; diff --git a/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap index c72a5212a..2b6612877 100644 --- a/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap +++ b/src/core/client/stream/test/__snapshots__/loadMore.spec.tsx.snap @@ -181,7 +181,7 @@ exports[`loads more comments 1`] = ` >