Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patches4 #659

Merged
merged 3 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions ui/design/src/components/Bars/topbar/mobile-search-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react';
import { Box, TextInput } from 'grommet';
import { Icon } from '../../Icon';

export interface ISearchBar {
inputValue: string;
setInputValue: any;
inputPlaceholderLabel?: string;
handleKeyDown: (ev: React.KeyboardEvent<HTMLInputElement>) => void;
handleCloseInput: () => void;
}

const MobileSearchBar: React.FC<ISearchBar> = props => {
const {
inputValue,
setInputValue,
inputPlaceholderLabel,
handleKeyDown,
handleCloseInput,
} = props;

return (
<Box
direction="row"
align="center"
pad={{ vertical: 'xsmall', horizontal: 'small' }}
fill="horizontal"
>
<Icon type="arrowLeft" primaryColor={true} size="xs" onClick={handleCloseInput} />
<TextInput
autoFocus={true}
size="xsmall"
value={inputValue}
onChange={ev => {
setInputValue(ev.target.value);
}}
placeholder={inputPlaceholderLabel}
plain={true}
onKeyDown={handleKeyDown}
/>
{inputValue ? (
<Icon
type="close"
size="xs"
onClick={() => {
setInputValue('');
}}
primaryColor={true}
/>
) : (
<Icon type="search" size="xs" />
)}
</Box>
);
};

MobileSearchBar.defaultProps = {
inputPlaceholderLabel: 'Search profiles or topics',
};

export { MobileSearchBar };
51 changes: 41 additions & 10 deletions ui/design/src/components/Bars/topbar/topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Box, Stack } from 'grommet';
import * as React from 'react';
import { Icon } from '../../Icon';
import { SearchBar } from './search-bar';
import { MobileSearchBar } from './mobile-search-bar';
import { Avatar } from '../../Avatar/index';
import { IMenuItem } from '@akashaproject/ui-awf-typings/lib/app-loader';
import { LogoTypeSource } from '@akashaproject/ui-awf-typings/lib/';
Expand Down Expand Up @@ -74,6 +75,7 @@ const Topbar = (props: ITopbarProps) => {
const [dropOpen, setDropOpen] = React.useState(false);
const [avatarDropOpen, setAvatarDropOpen] = React.useState(false);
const [dropItems, setDropItems] = React.useState<IMenuItem[]>([]);
const [mobileSearchOpen, setMobileSearchOpen] = React.useState(false);

const [currentDropItem, setCurrentDropItem] = React.useState<IMenuItem | null>(null);

Expand Down Expand Up @@ -213,7 +215,45 @@ const Topbar = (props: ITopbarProps) => {
</StyledDiv>
);

const renderSearchArea = () => {
if (searchAreaItem) {
if (size === 'small') {
return (
<Icon
type="search"
size="xs"
onClick={() => {
setMobileSearchOpen(true);
}}
/>
);
}
return (
<StyledSearchContainer>
<SearchBar
inputValue={inputValue}
onInputChange={ev => setInputValue(ev.target.value)}
handleKeyDown={ev => onSearch(ev, inputValue)}
inputPlaceholderLabel={searchBarLabel}
/>
</StyledSearchContainer>
);
}
return;
};

const renderContent = () => {
if (size === 'small' && mobileSearchOpen) {
return (
<MobileSearchBar
inputValue={inputValue}
setInputValue={setInputValue}
handleKeyDown={ev => onSearch(ev, inputValue)}
inputPlaceholderLabel={searchBarLabel}
handleCloseInput={() => setMobileSearchOpen(false)}
/>
);
}
return (
<>
<Box
Expand All @@ -230,16 +270,7 @@ const Topbar = (props: ITopbarProps) => {
</Box>

<Box direction="row" align="center" gap="small" pad={{ horizontal: 'medium' }}>
{searchAreaItem && (
<StyledSearchContainer>
<SearchBar
inputValue={inputValue}
onInputChange={event => setInputValue(event.target.value)}
handleKeyDown={ev => onSearch(ev, inputValue)}
inputPlaceholderLabel={searchBarLabel}
/>
</StyledSearchContainer>
)}
{renderSearchArea()}
{quickAccessItems && quickAccessItems.map(renderPluginButton)}
{!ethAddress && (
<Box direction="row" align="center" gap="small">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const ProfileMiniCard: React.FC<IProfileMiniCard> = props => {
<Box
round="xsmall"
direction="column"
background="ultraLightBackground"
border={{ side: 'all', color: 'border', size: 'xsmall', style: 'solid' }}
>
<Box
Expand Down
5 changes: 3 additions & 2 deletions ui/hooks/src/use-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface UseSearchProps {
postsService: any;
}

/* A hook to get notifications and mark them as read */
/* A hook to get search results and resolve the data within */
export const useSearch = (props: UseSearchProps): [UseSearchState, UseSearchActions] => {
const { onError, logger, ipfsService, profileService, postsService } = props;
const [searchResultsState, setSearchResultsState] = React.useState<UseSearchState>({
Expand Down Expand Up @@ -80,7 +80,7 @@ export const useSearch = (props: UseSearchProps): [UseSearchState, UseSearchActi
return mapEntry(entryResp.data?.getPost, ipfsGatewayResp.data, logger);
});

// get posts data
// get comments data
const getCommentsCalls = searchResp.data?.globalSearch?.comments?.map(
(comment: { id: string }) => postsService.comments.getComment({ commentID: comment.id }),
);
Expand All @@ -90,6 +90,7 @@ export const useSearch = (props: UseSearchProps): [UseSearchState, UseSearchActi
return mapEntry(commentResp.data?.getComment, ipfsGatewayResp.data, logger);
});

// get tags data
const completeTags = searchResp.data?.globalSearch?.tags?.map(
(tag: { id: string; name: string }) => tag.name,
);
Expand Down
39 changes: 9 additions & 30 deletions ui/plugins/search/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import React, { PureComponent } from 'react';
import DS from '@akashaproject/design-system';
import { I18nextProvider } from 'react-i18next';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import SearchPage from './search-page';
import { rootRoute } from '../routes';
import Routes from './routes';
import { RootComponentProps } from '@akashaproject/ui-awf-typings/src';

const { Box, lightTheme, ThemeSelector, ViewportSizeProvider } = DS;
const { Box, lightTheme, ThemeSelector, ViewportSizeProvider, Helmet } = DS;

export interface IProps {
singleSpa: any;
activeWhen: {
path: string;
};

mountParcel: (config: any, props: any) => void;
rootNodeId: string;
sdkModules: any;
globalChannel: any;
logger: any;
i18n?: any;
}

class App extends PureComponent<IProps> {
class App extends PureComponent<RootComponentProps> {
public state: { hasErrors: boolean };

constructor(props: IProps) {
constructor(props: RootComponentProps) {
super(props);
this.state = {
hasErrors: false,
Expand All @@ -50,17 +35,11 @@ class App extends PureComponent<IProps> {
<React.Suspense fallback={<>Loading</>}>
<ThemeSelector availableThemes={[lightTheme]} settings={{ activeTheme: 'Light-Theme' }}>
<I18nextProvider i18n={i18n ? i18n : null}>
<Helmet>
<title>Search</title>
</Helmet>
<ViewportSizeProvider>
<Router>
<Route path={`${rootRoute}/:searchKeyword`}>
<SearchPage
logger={this.props.logger}
sdkModules={this.props.sdkModules}
singleSpa={this.props.singleSpa}
globalChannel={this.props.globalChannel}
/>
</Route>
</Router>
<Routes {...this.props} />
</ViewportSizeProvider>
</I18nextProvider>
</ThemeSelector>
Expand Down
89 changes: 89 additions & 0 deletions ui/plugins/search/src/components/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import DS from '@akashaproject/design-system';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { rootRoute } from '../../routes';
import SearchPage from './search-page';
import { RootComponentProps } from '@akashaproject/ui-awf-typings';
import { useLoginState, useModalState, useErrors } from '@akashaproject/ui-awf-hooks';

const { Box, LoginModal } = DS;

const Routes: React.FC<RootComponentProps> = props => {
const [errorState, errorActions] = useErrors({ logger: props.logger });

const [loginState, loginActions] = useLoginState({
globalChannel: props.globalChannel,
authService: props.sdkModules.auth.authService,
profileService: props.sdkModules.profiles.profileService,
ipfsService: props.sdkModules.commons.ipfsService,
onError: errorActions.createError,
});

const [modalState, modalStateActions] = useModalState({
initialState: {
updateProfile: false,
changeUsername: false,
changeENS: false,
reportModal: false,
},
isLoggedIn: !!loginState.ethAddress,
});

const { t } = useTranslation();

const loginErrors: string | null = React.useMemo(() => {
if (errorState && Object.keys(errorState).length) {
const txt = Object.keys(errorState)
.filter(key => key.split('.')[0] === 'useLoginState')
.map(k => errorState![k])
.reduce((acc, errObj) => `${acc}\n${errObj.error.message}`, '');
return txt;
}
return null;
}, [errorState]);

const showLoginModal = () => {
modalStateActions.show('loginModal');
};

const hideLoginModal = () => {
modalStateActions.hide('loginModal');
};
const handleTutorialLinkClick = () => {
/* goto tutorials */
};

return (
<Router>
<Box>
<Switch>
<Route path={`${rootRoute}/:searchKeyword`}>
<SearchPage
logger={props.logger}
sdkModules={props.sdkModules}
singleSpa={props.singleSpa}
globalChannel={props.globalChannel}
loggedEthAddress={loginState.ethAddress}
showLoginModal={showLoginModal}
/>
</Route>
</Switch>
</Box>
<LoginModal
showModal={modalState.loginModal}
slotId={props.layout.app.modalSlotId}
onLogin={loginActions.login}
onModalClose={hideLoginModal}
tutorialLinkLabel={t('Tutorial')}
metamaskModalHeadline={t('Connecting')}
metamaskModalMessage={t('Please complete the process in your wallet')}
onTutorialLinkClick={handleTutorialLinkClick}
helpText={t('What is a wallet? How do i get an Ethereum address?')}
error={loginErrors}
/>
</Router>
);
};

export default Routes;
Loading