react-i18next#withTranslation TypeScript Examples
The following examples show how to use
react-i18next#withTranslation.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ReplayVideoPage.tsx From whiteboard-demo with MIT License | 6 votes |
public constructor(props: PlayerVideoPageProps & WithTranslation) {
super(props);
this.state = {
currentTime: 0,
phase: PlayerPhase.Pause,
isPlayerSeeking: false,
isVisible: false,
replayFail: false,
replayState: false,
};
this.videoRef = React.createRef();
}
Example #2
Source File: PoweredBy.tsx From clearflask with Apache License 2.0 | 6 votes |
class PoweredBy extends Component<WithTranslation<'app'> & WithStyles<typeof styles, true>> {
render() {
return (
<MuiLink underline='none' target="_blank" href={`https://clearflask.com/?utm_source=${windowIso.location.hostname}&utm_medium=pby_footer`}>
<div className={this.props.classes.container}>
<div className={this.props.classes.poweredBy}>{this.props.t('powered-by')} </div>
<div className={this.props.classes.name}>ClearFlask</div>
</div>
</MuiLink>
);
}
}
Example #3
Source File: PageError.tsx From whiteboard-demo with MIT License | 6 votes |
class PageError extends React.Component<WithTranslation, {}> {
public constructor(props: WithTranslation) {
super(props);
}
public render(): React.ReactNode {
const { t } = this.props
return (
<div className="page404-box">
<div className="page404-image-box">
<img className="page404-image-inner"
src={room_not_find}
alt={"room_not_find"}/>
<div className="page404-inner">
<div className="page404-inner-title">
{t('pageErrorTitle')}
</div>
<div className="page404-inner-script">
{t('pageErrorInner')}
</div>
<Link to={"/"}>
<Button size={"large"} style={{width: 118}}>
{t('backHomePage')}
</Button>
</Link>
</div>
</div>
</div>
);
}
}
Example #4
Source File: BasePage.tsx From clearflask with Apache License 2.0 | 6 votes |
class BasePage extends Component<Props & ConnectProps & WithTranslation<'app'> & WithStyles<typeof styles, true>> {
readonly styles = {
};
render() {
if (!this.props.suppressSetTitle && !!this.props.projectName) {
setAppTitle(this.props.projectName, this.props.t(this.props.titleText as any));
}
return (
<>
<div className={this.props.classes.animationContainer}>
<div className={this.props.classes.page}>
<div className={this.props.classes.anchor}>
{this.props.children}
</div>
</div>
</div>
{!!this.props.showFooter && (
<Footer
customPageSlug={this.props.customPageSlug}
isFrontPage={this.props.isFrontPage}
/>
)}
</>
);
}
}
Example #5
Source File: ReplayPage.tsx From whiteboard-demo with MIT License | 6 votes |
public constructor(props: PlayerPageProps & WithTranslation) {
super(props);
this.state = {
currentTime: 0,
phase: PlayerPhase.Pause,
isPlayerSeeking: false,
isVisible: false,
replayFail: false,
replayState: false,
};
}
Example #6
Source File: my-projects.tsx From microsoft-teams-apps-growyourskills with MIT License | 6 votes |
constructor(props: WithTranslation) {
super(props);
this.localize = this.props.t;
this.state = {
activeIndex: 0,
joinedCount: 0,
createdCount: 0
}
}
Example #7
Source File: Storage.tsx From whiteboard-demo with MIT License | 6 votes |
public constructor(props: WithTranslation) {
super(props);
this.state = {
mode: DownloadingMode.Freedom,
isAdding: false,
nextAddIndex: 0,
pptStates: [],
};
}
Example #8
Source File: ErrorBoundary.tsx From next-core with GNU General Public License v3.0 | 6 votes |
// Ref https://reactjs.org/docs/error-boundaries.html
class LegacyErrorBoundary extends React.Component<
WithTranslation<typeof NS_BRICK_KIT>,
ErrorBoundaryState
> {
constructor(props: WithTranslation<typeof NS_BRICK_KIT>) {
super(props);
this.state = { error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
// Update state so the next render will show the fallback UI.
return { error };
}
/* componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
} */
render(): React.ReactNode {
if (this.state.error) {
// You can render any custom fallback UI
return (
<div data-testid="error-boundary">
<h3>{this.props.t(K.SOMETHING_WENT_WRONG)}</h3>
<p>{httpErrorToString(this.state.error)}</p>
</div>
);
}
return this.props.children;
}
}
Example #9
Source File: H5UploadButton.tsx From whiteboard-demo with MIT License | 6 votes |
constructor(props: H5UploadButtonProps & WithTranslation) {
super(props);
this.state = {
visible: false,
fileList: [],
percent: 0,
startUpload: false,
getSiteTimer: null,
deploying: false
};
const bucket = this.props.region === "us-sv" ? `${h5OssConfigObj.h5Bucket}-us` : h5OssConfigObj.h5Bucket
let region = h5OssConfigObj.h5Region;
this.prefix = h5OssConfigObj.h5Prefix;
if (this.props.region && this.props.region !== "cn-hz") {
region = "oss-us-west-1";
this.prefix = h5OssConfigObj.h5PrefixUs;
}
this.oss = new OSS({
accessKeyId: h5OssConfigObj.h5AccessKeyId,
accessKeySecret: h5OssConfigObj.h5AccessKeySecret,
bucket: bucket,
region: region,
});
}
Example #10
Source File: GeneralLogin.spec.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
i18nProps: WithTranslation = {
t: ((key: string) => key) as any,
i18n: {
language: "zh-CN",
} as any,
tReady: null,
}
Example #11
Source File: ErrorBoundary.tsx From next-core with GNU General Public License v3.0 | 5 votes |
ErrorBoundary: React.ComponentType<
Omit<
Subtract<WithTranslation<typeof NS_BRICK_KIT>, WithTranslationProps>,
keyof WithTranslation<typeof NS_BRICK_KIT>
> &
WithTranslationProps
> = withTranslation(NS_BRICK_KIT)(LegacyErrorBoundary)
Example #12
Source File: AddNamePage.tsx From whiteboard-demo with MIT License | 5 votes |
public constructor(props: AddNamePageProps & WithTranslation) {
super(props);
const {uuid} = this.props.match.params;
this.state = {
name: "",
uuid: uuid ? uuid : "",
};
}
Example #13
Source File: Menu.tsx From clearflask with Apache License 2.0 | 5 votes |
MenuPage = withStyles(styles, { withTheme: true })(withTranslation('app', { withRef: true })(MenuPageWithoutStyle))
Example #14
Source File: HistoryPage.tsx From whiteboard-demo with MIT License | 5 votes |
public constructor(props: RouteComponentProps & WithTranslation) {
super(props);
const rooms = localStorage.getItem("rooms");
this.state = {
rooms: JSON.parse(rooms!),
};
}
Example #15
Source File: message.tsx From protect-scotland with Apache License 2.0 | 5 votes |
Message = withTranslation()(MessageBase)
Example #16
Source File: GeneralLogin.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
InnerGeneralLogin = withTranslation(NS_GENERAL_AUTH)(LegacyGeneralLogin)
Example #17
Source File: NotificationList.tsx From clearflask with Apache License 2.0 | 5 votes |
class NotificationList extends Component<Props & ConnectProps & WithTranslation<'app'> & WithStyles<typeof styles, true> & RouteComponentProps> {
constructor(props) {
super(props);
props.callOnMount?.();
}
render() {
if (!this.props.userId) {
return (<ErrorMsg msg='You need to log in to see your notifications' variant='info' />);
}
const hasNotifications = this.props.notifications && this.props.notifications.length > 0;
return (
<div className={this.props.className}>
<div className={this.props.classes.table}>
<Table size='medium'>
<TableBody>
{!hasNotifications ? (
<Typography
className={this.props.classes.noNotificationsLabel}
variant='overline'
>{this.props.t('no-notifications')}</Typography>
) : this.props.notifications!.map(notification => (
<TableRow
key={notification.notificationId}
hover
>
<Link
to={this.getNotificationTo(notification)}
onClick={() => this.clearNotification(notification)}
>
<TableCell key='date'><Typography><TimeAgo date={notification.created} /></Typography></TableCell>
<TableCell key='description'><Typography>{notification.description}</Typography></TableCell>
</Link>
</TableRow>
))}
</TableBody>
</Table>
</div>
{hasNotifications && (
<Button fullWidth className={this.props.classes.button} onClick={() => this.clearAll()}>
{this.props.t('clear-all')}
</Button>
)}
{this.props.getNextNotifications && (
<Button fullWidth className={this.props.classes.button} onClick={() => this.props.getNextNotifications && this.props.getNextNotifications()}>
{this.props.t('show-more')}
</Button>
)}
</div>
);
}
getNotificationTo(notification: Client.Notification): string {
if (notification.relatedIdeaId) {
if (notification.relatedCommentId) {
return `/post/${notification.relatedIdeaId}/comment/${notification.relatedCommentId}`;
} else {
return `/post/${notification.relatedIdeaId}`;
}
} else {
return `/account`;
}
}
clearNotification(notification: Client.Notification) {
this.props.server.dispatch().then(d => d.notificationClear({
projectId: this.props.server.getProjectId(),
notificationId: notification.notificationId,
}));
}
clearAll() {
this.props.server.dispatch().then(d => d.notificationClearAll({
projectId: this.props.server.getProjectId(),
}));
}
}
Example #18
Source File: BrickForm.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
BrickForm = withTranslation(NS_PRESENTATIONAL_BRICKS)(
Form.create<LegacyBrickFormProps>({ name: "brick_form" })(LegacyBrickForm)
)
Example #19
Source File: SettingsDynamicPage.tsx From clearflask with Apache License 2.0 | 5 votes |
class SettingsDynamicPage extends Component<Props & ConnectProps & WithTranslation<'app'> & WithStyles<typeof styles, true>> {
unsubscribePage?: () => void;
unsubscribeUsedSettings?: () => void;
cachedUsedAdvancedSettings?: boolean;
componentDidMount() {
this.unsubscribePage = this.props.page.subscribe(this.onChangedPage.bind(this));
if (!this.props.editor.getProperty<ConfigEditor.BooleanProperty>(['usedAdvancedSettings']).value) {
this.unsubscribeUsedSettings = this.props.editor.subscribe(this.usedSettings.bind(this));
}
}
componentWillUnmount() {
this.unsubscribePage?.();
this.unsubscribeUsedSettings?.();
}
render() {
const translatedDynamicName = this.props.t(this.props.page.getDynamicName() as any);
setTitle(translatedDynamicName);
const creditPreview = this.props.page.pathStr === 'users.credits'
&& (<CreditPreview editor={this.props.editor} />);
var workflowPreview;
if (this.props.page.path.length > 0 && this.props.page.path[this.props.page.path.length - 1] === 'workflow') {
workflowPreview = (
<WorkflowPreview editor={this.props.editor} categoryIndex={this.props.page.path[2] as number} />
);
}
return (
<div>
<Typography variant='h4' component='h1'>{translatedDynamicName}</Typography>
<Typography variant='body1' component='p'>{this.props.page.description}</Typography>
<PresetWidget page={this.props.page} editor={this.props.editor} />
{creditPreview}
{workflowPreview}
{this.props.page.getChildren().all
.filter(child => !child.hide)
.map(child => (
<Property
server={this.props.server}
key={child.key}
prop={child}
pageClicked={this.props.pageClicked}
width={350}
/>
))}
</div>
);
}
onChangedPage() {
this.forceUpdate();
}
usedSettings() {
this.unsubscribeUsedSettings?.();
this.props.editor.getProperty<ConfigEditor.BooleanProperty>(['usedAdvancedSettings'])
.set(true);
}
}
Example #20
Source File: ErrorBoundary.tsx From next-core with GNU General Public License v3.0 | 5 votes |
ErrorBoundary: React.ComponentType<
Omit<
Subtract<WithTranslation<typeof NS_BRICK_KIT>, WithTranslationProps>,
keyof WithTranslation<typeof NS_BRICK_KIT>
> &
WithTranslationProps
> = withTranslation(NS_BRICK_KIT)(LegacyErrorBoundary)
Example #21
Source File: Crumbs.tsx From clearflask with Apache License 2.0 | 5 votes |
class Crumbs extends Component<Props & WithTranslation<'app'> & WithStyles<typeof styles, true>> {
unsubscribe: { [pathStr: string]: (() => void) } = {};
subscribe(item: ConfigEditor.Page | ConfigEditor.PageGroup | ConfigEditor.Property) {
if (this.unsubscribe[item.pathStr] !== undefined) return;
this.unsubscribe[item.pathStr] = item.subscribe(this.forceUpdate.bind(this));
}
componentWillUnmount() {
Object.values(this.unsubscribe).forEach(u => u());
}
render() {
const crumbs: React.ReactNode[] = [];
if (this.props.crumbs) {
this.props.crumbs.map(crumb => this.createCrumb(crumb.name, crumb.slug));
} else if (this.props.activeProject && this.props.activeProjectSlug) {
const subpath = this.props.activeSubPath || [];
for (let i = 0; i <= subpath.length; i++) {
const currSubPath = subpath.slice(0, i);
const item = this.props.activeProject.editor.get(currSubPath);
if (item.type !== ConfigEditor.PageType) continue;
this.subscribe(item);
const name = (i === 0 && this.props.activeProjectSlugName)
? this.props.activeProjectSlugName : this.props.t(item.getDynamicName() as any);
crumbs.push(this.createCrumb(name, this.props.activeProjectSlug, item.path));
}
}
return (
<Breadcrumbs separator="/" arial-label="Breadcrumb">
{crumbs}
</Breadcrumbs>
);
}
createCrumb(name: string, path: string, subPath?: ConfigEditor.Path) {
return (
<Link
key={path}
className={this.props.classes.link}
color="inherit"
onClick={() => this.props.pageClicked(path, subPath)}
>
{name}
</Link>
);
}
}
Example #22
Source File: InviteButton.tsx From whiteboard-demo with MIT License | 4 votes |
class InviteButton extends React.Component<InviteButtonProps & WithTranslation, InviteButtonStates> {
public constructor(props: InviteButtonProps & WithTranslation) {
super(props);
this.state = {
inviteDisable: false,
};
}
private onVisibleChange = (event: boolean): void => {
if (event) {
this.setState({inviteDisable: true});
} else {
this.setState({inviteDisable: false});
}
}
private handleInvite = (): void => {
this.setState({inviteDisable: !this.state.inviteDisable})
}
private handleCopy = (): void => {
const { t, uuid, region } = this.props;
let url = `https://demo.netless.link/whiteboard/${Identity.joiner}/${uuid}/${region}`;
const h5Url = this.getH5Url();
if (h5Url) {
url = url + `?h5Url=${h5Url}`;
}
this.handleInvite();
copy(`${t('roomNumber')}:${uuid}\n${t('joinLink')}:${url}`);
message.success(t('copyClipboard'));
}
private getH5Url = () => {
const query = new URLSearchParams(window.location.search);
return query.get("h5Url");
}
private renderInviteContent = (): React.ReactNode => {
const { t, uuid, region } = this.props;
const isLocal = location.hostname === "localhost";
const protocol = isLocal ? "http" : "https";
let shareLink = `${protocol}://${location.host}/whiteboard/${Identity.joiner}/${uuid}/${region}`
const h5Url = this.getH5Url();
if (h5Url) {
shareLink = shareLink + `?h5Url=${encodeURIComponent(h5Url)}`;
}
return (
<div className="invite-box">
<div className="invite-box-title">
{t('inviteJoin')}
</div>
<div style={{width: 400, height: 0.5, backgroundColor: "#E7E7E7"}}/>
<div className="invite-text-box">
<div className="invite-url-box" style={{marginBottom: 12}}>
<span style={{width: 96}}>{t('roomNumber')}:</span>
<Input
size={"middle"}
value={uuid}
addonAfter={
<CopyOutlined
onClick={() => {
copy(uuid);
message.success(t('copyUuidMessage'));
}}
/>
}
/>
</div>
<div className="invite-url-box">
<span style={{width: 96}}>{t('joinLink')}:</span>
<Input size={"middle"}
value={shareLink}
addonAfter={
<CopyOutlined
onClick={() => {
copy(shareLink);
message.success(t('copyClipboard'));
}}
/>
}/>
</div>
</div>
<div className="invite-button-box">
<Button onClick={this.handleCopy} style={{width: 164, height: 40}} type={"primary"} size={"middle"}>
{t('copy')}
</Button>
</div>
</div>
);
}
public render(): React.ReactNode {
return (
<Popover visible={this.state.inviteDisable}
trigger="click"
onVisibleChange={this.onVisibleChange}
content={() => this.renderInviteContent()}
placement={"bottomRight"}>
<div className="page-preview-cell" onClick={this.handleInvite}>
<img
style={{width: "28px"}}
src={this.state.inviteDisable ? inviteActive : invite}
alt={"invite"}/>
</div>
</Popover>
);
}
}
Example #23
Source File: skills-aquired-wrapper.tsx From microsoft-teams-apps-growyourskills with MIT License | 4 votes |
class SkillsAcquiredWrapperPage extends React.Component<WithTranslation, ISkillsAcquiredState> {
localize: TFunction;
constructor(props: any) {
super(props);
this.localize = this.props.t;
window.addEventListener("resize", this.update);
this.state = {
isLoading: true,
screenWidth: 0,
projectSkillsDetails:[]
}
}
/**
* Used to initialize Microsoft Teams sdk
*/
async componentDidMount() {
this.setState({ isLoading: true });
this.getProjectSkillsAcquired();
this.update();
}
/**
* get screen width real time
*/
update = () => {
this.setState({
screenWidth: window.innerWidth
});
};
/**
* Fetch projects for user private list tab from API
*/
getProjectSkillsAcquired = async () => {
let response = await getUserAcquiredSkills();
if (response.status === 200 && response.data) {
this.setState({
projectSkillsDetails: response.data
});
}
this.setState({
isLoading: false
});
}
/**
* Renders the component
*/
public render(): JSX.Element {
return (
<div className="container-div">
<div className="container-subdiv">
{this.getWrapperPage()}
</div>
</div>
);
}
/**
*Get wrapper for page which acts as container for all child components
*/
private getWrapperPage = () => {
if (this.state.isLoading) {
return (
<div className="container-div">
<div className="container-subdiv">
<div className="loader">
<Loader />
</div>
</div>
</div>
);
} else {
return this.state.projectSkillsDetails.length ?
<SkillsAcquiredTable
screenWidth={this.state.screenWidth}
projectSkillsDetails={this.state.projectSkillsDetails}/>
: <NoPrivatePost />
}
}
}
Example #24
Source File: ReplayPage.tsx From whiteboard-demo with MIT License | 4 votes |
class NetlessPlayer extends React.Component<PlayerPageProps & WithTranslation, PlayerPageStates> {
public constructor(props: PlayerPageProps & WithTranslation) {
super(props);
this.state = {
currentTime: 0,
phase: PlayerPhase.Pause,
isPlayerSeeking: false,
isVisible: false,
replayFail: false,
replayState: false,
};
}
private getRoomToken = async (uuid: string): Promise<string | null> => {
const roomToken = await netlessWhiteboardApi.room.joinRoomApi(uuid);
return roomToken || null;
};
public async componentDidMount(): Promise<void> {
window.addEventListener("keydown", this.handleSpaceKey);
const {uuid, identity, region} = this.props.match.params;
const plugins = createPlugins({
"video": videoPlugin, "audio": audioPlugin,
"video2": videoPlugin2, "audio2": audioPlugin2,
});
plugins.setPluginContext("video", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("audio", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("video2", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("audio2", {identity: identity === Identity.creator ? "host" : ""});
const roomToken = await this.getRoomToken(uuid);
if (uuid && roomToken) {
const h5Url = getQueryH5Url();
let whiteWebSdkParams: WhiteWebSdkConfiguration = {
appIdentifier: netlessToken.appIdentifier,
preloadDynamicPPT: true,
plugins: plugins,
region,
}
if (h5Url) {
whiteWebSdkParams = Object.assign(whiteWebSdkParams, {
invisiblePlugins: [IframeBridge],
wrappedComponents: [IframeWrapper]
})
}
const whiteWebSdk = new WhiteWebSdk(whiteWebSdkParams);
await this.loadPlayer(whiteWebSdk, uuid, roomToken);
}
}
private loadPlayer = async (whiteWebSdk: WhiteWebSdk, uuid: string, roomToken: string): Promise<void> => {
await polly().waitAndRetry(10).executeForPromise(async () => {
const isPlayable = whiteWebSdk.isPlayable({ room: uuid, roomToken });
if (!isPlayable) {
throw Error("the current room cannot be replay");
}
return;
});
this.setState({replayState: true});
await this.startPlayer(whiteWebSdk, uuid, roomToken);
}
private startPlayer = async (whiteWebSdk: WhiteWebSdk, uuid: string, roomToken: string): Promise<void> => {
const cursorAdapter = new CursorTool();
let firstPlay = false;
const player = await whiteWebSdk.replayRoom(
{
room: uuid,
roomToken: roomToken,
cursorAdapter: cursorAdapter,
}, {
onPhaseChanged: phase => {
this.setState({phase: phase});
if (phase === PlayerPhase.Playing) {
if (!firstPlay) {
setTimeout(() => {
const h5Url = getQueryH5Url();
if (h5Url && (h5Url === h5DemoUrl3)) {
const bridge = player.getInvisiblePlugin(IframeBridge.kind);
new ReplayAdapter(player, bridge as IframeBridge, this.props.match.params.userId, h5Url);
}
}, 500);
}
firstPlay = true;
}
},
onStoppedWithError: (error: Error) => {
message.error(`Playback error: ${error}`);
this.setState({replayFail: true});
},
onProgressTimeChanged: (scheduleTime: number) => {
this.setState({currentTime: scheduleTime});
},
});
(window as any).player = player;
cursorAdapter.setPlayer(player);
this.setState({
player: player,
});
}
private handleBindRoom = (ref: HTMLDivElement): void => {
const {player} = this.state;
if (player) {
player.bindHtmlElement(ref);
}
}
private handleSpaceKey = (evt: any): void => {
if (evt.code === "Space") {
if (this.state.player) {
this.onClickOperationButton(this.state.player);
}
}
}
private onClickOperationButton = (player: Player): void => {
switch (player.phase) {
case PlayerPhase.WaitingFirstFrame:
case PlayerPhase.Pause: {
player.play();
break;
}
case PlayerPhase.Playing: {
player.pause();
break;
}
case PlayerPhase.Ended: {
player.seekToProgressTime(0);
break;
}
}
}
private renderScheduleView(): React.ReactNode {
const {player, isVisible} = this.state;
if (player && isVisible) {
return (
<div onMouseEnter={() => this.setState({isVisible: true})}>
<PlayerController player={player} i18nLanguage={this.props.i18n.language} />
</div>
);
} else {
return null;
}
}
public render(): React.ReactNode {
const { t } = this.props
const {player, phase, replayState} = this.state;
const { identity, uuid, userId, region } = this.props.match.params;
if (this.state.replayFail) {
return <PageError/>;
}
if (!replayState) {
return <LoadingPage text={t('waitingReplayGenerate')}/>;
}
if (player === undefined) {
return <LoadingPage/>;
}
switch (phase) {
case (PlayerPhase.WaitingFirstFrame): {
return <LoadingPage/>;
}
default: {
return (
<div className="player-out-box">
<div className="logo-box">
<img src={logo} alt={"logo"}/>
</div>
<div className="player-board">
{this.renderScheduleView()}
<div
className="player-board-inner"
onMouseOver={() => this.setState({isVisible: true})}
onMouseLeave={() => this.setState({isVisible: false})}
>
<div
onClick={() => this.onClickOperationButton(player)}
className="player-mask">
{phase === PlayerPhase.Pause &&
<div className="player-big-icon">
<img
style={{width: 50, marginLeft: 6}}
src={video_play}
alt={"video_play"}/>
</div>}
</div>
<div className="player-box"
ref={this.handleBindRoom}/>
</div>
</div>
<div className="room-controller-box">
<div className="page-controller-mid-box">
<ExitButtonPlayer
identity={identity}
uuid={uuid}
userId={userId}
player={player}
/>
</div>
</div>
</div>
);
}
}
}
}
Example #25
Source File: my-projects.tsx From microsoft-teams-apps-growyourskills with MIT License | 4 votes |
class TitleBar extends React.Component<WithTranslation, IFilterBarState> {
localize: TFunction;
constructor(props: WithTranslation) {
super(props);
this.localize = this.props.t;
this.state = {
activeIndex: 0,
joinedCount: 0,
createdCount: 0
}
}
componentDidMount() {
this.getProjectCounts();
}
/**
* Get filtered projects based on selected checkboxes.
*/
getMyProjects = async () => {
let response = await getMyCreatedProjects(0);
if (response.status === 200 && response.data) {
this.setState({
createdCount: response.data.length,
});
}
}
getProjectCounts = () => {
this.getMyProjects();
this.getJoinedProjects();
}
/**
* Fetch projects for Team tab from API
*/
getJoinedProjects = async () => {
let response = await getMyJoinedProjects(0);
if (response.status === 200 && response.data) {
this.setState({
joinedCount: response.data.length
})
}
}
onMenuItemClick = (e: any, props: any) => {
this.setState({
activeIndex: props.activeIndex
})
}
/**
* Renders the component
*/
public render(): JSX.Element {
let joinedCount = "";
let createdCount = "";
if (this.state.joinedCount > 0) {
if (this.state.joinedCount === 50) {
joinedCount = ' (50+)';
}
else {
joinedCount = ' (' + this.state.joinedCount + ')';
}
}
else {
joinedCount = " (0)";
}
if (this.state.createdCount > 0) {
if (this.state.createdCount === 50) {
createdCount = ' (50+)';
}
else {
createdCount = ' (' + this.state.createdCount + ')';
}
}
else {
createdCount = " (0)";
}
const items = [
{
key: 'Created project',
content: this.localize("projectsCreated") + createdCount,
},
{
key: 'Joined projects',
content: this.localize("projectsJoined") + joinedCount,
}
]
return (
<>
<div className="container-div">
<div className="container-subdiv-myprojects">
<Menu
defaultActiveIndex={0}
primary
items={items}
onActiveIndexChange={(e: any, props: any) => this.onMenuItemClick(e, props)}
/>
{
this.state.activeIndex === 0
? <MyCreatedProjects showProjectCount={this.getProjectCounts} />
: <MyJoinedProjects showProjectCount={this.getProjectCounts} />
}
</div>
</div>
</>
)
}
}
Example #26
Source File: ReplayVideoPage.tsx From whiteboard-demo with MIT License | 4 votes |
class NetlessVideoPlayer extends React.Component<PlayerVideoPageProps & WithTranslation, PlayerVideoPageStates> {
private readonly videoRef: React.RefObject<HTMLVideoElement>;
public constructor(props: PlayerVideoPageProps & WithTranslation) {
super(props);
this.state = {
currentTime: 0,
phase: PlayerPhase.Pause,
isPlayerSeeking: false,
isVisible: false,
replayFail: false,
replayState: false,
};
this.videoRef = React.createRef();
}
private getRoomToken = async (uuid: string): Promise<string | null> => {
const roomToken = await netlessWhiteboardApi.room.joinRoomApi(uuid);
return roomToken || null;
};
public async componentDidMount(): Promise<void> {
window.addEventListener("keydown", this.handleSpaceKey);
const {uuid, identity, region} = this.props.match.params;
const plugins = createPlugins({
"video": videoPlugin, "audio": audioPlugin,
"video2": videoPlugin2, "audio2": audioPlugin2,
});
plugins.setPluginContext("video", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("audio", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("video2", {identity: identity === Identity.creator ? "host" : ""});
plugins.setPluginContext("audio2", {identity: identity === Identity.creator ? "host" : ""});
const roomToken = await this.getRoomToken(uuid);
if (uuid && roomToken) {
const whiteWebSdk = new WhiteWebSdk({
appIdentifier: netlessToken.appIdentifier,
plugins,
region,
});
await this.loadPlayer(whiteWebSdk, uuid, roomToken);
}
}
private loadPlayer = async (whiteWebSdk: WhiteWebSdk, uuid: string, roomToken: string): Promise<void> => {
await polly().waitAndRetry(10).executeForPromise(async () => {
const isPlayable = whiteWebSdk.isPlayable({ room: uuid, roomToken });
if (!isPlayable) {
throw Error("the current room cannot be replay");
}
return;
});
this.setState({replayState: true});
await this.startPlayer(whiteWebSdk, uuid, roomToken);
}
private startPlayer = async (whiteWebSdk: WhiteWebSdk, uuid: string, roomToken: string): Promise<void> => {
const cursorAdapter = new CursorTool();
const player = await whiteWebSdk.replayRoom(
{
room: uuid,
roomToken: roomToken,
cursorAdapter: cursorAdapter,
}, {
onPhaseChanged: phase => {
this.setState({phase: phase});
},
onStoppedWithError: (error: Error) => {
message.error(`Playback error: ${error}`);
this.setState({replayFail: true});
},
onProgressTimeChanged: (scheduleTime: number) => {
this.setState({currentTime: scheduleTime});
},
});
(window as any).player = player;
cursorAdapter.setPlayer(player);
this.setState({
player: player,
});
this.initCombinePlayer(player);
}
private handleBindRoom = (ref: HTMLDivElement): void => {
const {player} = this.state;
if (player) {
player.bindHtmlElement(ref);
}
}
private handleSpaceKey = (evt: any): void => {
if (evt.code === "Space") {
if (this.state.player && this.state.combinePlayer) {
this.onClickOperationButton(this.state.player, this.state.combinePlayer);
}
}
}
private onClickOperationButton = (player: Player, combinePlayer: CombinePlayer | undefined): void => {
if (!player || !combinePlayer) {
return;
}
switch (player.phase) {
case PlayerPhase.WaitingFirstFrame:
case PlayerPhase.Pause:
case PlayerPhase.Ended:{
console.log(1);
combinePlayer.play();
break;
}
case PlayerPhase.Playing: {
combinePlayer.pause();
break;
}
}
}
private renderScheduleView(): React.ReactNode {
const {player, isVisible, combinePlayer} = this.state;
if (player && isVisible && combinePlayer) {
return (
<div onMouseEnter={() => this.setState({isVisible: true})}>
<PlayerController player={player} combinePlayer={combinePlayer} i18nLanguage={this.props.i18n.language} />
</div>
);
} else {
return null;
}
}
private initCombinePlayer(player: Player): void {
const { t } = this.props
if (this.videoRef.current === null) {
return;
}
const combinePlayerFactory = new CombinePlayerFactory(player, {
url: "https://docs-assets.oss-cn-hangzhou.aliyuncs.com/m3u8-video/test.m3u8",
videoDOM: this.videoRef.current,
}, true);
const combinePlayer = combinePlayerFactory.create();
combinePlayer.setOnStatusChange((status, message) => {
console.log(t('changeStatus'), status, message);
});
this.setState({
combinePlayer,
});
(window as any).combinePlayer = combinePlayer;
}
private getReplayPage() {
const { t } = this.props
const {player, phase, replayState, combinePlayer} = this.state;
const { identity, uuid, userId } = this.props.match.params;
if (this.state.replayFail) {
return <PageError/>;
}
if (!replayState) {
return <LoadingPage text={t('waitingReplayGenerate')}/>;
}
if (player === undefined) {
return <LoadingPage/>;
}
switch (phase) {
case (PlayerPhase.WaitingFirstFrame): {
return <LoadingPage/>;
}
default: {
return (
<div className="player-out-box">
<div className="logo-box">
<img src={logo} alt={"logo"}/>
</div>
<div className="player-board">
{this.renderScheduleView()}
<div
className="player-board-inner"
onMouseOver={() => this.setState({isVisible: true})}
onMouseLeave={() => this.setState({isVisible: false})}
>
<div
onClick={() => this.onClickOperationButton(player, combinePlayer)}
className="player-mask">
{phase === PlayerPhase.Pause &&
<div className="player-big-icon">
<img
style={{width: 50, marginLeft: 6}}
src={video_play}
alt={"video_play"}/>
</div>}
</div>
<div style={{backgroundColor: "#F2F2F2"}}
className="player-box"
ref={this.handleBindRoom}/>
</div>
</div>
<div className="room-controller-box">
<div className="page-controller-mid-box">
<ExitButtonPlayer
identity={identity}
uuid={uuid}
userId={userId}
player={player}
/>
</div>
</div>
</div>
);
}
}
}
public render(): React.ReactNode {
return (
<div className="overall-box">
{this.getReplayPage()}
<video
className="video-box video-js"
ref={this.videoRef}
width="500"
/>
</div>
)
}
}
Example #27
Source File: join-project-dialog.tsx From microsoft-teams-apps-growyourskills with MIT License | 4 votes |
class JoinProjectDialogContent extends React.Component<WithTranslation
, IJoinProjectDialogContentState> {
localize: TFunction;
teamId = "";
projectId = "";
currentUserId = "";
createdByUserId = "";
upn = "";
constructor(props: any) {
super(props);
this.localize = this.props.t;
let search = window.location.search;
let params = new URLSearchParams(search);
this.projectId = params.get("projectId")!;
this.currentUserId = params.get("currentUserId")!;
this.createdByUserId = params.get("createdByUserId")!;
this.state = {
skillList: [],
documentUrlList: [],
projectDetails: {
projectId: "",
status: 0,
title: "",
description: "",
supportDocuments: "",
requiredSkills: "",
createdDate: new Date(),
createdByName: "",
createdByUserId: "",
updatedDate: new Date(),
teamSize: 0,
isRemoved: false,
projectStartDate: "",
projectEndDate: "",
isJoinedByUser: false,
isCurrentUserProject: false,
avatarBackgroundColor: "",
projectParticipantsUserMapping: "",
projectParticipantsUserIds: "",
},
isEditDialogOpen: false,
isLoading: true,
showLoader: false,
theme:""
}
}
/**
* Called once component is mounted.
*/
async componentDidMount() {
microsoftTeams.initialize();
microsoftTeams.getContext((context: microsoftTeams.Context) => {
this.upn = context.upn!;
this.setState({ theme: context.theme! });
});
let response = await getProjectDetailToJoin(this.projectId, this.createdByUserId);
if (response.status === 200 && response.data) {
this.setState({
projectDetails: response.data
});
}
this.setState({
skillList: this.state.projectDetails.requiredSkills.split(";"),
documentUrlList: this.state.projectDetails.supportDocuments.split(";")
})
this.setState({
isLoading: false
});
}
/**
*Close the dialog and pass back card properties to parent component.
*/
onSubmitClick = async () => {
this.setState({
showLoader: true
});
let projectDetails = this.state.projectDetails;
let toBot =
{
projectDetails,
command: Resources.submitJoinProjectTaskModule,
upn: this.upn
};
microsoftTeams.tasks.submitTask(toBot);
}
onSkillRemoveClick = () => {
console.log('a');
}
onLinkRemoveClick = () => {
console.log('a');
}
/**
* Renders the component
*/
public render(): JSX.Element {
let membersJoined = 0;
if (this.state.projectDetails.projectParticipantsUserIds !== "") {
membersJoined = this.state.projectDetails.projectParticipantsUserIds.split(';').length
}
let startDate = moment.utc(this.state.projectDetails.projectStartDate).local().format("MM-DD-YYYY hh:mm A");
let endDate = moment.utc(this.state.projectDetails.projectEndDate).local().format("MM-DD-YYYY hh:mm A");
if (this.state.isLoading === false) {
return (
<Provider className="join-project-dialog-provider-wrapper-taskview">
<Flex styles={{height:"45rem"}}>
<div className="join-project-dialog-body-taskview">
<Flex gap="gap.smaller" className="input-label-space-between-taskview" styles={{ fontSize: "12px" }}>
<Flex.Item>
<Text styles={{ fontSize:"18px" }} className="project-title-taskview" content={this.state.projectDetails.title} />
</Flex.Item>
</Flex>
<div style={{fontSize:"12px"}}>
<Flex gap="gap.smaller" className="label-spacing-taskview joined-project-text-area-taskview input-label-space-between-taskview">
<Flex.Item>
<Text className="joined-project-text-area-taskview" content={this.state.projectDetails.description} />
</Flex.Item>
</Flex>
<Flex gap="gap.small">
<div className="joined-project-half-field-taskview label-spacing-taskview">
<Flex gap="gap.smaller" className="input-label-space-between-taskview edit-team-size-space">
<Flex.Item>
<Text content={this.localize("projectDurationLabel") + " :"} />
</Flex.Item>
</Flex>
</div>
<div className="joined-project-half-field-taskview label-spacing-taskview bold-value content-width">
<Flex gap="gap.smaller" className="input-label-space-between-taskview edit-team-size-space">
<Flex.Item>
<Text weight="semibold" content={startDate + " - " + endDate} />
</Flex.Item>
</Flex>
</div>
</Flex>
<Flex gap="gap.small">
<div className="joined-project-half-field-taskview label-spacing-taskview">
<Flex gap="gap.smaller" className="input-label-space-between-taskview">
<Flex.Item>
<Text content={this.localize("teamSize") + " :"} />
</Flex.Item>
</Flex>
</div>
<div className="joined-project-half-field-taskview label-spacing-taskview left-spacing-teamsize-taskview bold-value">
<Flex gap="gap.smaller" className="input-label-space-between-taskview">
<Flex.Item>
<Text weight="semibold" content={this.state.projectDetails.teamSize} />
</Flex.Item>
</Flex>
</div>
</Flex>
<Flex gap="gap.small">
<div className="joined-project-half-field-taskview label-spacing-taskview ">
<Flex gap="gap.smaller" className="input-label-space-between-taskview">
<Flex.Item>
<Text content={this.localize("membersJoinedLabel") + " :"} />
</Flex.Item>
</Flex>
</div>
<div className="joined-project-half-field-taskview label-spacing-taskview left-spacing-joined-taskview bold-value">
<Flex gap="gap.smaller" className="input-label-space-between-taskview">
<Flex.Item>
<Text weight="semibold" content={membersJoined} />
</Flex.Item>
</Flex>
</div>
</Flex>
<Flex gap="gap.smaller" vAlign="center" className="label-spacing-taskview input-label-space-between-taskview">
<Text content={this.localize("skillsNeededLabel") + " :"} />
</Flex>
<Flex gap="gap.smaller" className="skills-flex skills-new-project" vAlign="center">
<div>
{
this.state.skillList.map((value: string, index) => {
if (value.trim().length > 0) {
return <Label
styles={{ padding: "1rem" }}
circular
content={<Text className="tag-text-form" content={value.trim()} title={value.trim()} size="small" />}
className={this.state.theme === Resources.dark ? "tags-label-wrapper-dark" : "tags-label-wrapper"}
/>
}
})
}
</div>
</Flex>
<Flex gap="gap.smaller" className="label-spacing-taskview input-fields-margin-between-add-post-taskview">
<Text content={this.localize("docLinkFormLabel") + " :"} />
</Flex>
<Flex gap="gap.smaller" className="document-url-flex" vAlign="center">
<div>
{
this.state.documentUrlList.map((value: string, index) => {
if (value.trim().length > 0) {
return <DocumentUrl showDeleteIcon={false} index={index} urlContent={value.trim()} onRemoveClick={() => { }} />
}
else {
return <Text className="no-url-added" content={this.localize("noLinksAdded")} />
}
})
}
</div>
</Flex>
</div>
</div>
</Flex>
{
(this.state.projectDetails.status === 1 || this.state.projectDetails.status === 2) &&
!this.state.projectDetails.projectParticipantsUserIds.split(';').includes(this.currentUserId) &&
this.state.projectDetails.createdByUserId !== this.currentUserId &&
this.state.projectDetails.projectParticipantsUserIds.split(';').filter((userId) => userId).length < this.state.projectDetails.teamSize
? <Flex className="join-project-dialog-footer-wrapper-taskview">
<Flex gap="gap.smaller" className="join-project-dialog-footer-taskview input-fields-margin-between-add-post-taskview">
<Flex.Item push>
<Button content={this.localize("joinButtonText")} primary loading={this.state.showLoader} disabled={this.state.showLoader} onClick={this.onSubmitClick} />
</Flex.Item>
</Flex>
</Flex>
:
<></>
}
</Provider>
);
}
else {
return (<></>)
}
}
}
Example #28
Source File: Storage.tsx From whiteboard-demo with MIT License | 4 votes |
class Storage extends React.Component<WithTranslation, StorageState> {
private readonly spaceRefresher: AsyncRefresher = new AsyncRefresher(100, async () => {
const space = await netlessCaches.calculateCache();
const availableSpace = await netlessCaches.availableSpace();
this.setState({
space: Math.round(space),
availableSpace: Math.round(availableSpace),
});
});
public constructor(props: WithTranslation) {
super(props);
this.state = {
mode: DownloadingMode.Freedom,
isAdding: false,
nextAddIndex: 0,
pptStates: [],
};
}
public async componentDidMount(): Promise<void> {
const { t } = this.props;
try {
const tasks: PPTTask[] = taskUuids.map(task => ({
uuid: task.taskUuid,
name: task.name || "",
}));
const downloader = await DownloadLogic.create(tasks, {
onUpdateState: state => this.setState(state as any),
onSpaceUpdate: () => this.spaceRefresher.invoke(),
onCatchDownloadingError: this.onCatchDownloadingError,
});
this.setState({...downloader.state, downloader});
this.spaceRefresher.invoke();
} catch (error) {
console.error(error);
}
}
public componentWillUnmount(): void {
this.spaceRefresher.cancel();
}
private onCatchDownloadingError = (error: Error, task: PPTTask): void => {
console.error(`download task ${task.uuid} failed:`, error);
}
public render(): React.ReactNode {
const downloader = this.state.downloader;
if (!downloader) {
return null;
}
return (
<div className="page-index-box">
<FloatLink />
<div className="page-index-mid-box">
{this.renderHeadView(downloader)}
{this.state.pptStates.length === 0 ? (
<div className="page-history-body-empty">
<img src={empty_box} alt={"empty_box"} />
</div>
) : (
<div className="page-history-body">
{this.state.pptStates.map(
pptState => this.renderZipCell(downloader, pptState),
)}
</div>
)}
</div>
</div>
);
}
private renderHeadView(downloader: DownloadLogic): React.ReactNode {
const { t } = this.props
const shouldDisplaySpaceTag = (
typeof this.state.space === "number" &&
typeof this.state.availableSpace === "number"
);
return (
<div className="page-history-head">
<div className="page-history-head-left">
<Link to={"/"}>
<div className="page-history-back">
<LeftOutlined /> <div>{t('back')}</div>
</div>
</Link>
{shouldDisplaySpaceTag && <Tag
color={"blue"}
style={{marginLeft: 8}}>{this.state.space}(mb) / {this.state.availableSpace}(mb)
</Tag>}
</div>
<div>
{this.renderAddTaskButton(downloader)}
{this.renderDownloadOneByOneButton(downloader)}
{this.renderCleanAllCacheButton(downloader)}
</div>
</div>
);
}
private async onClickAddTask(downloader: DownloadLogic): Promise<void> {
const {taskUuid, name} = taskUuuidsToPush[this.state.nextAddIndex];
const task: PPTTask = {
uuid: taskUuid,
name: name || "",
};
this.setState({
isAdding: true,
});
try {
await downloader.addTask(task);
} finally {
this.setState({
isAdding: false,
nextAddIndex: this.state.nextAddIndex + 1,
});
}
}
private renderAddTaskButton(downloader: DownloadLogic): React.ReactNode {
const { t } = this.props
let disableAdd = false;
if (this.state.nextAddIndex >= taskUuuidsToPush.length) {
disableAdd = true;
} else if (this.state.isAdding) {
disableAdd = true;
}
return (
<Button
type="link"
disabled={disableAdd}
size={"small"}
style={{ marginRight: 20, fontSize: 14 }}
onClick={() => this.onClickAddTask(downloader)}>
{t('add')}
</Button>
);
}
private renderDownloadOneByOneButton(downloader: DownloadLogic): React.ReactNode {
const { t } = this.props
let node: React.ReactNode = null;
switch (this.state.mode) {
case DownloadingMode.OneByOne: {
node = (
<Button
type="link"
size={"small"}
style={{ marginRight: 20, fontSize: 14 }}
onClick={() => downloader.abort()}>
{t('stopDownload')}
</Button>
);
break;
}
case DownloadingMode.Freedom: {
const disabled = (
this.state.pptStates.some(ppt => ppt.phase === TaskPhase.Downloading) ||
!this.state.pptStates.some(ppt => ppt.phase === TaskPhase.NotCached)
);
node = (
<Button
type="link"
size={"small"}
style={{ marginRight: 20, fontSize: 14 }}
disabled={disabled}
onClick={() => downloader.startOneByOne()}>
{t('downloadAll')}
</Button>
);
break;
}
}
return node;
}
private renderCleanAllCacheButton(downloader: DownloadLogic): React.ReactNode {
const { t } = this.props
const enable = (
this.state.mode === DownloadingMode.Freedom &&
this.state.pptStates.some(ppt => ppt.phase !== TaskPhase.NotCached)
);
return (
<Button
type="link"
size={"small"}
disabled={!enable}
style={{ marginRight: 20, fontSize: 14 }}
onClick={() => downloader.removeAll()}>
{t('clearCache')}
</Button>
);
}
private renderZipCell(downloader: DownloadLogic, pptState: PPTState): React.ReactNode {
const { t } = this.props
const displayProgress = (
pptState.phase === TaskPhase.Downloading
);
const enableRemoveCache = (
this.state.mode === DownloadingMode.Freedom &&
pptState.phase === TaskPhase.Cached
);
return (
<div key={pptState.uuid}>
<div className="room-cell-box">
<div className="room-cell-left">
<div className="room-cell-image">
<img src={zip_icon} alt={"cover"} />
{displayProgress &&
<div className="room-cell-image-cover">
<Progress
width={42}
style={{color: "white"}}
strokeWidth={6}
type="circle"
trailColor={"white"}
percent={pptState.progress} />
</div>
}
</div>
<div>
<div className="room-cell-text">{pptState.name}</div>
</div>
</div>
<div className="room-download-cell-right">
{this.renderDownloadButton(downloader, pptState)}
<Button
onClick={() => downloader.removeTask(pptState.uuid)}
disabled={!enableRemoveCache}
style={{width: 96}}>
{t('delete')}
</Button>
</div>
</div>
<div className="room-cell-cut-line" />
</div>
);
}
private renderDownloadButton(downloader: DownloadLogic, pptState: PPTState): React.ReactNode {
const { t } = this.props
const enable = (
this.state.mode === DownloadingMode.Freedom &&
pptState.phase !== TaskPhase.Cached
);
switch (pptState.phase) {
case TaskPhase.NotCached: {
return (
<Button
onClick={() => downloader.startTask(pptState.uuid)}
type={"primary"}
disabled={!enable}
style={{width: 96}}>
{t('download')}
</Button>
);
}
case TaskPhase.Downloading: {
return (
<Button
onClick={() => downloader.abortTask(pptState.uuid)}
type={"primary"}
disabled={!enable}
style={{width: 96}}>
{t('stop')}
</Button>
);
}
case TaskPhase.Cached: {
return (
<Button
disabled
type={"primary"}
style={{width: 96}}>
{t('downloaded')}
</Button>
);
}
case TaskPhase.Failed: {
return (
<Button
onClick={() => downloader.startTask(pptState.uuid)}
type={"primary"}
disabled={!enable}
style={{width: 96}}>
{t('downloadAgain')}
</Button>
);
}
}
}
}
Example #29
Source File: discover-wrapper-page.tsx From microsoft-teams-apps-growyourskills with MIT License | 4 votes |
class DiscoverWrapperPage extends React.Component<WithTranslation, ICardViewState> {
localize: TFunction;
selectedSharedBy: Array<ICheckBoxItem>;
selectedPostprojectStatus: Array<ICheckBoxItem>;
selectedskills: Array<ICheckBoxItem>;
selectedSortBy: string;
filterSearchText: string;
allProjects: Array<IProjectDetails>;
loggedInUserObjectId: string;
loggedInUserName: string;
teamId: string;
authorAvatarBackground: Array<any>;
hasMoreProjects: boolean;
constructor(props: any) {
super(props);
this.localize = this.props.t;
let colors = localStorage.getItem("avatar-colors");
this.selectedSharedBy = [];
this.selectedPostprojectStatus = [];
this.selectedskills = [];
this.selectedSortBy = "";
this.filterSearchText = "";
this.allProjects = [];
this.loggedInUserObjectId = "";
this.loggedInUserName = "";
this.teamId = "";
this.authorAvatarBackground = colors === null ? [] : JSON.parse(colors!);
this.hasMoreProjects = true;
this.state = {
loader: true,
projectDetails: [],
projectSearchDetails: [],
resourceStrings: {},
alertMessage: "",
alertprojectStatus: 0,
showAlert: false,
searchText: "",
showNoProjectPage: false,
isFilterApplied: false,
infiniteScrollParentKey: 0,
isPageInitialLoad: true,
pageLoadStart: -1,
hasMoreProjects: true,
initialProjects: []
}
}
/**
* Used to initialize Microsoft Teams sdk
*/
async componentDidMount() {
this.initprojectDetails();
microsoftTeams.initialize();
microsoftTeams.getContext((context: microsoftTeams.Context) => {
this.loggedInUserObjectId = context.userObjectId!;
this.loggedInUserName = context.userPrincipalName!;
});
}
/**
* Fetch projects for initializing grid
*/
initprojectDetails = async () => {
let response = await getAllProjects(0);
if (response.status === 200 && response.data) {
this.setState({
initialProjects: response.data,
loader: false
});
}
}
/**
* Get comma separated selected filter entities string.
* @param filterEntity Array of selected filter entities.
*/
private getFilterString(filterEntity: Array<string>) {
return filterEntity.length > 1 ? filterEntity.join(";") : filterEntity.length === 1 ? filterEntity.join(";") + ";" : "";
}
/**
* Get filtered projects based on selected checkboxes.
* @param pageCount Page count for which next set of projects needs to be fetched
*/
getFilteredprojectDetails = async (pageCount: number) => {
let postprojectStatuss = this.selectedPostprojectStatus.map((postprojectStatus: ICheckBoxItem) => { return postprojectStatus.key.toString().trim() });
let postprojectStatussString = encodeURI(this.getFilterString(postprojectStatuss));
let authors = this.selectedSharedBy.map((authors: ICheckBoxItem) => { return authors.title.trim() });
let authorsString = encodeURI(this.getFilterString(authors));
let skills = this.selectedskills.map((skill: ICheckBoxItem) => { return skill.title.trim() });
let skillsString = encodeURI(this.getFilterString(skills));
let response = await getFilteredProjects(postprojectStatussString, authorsString, skillsString, pageCount);
if (response.status === 200 && response.data) {
if (response.data.length < 50) {
this.hasMoreProjects = false;
}
else {
this.hasMoreProjects = true;
}
response.data.map((post: IProjectDetails) => {
let searchedAuthor = this.authorAvatarBackground.find((author) => author.id === post.createdByUserId);
if (searchedAuthor) {
post.avatarBackgroundColor = searchedAuthor.color;
}
else {
let color = generateColor();
this.authorAvatarBackground.push({ id: post.createdByUserId, color: color });
post.avatarBackgroundColor = color;
localStorage.setItem("avatar-colors", JSON.stringify(this.authorAvatarBackground));
}
if (post.createdByUserId === this.loggedInUserObjectId) {
post.isCurrentUserProject = true;
}
else {
post.isCurrentUserProject = false;
}
this.allProjects.push(post);
});
if (response.data.count !== 0) {
this.setState({
isPageInitialLoad: false,
});
}
else {
this.setState({
showNoProjectPage: true,
isPageInitialLoad: false
})
}
//this.getUserVotes();
this.onFilterSearchTextChange(this.filterSearchText);
}
}
/**
* Reset app user selected filters
*/
resetAllFilters = () => {
this.selectedSortBy = Resources.sortBy[0].id;
this.selectedSharedBy = [];
this.selectedPostprojectStatus = [];
this.selectedskills = [];
this.filterSearchText = "";
}
/**
* Fetch projects for Team tab from API.
* @param pageCount Page count for which next set of projects needs to be fetched.
*/
getprojectDetails = async (pageCount: number) => {
this.resetAllFilters();
let response = await getAllProjects(pageCount);
if (response.status === 200 && response.data) {
if (response.data.length < 50) {
this.hasMoreProjects = false;
}
else {
this.hasMoreProjects = true;
}
response.data.map((post: IProjectDetails) => {
let searchedAuthor = this.authorAvatarBackground.find((author) => author.id === post.createdByUserId);
if (searchedAuthor) {
post.avatarBackgroundColor = searchedAuthor.color;
}
else {
let color = generateColor();
this.authorAvatarBackground.push({ id: post.createdByUserId, color: color });
post.avatarBackgroundColor = color;
localStorage.setItem("avatar-colors", JSON.stringify(this.authorAvatarBackground));
}
if (post.createdByUserId === this.loggedInUserObjectId) {
post.isCurrentUserProject = true;
}
else {
post.isCurrentUserProject = false;
}
this.allProjects.push(post);
});
if (response.data.count === 0) {
this.setState({
showNoProjectPage: true
})
}
//this.getUserVotes();
this.onFilterSearchTextChange(this.filterSearchText);
}
this.setState({
searchText: "",
isPageInitialLoad: false
});
}
/**
*Sets state for showing alert notification.
*@param content Notification message
*@param projectStatus Boolean value indicating 1- Success 2- Error
*/
showAlert = (content: string, projectStatus: number) => {
this.setState({ alertMessage: content, alertprojectStatus: projectStatus, showAlert: true }, () => {
setTimeout(() => {
this.setState({ showAlert: false })
}, 4000);
});
}
/**
*Sets state for hiding alert notification.
*/
hideAlert = () => {
this.setState({ showAlert: false })
}
/**
*Removes selected blog post from page
*@param projectId Id of post which needs to be deleted
*@param isSuccess Boolean indication whether operation succeeded
*/
handleDeleteButtonClick = (projectId: string, isSuccess: boolean) => {
if (isSuccess) {
this.allProjects.map((post: IProjectDetails) => {
if (post.projectId === projectId) {
post.isRemoved = true;
}
});
this.showAlert(this.localize("projectDeletedSuccess"), 1);
this.onFilterSearchTextChange(this.filterSearchText);
}
else {
this.showAlert(this.localize("postDeletedError"), 2);
}
}
/**
*Removes selected project from joined projects
*@param projectId Id of project which needs to be deleted
*@param isSuccess Boolean indication whether operation succeeded
*/
handleLeaveButtonClick = (projectId: string, isSuccess: boolean) => {
if (isSuccess) {
this.allProjects.map((post: IProjectDetails) => {
if (post.projectId === projectId) {
post.isRemoved = true;
}
});
this.showAlert(this.localize("leaveProjectSuccess"), 1);
this.onFilterSearchTextChange(this.filterSearchText);
}
else {
this.showAlert(this.localize("leaveProjectError"), 2);
}
}
/**
*Invoked by Infinite scroll component when user scrolls down to fetch next set of projects.
*@param pageCount Page count for which next set of projects needs to be fetched.
*/
loadMoreProjects = (pageCount: number) => {
if (!this.filterSearchText.trim().length) {
if (this.state.searchText.trim().length) {
this.searchFilterPostUsingAPI(pageCount);
}
else if (this.state.isFilterApplied) {
this.getFilteredprojectDetails(pageCount);
}
else {
this.getprojectDetails(pageCount);
}
}
}
/**
*Set state of search text as per user input change
*@param searchText Search text entered by user
*/
handleSearchInputChange = async (searchText: string) => {
this.setState({
searchText: searchText
});
if (searchText.length === 0) {
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
hasMoreProjects: true
});
this.allProjects = [];
}
}
/**
*Filter cards based on user input after clicking search icon in search bar.
*/
searchFilterPostUsingAPI = async (pageCount: number) => {
this.resetAllFilters();
if (this.state.searchText.trim().length) {
let response = await filterTitleAndSkills(this.state.searchText, pageCount);
if (response.status === 200 && response.data) {
if (response.data.length < 50) {
this.hasMoreProjects = false;
}
else {
this.hasMoreProjects = true;
}
response.data.map((post: IProjectDetails) => {
let searchedAuthor = this.authorAvatarBackground.find((author) => author.id === post.createdByUserId);
if (searchedAuthor) {
post.avatarBackgroundColor = searchedAuthor.color;
}
else {
let color = generateColor();
this.authorAvatarBackground.push({ id: post.createdByUserId, color: color });
post.avatarBackgroundColor = color;
localStorage.setItem("avatar-colors", JSON.stringify(this.authorAvatarBackground));
}
if (post.createdByUserId === this.loggedInUserObjectId) {
post.isCurrentUserProject = true;
}
else {
post.isCurrentUserProject = false;
}
this.allProjects.push(post)
});
this.setState({ isPageInitialLoad: false });
//this.getUserVotes();
this.onFilterSearchTextChange(this.filterSearchText);
}
}
}
/**
*Filter cards based on 'shared by' checkbox selection.
*@param selectedCheckboxes User selected checkbox array
*/
onSharedByCheckboxStateChange = (selectedCheckboxes: Array<ICheckBoxItem>) => {
this.selectedSharedBy = selectedCheckboxes.filter((value) => { return value.isChecked });
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
searchText: "",
hasMoreProjects: true
});
this.allProjects = [];
}
/**
*Filter cards based on post projectStatus checkbox selection.
*@param selectedCheckboxes User selected checkbox array
*/
onprojectStatusCheckboxStateChange = (selectedCheckboxes: Array<ICheckBoxItem>) => {
this.selectedPostprojectStatus = selectedCheckboxes.filter((value) => { return value.isChecked });
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
searchText: "",
hasMoreProjects: true
});
this.allProjects = [];
}
/**
*Filter cards based on skills checkbox selection.
*@param selectedCheckboxes User selected checkbox array
*/
onskillsStateChange = (selectedCheckboxes: Array<ICheckBoxItem>) => {
this.selectedskills = selectedCheckboxes.filter((value) => { return value.isChecked });
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
searchText: "",
hasMoreProjects: true
});
this.allProjects = [];
}
/**
*Filter cards based sort by value.
*@param selectedValue Selected value for 'sort by'
*/
onSortByChange = (selectedValue: string) => {
this.selectedSortBy = selectedValue;
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
searchText: "",
hasMoreProjects: true
});
this.allProjects = [];
}
/**
* Invoked when post is edited. Updates state and shows notification alert.
* @param cardDetails Updated post details
* @param isSuccess Boolean indicating whether edit operation is successful.
*/
onCardUpdate = (cardDetails: IProjectDetails, isSuccess: boolean) => {
if (isSuccess) {
this.allProjects.map((post: IProjectDetails) => {
if (post.projectId === cardDetails.projectId) {
post.description = cardDetails.description;
post.title = cardDetails.title;
post.requiredSkills = cardDetails.requiredSkills;
post.status = cardDetails.status;
post.projectParticipantsUserIds = cardDetails.projectParticipantsUserIds;
post.projectParticipantsUserMapping = cardDetails.projectParticipantsUserMapping;
post.projectEndDate = cardDetails.projectEndDate;
post.projectStartDate = cardDetails.projectStartDate;
post.teamSize = cardDetails.teamSize;
post.supportDocuments = cardDetails.supportDocuments;
}
});
this.onFilterSearchTextChange(this.filterSearchText);
this.showAlert(this.localize("postUpdateSuccess"), 1)
}
else {
this.showAlert(this.localize("postUpdateError"), 2)
}
}
/**
* Invoked when new post is added. Shows notification alert.
* @param isSuccess Boolean indicating whether add new post operation is successful.
* @param getSubmittedPost Post details which needs to be added.
*/
onNewPost = (isSuccess: boolean, getSubmittedPost: IProjectDetails) => {
if (isSuccess) {
let searchedAuthor = this.authorAvatarBackground.find((author) => author.id === getSubmittedPost.createdByUserId);
if (searchedAuthor) {
getSubmittedPost.avatarBackgroundColor = searchedAuthor.color;
}
else {
let color = generateColor();
this.authorAvatarBackground.push({ id: getSubmittedPost.createdByUserId, color: color });
getSubmittedPost.avatarBackgroundColor = color;
localStorage.setItem("avatar-colors", JSON.stringify(this.authorAvatarBackground));
}
let submittedPost = this.state.projectDetails;
if (getSubmittedPost.createdByUserId === this.loggedInUserObjectId) {
getSubmittedPost.isCurrentUserProject = true;
}
else {
getSubmittedPost.isCurrentUserProject = false;
}
submittedPost.unshift(getSubmittedPost);
this.setState({ projectDetails: submittedPost, initialProjects: submittedPost });
this.allProjects = this.state.projectDetails;
this.showAlert(this.localize("addNewPostSuccess"), 1)
}
else {
this.showAlert(this.localize("addNewPostError"), 2)
}
}
/**
* Filters projects inline by user search text
* @param searchText Search text entered by user.
*/
onFilterSearchTextChange = (searchText: string) => {
this.filterSearchText = searchText;
if (searchText.trim().length) {
let filteredPosts = this.allProjects.filter((post: IProjectDetails) => post.title.toLowerCase().includes(searchText.toLowerCase()) === true);
this.setState({
projectDetails: filteredPosts, loader: false, hasMoreProjects: this.hasMoreProjects, isPageInitialLoad: false
});
}
else {
this.setState({
projectDetails: [...this.allProjects], loader: false, hasMoreProjects: this.hasMoreProjects, isPageInitialLoad: false
});
}
}
/**
* Invoked when either filter bar is displayed or closed
* @param isOpen Boolean indicating whether filter bar is displayed or closed.
*/
handleFilterClear = (isOpen: boolean) => {
if (!isOpen && (this.selectedPostprojectStatus.length > 0 || this.selectedSharedBy.length > 0 || this.selectedskills.length > 0 || this.selectedSortBy !== Resources.sortBy[0].id)) {
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
searchText: "",
hasMoreProjects: true
});
this.allProjects = [];
}
this.setState({
isFilterApplied: isOpen
});
this.resetAllFilters();
}
/**
* Invoked when user hits enter or clicks on search icon for searching post through command bar
*/
invokeApiSearch = () => {
this.setState({
isPageInitialLoad: true,
pageLoadStart: -1,
infiniteScrollParentKey: this.state.infiniteScrollParentKey + 1,
projectDetails: [],
isFilterApplied: false,
hasMoreProjects: true
});
this.allProjects = [];
}
hideFilterbar = () => {
return true;
}
handleCloseProjectButtonClick = (isSuccess: boolean, projectId: string) => {
if (isSuccess) {
this.allProjects.map((post: IProjectDetails) => {
if (post.projectId === projectId) {
post.status = 4;
}
});
this.showAlert(this.localize("projectCloseSuccess"), 1);
this.onFilterSearchTextChange(this.filterSearchText);
}
else {
this.showAlert(this.localize("projectCloseFailure"), 2);
}
}
onProjectJoin = (projectId: string, isSuccess: boolean) => {
if (isSuccess) {
this.allProjects.map((post: IProjectDetails) => {
if (post.projectId === projectId) {
if (post.projectParticipantsUserIds === "") {
post.projectParticipantsUserIds = post.projectParticipantsUserIds + this.loggedInUserObjectId;
post.projectParticipantsUserMapping = post.projectParticipantsUserMapping + this.loggedInUserObjectId + ":" + this.loggedInUserName;
}
else {
post.projectParticipantsUserIds = post.projectParticipantsUserIds + ";" + this.loggedInUserObjectId;
post.projectParticipantsUserMapping = post.projectParticipantsUserMapping + ";" + this.loggedInUserObjectId + ":" + this.loggedInUserName;
}
}
});
this.setState({
projectDetails: this.allProjects
})
this.onFilterSearchTextChange(this.filterSearchText);
this.showAlert(this.localize("projectJoinedSuccess"), 1)
}
else {
this.showAlert(this.localize("projectJoinedFailure"), 2)
}
}
/**
* Renders the component
*/
public render(): JSX.Element {
return (
<div>
{this.getWrapperPage()}
</div>
);
}
/**
*Get wrapper for page which acts as container for all child components
*/
private getWrapperPage = () => {
if (this.state.loader) {
return (
<div className="container-div">
<div className="container-subdiv">
<div className="loader">
<Loader />
</div>
</div>
</div>
);
} else {
// Cards component array to be rendered in grid.
const cards = new Array<any>();
this.state.projectDetails!.map((value: IProjectDetails, index) => {
if (!value.isRemoved) {
cards.push(<Col lg={3} sm={6} md={4} className="grid-column d-flex justify-content-center">
<Card loggedInUserId={this.loggedInUserObjectId} projectDetails={this.state.projectDetails} onJoinMenuItemClick={this.onProjectJoin} onCloseProjectButtonClick={this.handleCloseProjectButtonClick} onLeaveButtonClick={this.handleLeaveButtonClick} showLeaveProjects={false} showJoinProjectMenu={true} index={index} cardDetails={value} onCardUpdate={this.onCardUpdate} onDeleteButtonClick={this.handleDeleteButtonClick} />
</Col>)
}
});
if (this.state.initialProjects.length === 0) {
return (
<div className="container-div">
<div className="container-subdiv">
<NotificationMessage onClose={this.hideAlert} showAlert={this.state.showAlert} content={this.state.alertMessage} notificationType={this.state.alertprojectStatus} />
<NoPostAddedPage showAddPost={true} onNewPostSubmit={this.onNewPost} />
</div>
</div>
)
}
let scrollViewStyle = { height: this.state.isFilterApplied === true ? "84vh" : "92vh" };
return (
<div className="container-div">
<div className="container-subdiv-cardview">
<Container fluid className="container-fluid-overriden">
<NotificationMessage
onClose={this.hideAlert}
showAlert={this.state.showAlert}
content={this.state.alertMessage}
notificationType={this.state.alertprojectStatus}
/>
<TitleBar
projectDetails={this.state.projectDetails}
showFilter={true}
teamId={this.teamId}
commandBarSearchText={this.state.searchText}
searchFilterProjectsUsingAPI={this.invokeApiSearch}
onFilterClear={this.handleFilterClear}
hideFilterbar={!this.state.isFilterApplied}
onSortByChange={this.onSortByChange}
onFilterSearchChange={this.onFilterSearchTextChange}
onSearchInputChange={this.handleSearchInputChange}
onNewPostSubmit={this.onNewPost}
onSharedByCheckboxStateChange={this.onSharedByCheckboxStateChange}
onTypeCheckboxStateChange={this.onprojectStatusCheckboxStateChange}
onSkillsStateChange={this.onskillsStateChange}
/>
<div key={this.state.infiniteScrollParentKey} className="scroll-view scroll-view-mobile" style={scrollViewStyle}>
<InfiniteScroll
pageStart={this.state.pageLoadStart}
loadMore={this.loadMoreProjects}
hasMore={this.state.hasMoreProjects && !this.filterSearchText.trim().length}
initialLoad={this.state.isPageInitialLoad}
useWindow={false}
loader={<div className="loader"><Loader /></div>}>
<Row>
{
cards.length ? cards : this.state.hasMoreProjects === true ? <></> : <FilterNoPostContentPage />
}
</Row>
</InfiniteScroll>
</div>
</Container>
</div>
</div>
);
}
}
}