();
+ let _reactMentionStyles = reactMentionStyles;
+
+ const _onChange = useCallback((event, newValue: string, newPlainTextValue: string, mentions: MentionItem[]) => {
+ let _reactMentionStyles = reactMentionStyles;
+ if (newValue) {
+ setSingleLine(false);
+ _reactMentionStyles["&multiLine"].control = { height: 63 };
+ _addCommentText.current.text = newPlainTextValue;
+ _addCommentText.current.mentions = [];
+ for (let index = 0; index < mentions.length; index++) {
+ const mention = mentions[index];
+ _addCommentText.current.text = _addCommentText.current.text.replace(mention.display, `@mention{${index}}`);
+ _addCommentText.current.mentions.push({ email: mention.id, name: mention.display.replace("@", "") });
+ }
+ } else {
+ setSingleLine(true);
+ _reactMentionStyles["&multiLine"].control = { height: 35 };
+ _addCommentText.current = { mentions: [], text: "" };
+ }
+ setCommentText(newValue);
+ }, []);
+
+ const _addComment = useCallback(() => {
+ setlistItemCommentsState({ type: EListItemCommentsStateTypes.SET_COMMENT_ACTION, payload: ECommentAction.ADD });
+ setlistItemCommentsState({ type: EListItemCommentsStateTypes.SET_ADD_COMMENT, payload: _addCommentText.current });
+ setSingleLine(true);
+ setCommentText("");
+ }, []);
+
+ const _searchData = (search: string, callback: (users: SuggestionDataItem[]) => void) => {
+ // Try to get sugested users when user type '@'
+ if (!search) {
+ getSuggestions()
+ .then((res) => res.users.map((user) => ({ display: user.displayName, id: user.mail })))
+ .then(callback);
+ } else {
+ getUsers(search)
+ .then((res) => res.users.map((user) => ({ display: user.displayName, id: user.mail })))
+ .then(callback);
+ }
+ };
+
+ const renderSugestion = useCallback((suggestion: SuggestionDataItem): React.ReactNode => {
+ const _user: IUserInfo = {
+ id: suggestion.id as string,
+ displayName: suggestion.display,
+ mail: suggestion.id as string,
+ };
+ return (
+ <>
+
+
+
+
+
+ {_user.displayName}
+
+
+ {_user.mail}
+
+
+
+
+ >
+ );
+ }, []);
+
+ return (
+ <>
+ {/** Render Sugestions in the host element */}
+ {
+ sugestionsContainer.current = el;
+ }}
+ >
+
+
+ `@${display}`}
+ className={mentionsClasses.mention}
+ />
+
+
+ {
+ _addComment();
+ }}
+ />
+
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/AddComment/index.ts b/src/controls/listItemComments/components/AddComment/index.ts
new file mode 100644
index 000000000..231ba2cf9
--- /dev/null
+++ b/src/controls/listItemComments/components/AddComment/index.ts
@@ -0,0 +1,2 @@
+export * from './AddComment';
+export * from './useAddCommentStyles';
diff --git a/src/controls/listItemComments/components/AddComment/useAddCommentStyles.ts b/src/controls/listItemComments/components/AddComment/useAddCommentStyles.ts
new file mode 100644
index 000000000..ab02ed852
--- /dev/null
+++ b/src/controls/listItemComments/components/AddComment/useAddCommentStyles.ts
@@ -0,0 +1,150 @@
+import * as React from "react";
+import { IDocumentCardStyles, IStackStyles, IStyle, mergeStyleSets } from "@fluentui/react";
+import { AppContext } from "../../common";
+
+export const useAddCommentStyles = () => {
+ const { theme } = React.useContext(AppContext);
+ const itemContainerStyles: IStackStyles = {
+ root: { paddingTop: 0, paddingLeft: 20, paddingRight: 20, paddingBottom: 20 } as IStyle,
+ };
+
+ const deleteButtonContainerStyles: Partial = {
+ root: {
+ position: "absolute",
+ top: 0,
+ right: 0,
+ },
+ };
+
+ const searchMentionContainerStyles: Partial = {
+ root: {
+ borderWidth: 1,
+ borderStyle: "solid",
+ borderColor: "silver",
+ width: 322,
+ ":focus": {
+ borderColor: theme.themePrimary,
+ },
+ ":hover": {
+ borderColor: theme.themePrimary,
+ },
+ },
+ };
+
+ const documentCardUserStyles: Partial = {
+ root: {
+ marginTop: 2,
+ backgroundColor: theme?.white,
+ boxShadow: "0 5px 15px rgba(50, 50, 90, .1)",
+ ":hover": {
+ borderColor: theme.themePrimary,
+ backgroundColor: theme.neutralLighterAlt,
+ borderWidth: 1,
+ } as IStyle,
+ } as IStyle,
+ };
+
+ const componentClasses = mergeStyleSets({
+ container: {
+ borderWidth: 1,
+ borderStyle: "solid",
+ display: "block",
+ borderColor: "silver",
+ overflow: "hidden",
+ width: 320,
+ ":focus": {
+ borderWidth: 2,
+ borderColor: theme.themePrimary,
+ },
+ ":hover": {
+ borderWidth: 2,
+ borderColor: theme.themePrimary,
+ },
+ } as IStyle,
+ });
+
+ const mentionsClasses = mergeStyleSets({
+ mention: {
+ position: "relative",
+ zIndex: 9999,
+ color: theme.themePrimary,
+ pointerEvents: "none",
+ } as IStyle,
+ });
+
+ const reactMentionStyles = {
+ control: {
+ backgroundColor: "#fff",
+ fontSize: 12,
+ border: "none",
+ fontWeight: "normal",
+ outlineColor: theme.themePrimary,
+ borderRadius: 0,
+ } as IStyle,
+ "&multiLine": {
+ control: {
+ border: "none",
+ fontFamily:
+ '"Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue"',
+ minHeight: 35,
+ fontSize: 14,
+ fontWeight: 400,
+ borderRadius: 0,
+ } as IStyle,
+ highlighter: {
+ padding: 9,
+ border: "none",
+ borderWidth: 0,
+ borderRadius: 0,
+ } as IStyle,
+ input: {
+ padding: 9,
+ border: "none",
+ outline: "none",
+ } as IStyle,
+ },
+
+ "&singleLine": {
+ display: "inline-block",
+ height: 50,
+ outlineColor: theme.themePrimary,
+ border: "none",
+ highlighter: {
+ padding: 1,
+ border: "1px inset transparent",
+ },
+ input: {
+ padding: 1,
+ width: "100%",
+ borderRadius: 0,
+ border: "none",
+ },
+ },
+
+ suggestions: {
+ list: {
+ backgroundColor: "white",
+ border: "1px solid rgba(0,0,0,0.15)",
+ fontSize: 14,
+ },
+ item: {
+ padding: "5px 15px",
+ borderBottom: "1px solid",
+ borderBottomColor: theme.themeLight,
+ "&focused": {
+ backgroundColor: theme.neutralLighterAlt,
+ },
+ },
+ },
+ };
+
+ return {
+ documentCardUserStyles,
+ deleteButtonContainerStyles,
+ reactMentionStyles,
+ itemContainerStyles,
+ searchMentionContainerStyles,
+ mentionsClasses,
+ componentClasses,
+ };
+};
diff --git a/src/controls/listItemComments/components/Comments/CommentItem.tsx b/src/controls/listItemComments/components/Comments/CommentItem.tsx
new file mode 100644
index 000000000..9cfb36620
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/CommentItem.tsx
@@ -0,0 +1,45 @@
+import * as React from "react";
+import { ReactNode, useMemo } from "react";
+import format from "date-fns/format";
+import parseISO from "date-fns/parseISO";
+import { ActivityItem, Link, Text } from "@fluentui/react";
+import { Stack } from "@fluentui/react/lib/Stack";
+import { IComment } from "./IComment";
+import { CommentText } from "./CommentText";
+import { isEmpty } from "lodash";
+
+const PHOTO_URL = "/_layouts/15/userphoto.aspx?size=M&accountname=";
+
+export interface IRenderNotificationItemProps {
+ comment: IComment;
+}
+
+export const CommentItem: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ if (isEmpty(props.comment)) return null;
+ const { author, createdDate, text, mentions } = props.comment;
+
+ const activityDescription = useMemo((): ReactNode => {
+ const _activity: JSX.Element[] = [];
+ _activity.push(
+
+ {author.name}
+
+ );
+ _activity.push();
+ return _activity;
+ }, [mentions, text]);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/CommentText.tsx b/src/controls/listItemComments/components/Comments/CommentText.tsx
new file mode 100644
index 000000000..bbeb30880
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/CommentText.tsx
@@ -0,0 +1,73 @@
+import * as React from "react";
+import { useContext, useEffect, useState } from "react";
+import { Mention } from "./IComment";
+import { Text } from "@fluentui/react/lib/Text";
+import { LivePersona } from "../../../LivePersona";
+import { AppContext } from "../../common";
+import regexifyString from "regexify-string";
+import { Stack } from "@fluentui/react/lib/Stack";
+import { isArray, isObject } from "lodash";
+import he from 'he';
+export interface ICommentTextProps {
+ text: string;
+ mentions: Mention[];
+}
+
+export const CommentText: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ const [commentText, setCommentText] = useState("");
+ const { theme, serviceScope } = useContext(AppContext);
+ const { text, mentions } = props;
+ const mentionsResults: Mention[] = mentions;
+
+ useEffect(() => {
+ const hasMentions = mentions?.length ? true : false;
+ let result: string | JSX.Element[] = text;
+ if (hasMentions) {
+ result = regexifyString({
+ pattern: /@mention{\d+}/g,
+ decorator: (match, index) => {
+ const mention = mentionsResults[index];
+ const _name = `@${mention.name}`;
+ return (
+ <>
+ {_name}}
+ />
+ >
+ );
+ },
+ input: text,
+ }) as JSX.Element[];
+ }
+ setCommentText(result);
+ }, []);
+
+ return (
+ <>
+
+ {isArray(commentText) ? (
+ (commentText as any[]).map((el, i) => {
+ if (isObject(el)) {
+ return {el};
+ } else {
+ const _el: string = el.trim();
+ if (_el.length) {
+ return (
+
+ {he.decode(_el)}
+
+ );
+ }
+ }
+ })
+ ) : (
+ {he.decode(commentText)}
+ )}
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/CommentsList.tsx b/src/controls/listItemComments/components/Comments/CommentsList.tsx
new file mode 100644
index 000000000..176d2c9f8
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/CommentsList.tsx
@@ -0,0 +1,169 @@
+import * as React from "react";
+import { useContext, useEffect, useRef } from "react";
+import { Stack } from "@fluentui/react/lib/Stack";
+import { useSpAPI } from "../../hooks";
+import { EListItemCommentsStateTypes, ListItemCommentsStateContext } from "../ListItemCommentsStateProvider";
+import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
+import { IlistItemCommentsResults, IPageInfo } from "../../models";
+import { getScrollPosition } from "../../utils/utils";
+import { IErrorInfo } from "../ErrorInfo/IErrorInfo";
+import { RenderError } from "./RenderError";
+import { RenderSpinner } from "./RenderSpinner";
+import { Text } from "@fluentui/react/lib/Text";
+import { AddComment } from "../AddComment/AddComment";
+import { ECommentAction } from "../../common/ECommentAction";
+import { IAddCommentPayload } from "../../models/IAddCommentPayload";
+import { useCallback } from "react";
+import strings from "ControlStrings";
+import { RenderComments } from "./RenderComments";
+
+export const CommentsList: React.FunctionComponent = () => {
+ const { listItemCommentsState, setlistItemCommentsState } = useContext(ListItemCommentsStateContext);
+ const { configurationListClasses } = useListItemCommentsStyles();
+ const { getListItemComments, getNextPageOfComments, addComment, deleteComment } = useSpAPI();
+ const { comments, isScrolling, pageInfo, commentAction, commentToAdd, selectedComment } = listItemCommentsState;
+ const { hasMore, nextLink } = pageInfo;
+ const scrollPanelRef = useRef();
+ const { errorInfo } = listItemCommentsState;
+
+ const _loadComments = useCallback(async () => {
+ try {
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_IS_LOADING,
+ payload: true,
+ });
+ const _commentsResults: IlistItemCommentsResults = await getListItemComments();
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_LIST_ITEM_COMMENTS,
+ payload: _commentsResults.comments,
+ });
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_DATA_PAGE_INFO,
+ payload: { hasMore: _commentsResults.hasMore, nextLink: _commentsResults.nextLink } as IPageInfo,
+ });
+ setlistItemCommentsState({ type: EListItemCommentsStateTypes.SET_COMMENT_ACTION, payload: undefined });
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_IS_LOADING,
+ payload: false,
+ });
+ } catch (error) {
+ const _errorInfo: IErrorInfo = { showError: true, error: error.message };
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_ERROR_INFO,
+ payload: _errorInfo,
+ });
+ }
+ }, [setlistItemCommentsState]);
+
+ const _onAddComment = useCallback(
+ async (commentText: IAddCommentPayload) => {
+ try {
+ const _errorInfo: IErrorInfo = { showError: false, error: undefined };
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_ERROR_INFO,
+ payload: _errorInfo,
+ });
+ await addComment(commentText);
+ await _loadComments();
+ } catch (error) {
+ const _errorInfo: IErrorInfo = { showError: true, error: error };
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_ERROR_INFO,
+ payload: _errorInfo,
+ });
+ }
+ },
+ [setlistItemCommentsState, addComment, _loadComments]
+ );
+
+ const _onADeleteComment = useCallback(
+ async (commentId: number) => {
+ if (!commentId) return;
+ try {
+ const _errorInfo: IErrorInfo = { showError: false, error: undefined };
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_ERROR_INFO,
+ payload: _errorInfo,
+ });
+
+ await deleteComment(commentId);
+ await _loadComments();
+ } catch (error) {
+ const _errorInfo: IErrorInfo = { showError: true, error: error };
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_ERROR_INFO,
+ payload: _errorInfo,
+ });
+ }
+ },
+ [setlistItemCommentsState, _loadComments]
+ );
+
+ useEffect(() => {
+ switch (commentAction) {
+ case ECommentAction.ADD:
+ (async () => {
+ // Add new comment
+ await _onAddComment(commentToAdd);
+ })();
+ break;
+ case ECommentAction.DELETE:
+ (async () => {
+ // delete comment
+ const commentId = Number(selectedComment.id);
+ await _onADeleteComment(commentId);
+ })();
+ break;
+ default:
+ break;
+ }
+ }, [commentAction, selectedComment, commentToAdd, _onAddComment, _onADeleteComment]);
+
+ useEffect(() => {
+ (async () => {
+ await _loadComments();
+ })();
+ }, [_loadComments]);
+
+ const handleScroll = React.useCallback(async () => {
+ const _scrollPosition = getScrollPosition(scrollPanelRef.current);
+ if (isScrolling) return;
+ if (hasMore && _scrollPosition > 90) {
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_IS_SCROLLING,
+ payload: true,
+ });
+ const _commentsResults: IlistItemCommentsResults = await getNextPageOfComments(nextLink);
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_LIST_ITEM_COMMENTS,
+ payload: [...comments, ..._commentsResults.comments],
+ });
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_DATA_PAGE_INFO,
+ payload: { hasMore: _commentsResults.hasMore, nextLink: _commentsResults.nextLink } as IPageInfo,
+ });
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_IS_SCROLLING,
+ payload: false,
+ });
+ }
+ }, [hasMore, nextLink, isScrolling, setlistItemCommentsState]);
+
+ return (
+ <>
+
+
+
+
+ {strings.ListItemCommentsLabel}
+
+
+
+
+
+
+
+ {}
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/IComment.ts b/src/controls/listItemComments/components/Comments/IComment.ts
new file mode 100644
index 000000000..b58ed53f5
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/IComment.ts
@@ -0,0 +1,63 @@
+
+export interface IComment {
+ __metadata: Metadata;
+ likedBy: LikedBy;
+ replies: Replies;
+ author: Author;
+ createdDate: string;
+ id: string;
+ isLikedByUser: boolean;
+ isReply: boolean;
+ itemId: number;
+ likeCount: number;
+ listId: string;
+ mentions: Mention[];
+ parentId: string;
+ replyCount: number;
+ text: string;
+}
+
+export interface Mentions {
+ __metadata: string;
+ results: Mention[];
+}
+
+export interface Mention {
+ email: string;
+ id: number;
+ loginName: string;
+ name: string;
+}
+
+export interface Author {
+ __metadata: string;
+ email: string;
+ expiration?: string;
+ id: number;
+ isActive: boolean;
+ isExternal: boolean;
+ jobTitle?: string;
+ loginName: string;
+ name: string;
+ principalType: number;
+ userId?: string;
+ userPrincipalName?: string;
+}
+
+interface Replies {
+ results: any[];
+}
+
+interface LikedBy {
+ __deferred: Deferred;
+}
+
+interface Deferred {
+ uri: string;
+}
+
+interface Metadata {
+ id: string;
+ uri: string;
+ type: string;
+}
diff --git a/src/controls/listItemComments/components/Comments/RenderComments.tsx b/src/controls/listItemComments/components/Comments/RenderComments.tsx
new file mode 100644
index 000000000..ad19485ae
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderComments.tsx
@@ -0,0 +1,78 @@
+import { IconButton } from "office-ui-fabric-react/lib/Button";
+import { DocumentCard, DocumentCardDetails } from "office-ui-fabric-react/lib/DocumentCard";
+import { Stack } from "office-ui-fabric-react/lib/Stack";
+import * as React from "react";
+import { useCallback, useState } from "react";
+import { useContext } from "react";
+import { ConfirmDelete } from "../ConfirmDelete/ConfirmDelete";
+import { EListItemCommentsStateTypes, ListItemCommentsStateContext } from "../ListItemCommentsStateProvider";
+import { CommentItem } from "./CommentItem";
+import { IComment } from "./IComment";
+import { RenderNoComments } from "./RenderNoComments";
+import { RenderSpinner } from "./RenderSpinner";
+import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
+import { useBoolean } from "@fluentui/react-hooks";
+import { List } from "@fluentui/react/lib/List";
+import { ECommentAction } from "../..";
+
+export interface IRenderCommentsProps {}
+
+export const RenderComments: React.FunctionComponent = () => {
+ const { listItemCommentsState, setlistItemCommentsState } = useContext(ListItemCommentsStateContext);
+ const { documentCardStyles, itemContainerStyles, deleteButtonContainerStyles } = useListItemCommentsStyles();
+ const { comments, isLoading, selectedComment } = listItemCommentsState;
+
+ const [hideDialog, { toggle: setHideDialog }] = useBoolean(true);
+
+ const onRenderCell = useCallback(
+ (comment: IComment, index: number): JSX.Element => {
+ return (
+
+
+ {
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_SELECTED_COMMENT,
+ payload: comment,
+ });
+ setHideDialog();
+ }}
+ >
+
+
+
+
+
+
+
+ );
+ },
+ [comments]
+ );
+
+ return (
+ <>
+ {isLoading ? :
}
+ {
+ if (deleteComment) {
+ setlistItemCommentsState({
+ type: EListItemCommentsStateTypes.SET_COMMENT_ACTION,
+ payload: ECommentAction.DELETE,
+ });
+ }
+ setHideDialog();
+ }}
+ />
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/RenderError.tsx b/src/controls/listItemComments/components/Comments/RenderError.tsx
new file mode 100644
index 000000000..8150e281d
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderError.tsx
@@ -0,0 +1,33 @@
+import { Guid } from "@microsoft/sp-core-library";
+import { DocumentCard, DocumentCardDetails } from "office-ui-fabric-react/lib/DocumentCard";
+import { Stack } from "office-ui-fabric-react/lib/Stack";
+import * as React from "react";
+import { ErrorInfo } from "../ErrorInfo";
+import { IErrorInfo } from "../ErrorInfo/IErrorInfo";
+import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
+export interface IRenderErrorProps {
+ errorInfo: IErrorInfo;
+}
+export const RenderError: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ const { showError, error } = props.errorInfo || ({} as IErrorInfo);
+ const { documentCardStyles } = useListItemCommentsStyles();
+
+ if (!showError) return null;
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/RenderNoComments.tsx b/src/controls/listItemComments/components/Comments/RenderNoComments.tsx
new file mode 100644
index 000000000..11dfdbfc0
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderNoComments.tsx
@@ -0,0 +1,28 @@
+import { Guid } from "@microsoft/sp-core-library";
+import { DocumentCard, DocumentCardDetails } from "office-ui-fabric-react/lib/DocumentCard";
+import { Stack } from "office-ui-fabric-react/lib/Stack";
+import * as React from "react";
+import { Text } from "office-ui-fabric-react/lib/Text";
+import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
+import strings from "ControlStrings";
+
+export const RenderNoComments: React.FunctionComponent = () => {
+ const { documentCardStyles } = useListItemCommentsStyles();
+ return (
+ <>
+
+
+
+ {strings.ListItemCommentsNoCommentsLabel}
+
+
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/RenderSpinner.tsx b/src/controls/listItemComments/components/Comments/RenderSpinner.tsx
new file mode 100644
index 000000000..78020509c
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderSpinner.tsx
@@ -0,0 +1,31 @@
+import { Spinner } from "@fluentui/react/lib/Spinner";
+import { Guid } from "@microsoft/sp-core-library";
+import { DocumentCard, DocumentCardDetails } from "office-ui-fabric-react/lib/DocumentCard";
+import { SpinnerSize } from "office-ui-fabric-react/lib/Spinner";
+import { Stack } from "office-ui-fabric-react/lib/Stack";
+import * as React from "react";
+import { useContext } from "react";
+import { ListItemCommentsStateContext } from "../ListItemCommentsStateProvider";
+import { useListItemCommentsStyles } from "./useListItemCommentsStyles";
+
+export const RenderSpinner: React.FunctionComponent = () => {
+ const { documentCardStyles } = useListItemCommentsStyles();
+ const { listItemCommentsState } = useContext(ListItemCommentsStateContext);
+ const { isScrolling , isLoading} = listItemCommentsState;
+ if (!isScrolling && !isLoading) return null;
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/RenderUser/RenderUser.tsx b/src/controls/listItemComments/components/Comments/RenderUser/RenderUser.tsx
new file mode 100644
index 000000000..95e9e1bf0
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderUser/RenderUser.tsx
@@ -0,0 +1,41 @@
+import { Guid } from "@microsoft/sp-core-library";
+import { DocumentCard, DocumentCardDetails } from "office-ui-fabric-react/lib/DocumentCard";
+import { Persona } from "office-ui-fabric-react/lib/Persona";
+import { Stack } from "office-ui-fabric-react/lib/Stack";
+import * as React from "react";
+import { IUserInfo } from "../../../models/IUsersResults";
+import { useListItemCommentsStyles } from "../useListItemCommentsStyles";
+import { PHOTO_URL } from "./../../../common/constants";
+
+export interface IRenderUserProps {
+ user: IUserInfo;
+}
+export const RenderUser: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ const { user } = props;
+ const { documentCardUserStyles, renderUserContainerStyles } = useListItemCommentsStyles();
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/Comments/RenderUser/index.ts b/src/controls/listItemComments/components/Comments/RenderUser/index.ts
new file mode 100644
index 000000000..59e2c1aa7
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/RenderUser/index.ts
@@ -0,0 +1 @@
+export * from './RenderUser';
diff --git a/src/controls/listItemComments/components/Comments/useListItemCommentsStyles.ts b/src/controls/listItemComments/components/Comments/useListItemCommentsStyles.ts
new file mode 100644
index 000000000..5b1547c45
--- /dev/null
+++ b/src/controls/listItemComments/components/Comments/useListItemCommentsStyles.ts
@@ -0,0 +1,129 @@
+import * as React from "react";
+import {
+ IDocumentCardStyles,
+ IProcessedStyleSet,
+ IStackStyles,
+ IStyle,
+ mergeStyles,
+ mergeStyleSets,
+} from "@fluentui/react";
+import { AppContext } from "../../common";
+import { IScrollablePaneStyles } from "office-ui-fabric-react/lib/ScrollablePane";
+import { TILE_HEIGHT } from "../../common/constants";
+
+interface returnObjectStyles {
+ itemContainerStyles: IStackStyles;
+ deleteButtonContainerStyles: Partial;
+ userListContainerStyles: Partial;
+ renderUserContainerStyles: Partial;
+ documentCardStyles: Partial;
+ documentCardDeleteStyles: Partial;
+ documentCardUserStyles: Partial;
+ configurationListClasses: any;
+}
+
+export const useListItemCommentsStyles = (): returnObjectStyles => {
+ const { theme, numberCommentsPerPage } = React.useContext(AppContext);
+ // Calc Height List tiles Container Based on number Items per Page
+ const tilesHeight: number = numberCommentsPerPage
+ ? (numberCommentsPerPage < 5 ? 5 : numberCommentsPerPage) * TILE_HEIGHT + 35
+ : 7 * TILE_HEIGHT;
+
+ const itemContainerStyles: IStackStyles = {
+ root: { paddingTop: 0, paddingLeft: 20, paddingRight: 20, paddingBottom: 20 } as IStyle,
+ };
+
+ const deleteButtonContainerStyles: Partial = {
+ root: {
+ position: "absolute",
+ top: 0,
+ right: 0,
+ },
+ };
+
+ const userListContainerStyles: Partial = {
+ root: { paddingLeft: 2, paddingRight: 2, paddingBottom: 2, minWidth: 206 },
+ };
+
+ const renderUserContainerStyles: Partial = {
+ root: { paddingTop: 5, paddingBottom: 5, paddingLeft: 10, paddingRight: 10 },
+ };
+ const documentCardStyles: Partial = {
+ root: {
+ marginBottom: 7,
+ width: 322,
+ backgroundColor: theme.neutralLighterAlt,
+ ":hover": {
+ borderColor: theme.themePrimary,
+ borderWidth: 1,
+ } as IStyle,
+ } as IStyle,
+ };
+
+ const documentCardDeleteStyles: Partial = {
+ root: {
+ marginBottom: 5,
+ backgroundColor: theme.neutralLighterAlt,
+ ":hover": {
+ borderColor: theme.themePrimary,
+ borderWidth: 1,
+ } as IStyle,
+ } as IStyle,
+ };
+
+ const documentCardUserStyles: Partial = {
+ root: {
+ marginTop: 2,
+ backgroundColor: theme?.white,
+ boxShadow: "0 5px 15px rgba(50, 50, 90, .1)",
+
+ ":hover": {
+ borderColor: theme.themePrimary,
+ backgroundColor: theme.neutralLighterAlt,
+ borderWidth: 1,
+ } as IStyle,
+ } as IStyle,
+ };
+
+ const configurationListClasses = mergeStyleSets({
+ listIcon: mergeStyles({
+ fontSize: 18,
+ width: 18,
+ height: 18,
+ color: theme.themePrimary,
+ }),
+ nolistItemIcon: mergeStyles({
+ fontSize: 28,
+ width: 28,
+ height: 28,
+ color: theme.themePrimary,
+ }),
+ divContainer: {
+ display: "block",
+ } as IStyle,
+ titlesContainer: {
+ height: tilesHeight,
+ marginBottom: 10,
+ display: "flex",
+ marginTop: 15,
+ overflow: "auto",
+ "&::-webkit-scrollbar-thumb": {
+ backgroundColor: theme.neutralLighter,
+ },
+ "&::-webkit-scrollbar": {
+ width: 5,
+ },
+ } as IStyle,
+ });
+
+ return {
+ itemContainerStyles,
+ deleteButtonContainerStyles,
+ userListContainerStyles,
+ renderUserContainerStyles,
+ documentCardStyles,
+ documentCardDeleteStyles,
+ documentCardUserStyles,
+ configurationListClasses,
+ };
+};
diff --git a/src/controls/listItemComments/components/ConfirmDelete/ConfirmDelete.tsx b/src/controls/listItemComments/components/ConfirmDelete/ConfirmDelete.tsx
new file mode 100644
index 000000000..212020066
--- /dev/null
+++ b/src/controls/listItemComments/components/ConfirmDelete/ConfirmDelete.tsx
@@ -0,0 +1,68 @@
+import * as React from "react";
+import { useContext } from "react";
+import { ListItemCommentsStateContext } from "../ListItemCommentsStateProvider";
+import { Dialog, DialogType, DialogFooter } from "@fluentui/react/lib/Dialog";
+import { PrimaryButton, DefaultButton } from "@fluentui/react/lib/Button";
+import { CommentItem } from "../Comments/CommentItem";
+import { DocumentCard } from "office-ui-fabric-react/lib/components/DocumentCard";
+import { DocumentCardDetails } from "@fluentui/react/lib/DocumentCard";
+import { Stack } from "@fluentui/react/lib/Stack";
+import { useListItemCommentsStyles } from "../Comments/useListItemCommentsStyles";
+import { IDialogContentStyles } from "office-ui-fabric-react";
+import strings from "ControlStrings";
+export interface IConfirmDeleteProps {
+ hideDialog: boolean;
+ onDismiss: (deleteComment: boolean) => void;
+}
+
+export const ConfirmDelete: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ const { listItemCommentsState, setlistItemCommentsState } = useContext(ListItemCommentsStateContext);
+ const { documentCardDeleteStyles, itemContainerStyles } = useListItemCommentsStyles();
+ const { hideDialog, onDismiss } = props;
+ const { selectedComment } = listItemCommentsState;
+ const stylesSubText: Partial = {
+ subText: { fontWeight: 600 },
+ };
+
+ const modelProps = {
+ isBlocking: false,
+ styles: { main: { maxWidth: 450 } },
+ };
+ const dialogContentProps = {
+ type: DialogType.largeHeader,
+ title: strings.ListItemCommentsDialogDeleteTitle,
+ styles: stylesSubText,
+ subText: strings.ListItemCommentDIalogDeleteSubText,
+ };
+ return (
+ <>
+
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/ConfirmDelete/index.ts b/src/controls/listItemComments/components/ConfirmDelete/index.ts
new file mode 100644
index 000000000..86c8276ff
--- /dev/null
+++ b/src/controls/listItemComments/components/ConfirmDelete/index.ts
@@ -0,0 +1 @@
+export * from './ConfirmDelete';
diff --git a/src/controls/listItemComments/components/ErrorInfo/ErrorInfo.tsx b/src/controls/listItemComments/components/ErrorInfo/ErrorInfo.tsx
new file mode 100644
index 000000000..f6a14cfaf
--- /dev/null
+++ b/src/controls/listItemComments/components/ErrorInfo/ErrorInfo.tsx
@@ -0,0 +1,26 @@
+import * as React from "react";
+import { MessageBar, MessageBarType } from "@fluentui/react/lib/MessageBar";
+import { Stack } from "@fluentui/react/lib/Stack";
+export interface IErrorInfoProps {
+ error: Error;
+ showError: boolean;
+ showStack?: boolean;
+}
+
+export const ErrorInfo: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ const { error, showStack, showError } = props;
+ return (
+ <>
+ {showError ? (
+
+
+ {error.message}
+ {showStack ? error.stack : ""}
+
+
+ ) : null}
+ >
+ );
+};
diff --git a/src/controls/listItemComments/components/ErrorInfo/IErrorInfo.ts b/src/controls/listItemComments/components/ErrorInfo/IErrorInfo.ts
new file mode 100644
index 000000000..575256246
--- /dev/null
+++ b/src/controls/listItemComments/components/ErrorInfo/IErrorInfo.ts
@@ -0,0 +1,4 @@
+export interface IErrorInfo {
+ error:Error;
+ showError:boolean;
+}
diff --git a/src/controls/listItemComments/components/ErrorInfo/index.ts b/src/controls/listItemComments/components/ErrorInfo/index.ts
new file mode 100644
index 000000000..39346c88c
--- /dev/null
+++ b/src/controls/listItemComments/components/ErrorInfo/index.ts
@@ -0,0 +1,2 @@
+export * from './ErrorInfo';
+export * from './IErrorInfo';
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/EListItemCommentsStateTypes.ts b/src/controls/listItemComments/components/ListItemCommentsStateProvider/EListItemCommentsStateTypes.ts
new file mode 100644
index 000000000..f2927e492
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/EListItemCommentsStateTypes.ts
@@ -0,0 +1,10 @@
+export enum EListItemCommentsStateTypes {
+ "SET_ERROR_INFO" = "SET_ERROR_INFO",
+ "SET_LIST_ITEM_COMMENTS" = "SET_LIST_ITEM_COMMENTS",
+ "SET_IS_LOADING" = "SET_IS_LOADING",
+ "SET_IS_SCROLLING" = "SET_IS_SCROLLING",
+ "SET_DATA_PAGE_INFO" = "SET_DATA_PAGE_INFO",
+ "SET_COMMENT_ACTION" = "SET_COMMENT_ACTION",
+ "SET_ADD_COMMENT" = "SET_ADD_COMMENT",
+ "SET_SELECTED_COMMENT" = "SET_SELECTED_COMMENT",
+}
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsState.ts b/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsState.ts
new file mode 100644
index 000000000..c10affe41
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsState.ts
@@ -0,0 +1,19 @@
+
+
+import { IErrorInfo } from "../ErrorInfo/IErrorInfo";
+import { IComment } from "../Comments/IComment";
+import { IPageInfo } from "../../models";
+import { ECommentAction } from "../../common/ECommentAction";
+import { IAddCommentPayload } from "../../models/IAddCommentPayload";
+
+// Global State (Store)
+export interface IListItemCommentsState {
+ errorInfo: IErrorInfo | undefined;
+ comments: IComment[];
+ isLoading: boolean;
+ isScrolling: boolean;
+ pageInfo: IPageInfo;
+ commentAction: ECommentAction;
+ commentToAdd: IAddCommentPayload;
+ selectedComment: IComment;
+}
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsStateContext.ts b/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsStateContext.ts
new file mode 100644
index 000000000..1f2a4f0ff
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/IListItemCommentsStateContext.ts
@@ -0,0 +1,6 @@
+import { EListItemCommentsStateTypes } from "./EListItemCommentsStateTypes";
+import { IListItemCommentsState } from "./IListItemCommentsState";
+export interface IListItemCommentsStateContext {
+ listItemCommentsState: IListItemCommentsState;
+ setlistItemCommentsState: React.Dispatch<{type:EListItemCommentsStateTypes, payload: unknown}>;
+}
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateProvider.tsx b/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateProvider.tsx
new file mode 100644
index 000000000..21fcdd9b5
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateProvider.tsx
@@ -0,0 +1,36 @@
+import React, { createContext, useReducer } from "react";
+import { ListItemCommentsStateReducer } from "./ListItemCommentsStateReducer";
+import { IListItemCommentsState } from "./IListItemCommentsState";
+import { IListItemCommentsStateContext } from "./IListItemCommentsStateContext";
+import { IPageInfo } from "../../models/IPageInfo";
+import { IAddCommentPayload } from "../../models/IAddCommentPayload";
+import { IComment } from "../Comments/IComment";
+// Reducer
+// Initial State (Store )
+const initialState: IListItemCommentsState = {
+ errorInfo: undefined,
+ comments: [],
+ isLoading: false,
+ isScrolling: false,
+ pageInfo: {} as IPageInfo,
+ commentAction: undefined,
+ commentToAdd: {} as IAddCommentPayload,
+ selectedComment: {} as IComment,
+};
+
+const stateInit: IListItemCommentsStateContext = {
+ listItemCommentsState: initialState,
+ setlistItemCommentsState: ({}) => {return;},
+};
+
+// (store)
+export const ListItemCommentsStateContext = createContext(stateInit);
+export const ListItemCommentsStateProvider = (props: { children: React.ReactNode }): JSX.Element => {
+ const [listItemCommentsState, setlistItemCommentsState] = useReducer(ListItemCommentsStateReducer, initialState);
+
+ return (
+
+ {props.children}
+
+ );
+};
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateReducer.ts b/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateReducer.ts
new file mode 100644
index 000000000..58ba4456d
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/ListItemCommentsStateReducer.ts
@@ -0,0 +1,34 @@
+import { IErrorInfo } from "../ErrorInfo/IErrorInfo";
+import { IComment } from "../Comments/IComment";
+import { EListItemCommentsStateTypes } from "./EListItemCommentsStateTypes";
+import { IListItemCommentsState } from "./IListItemCommentsState";
+import { IPageInfo } from "../../models/IPageInfo";
+import { ECommentAction } from "../../common/ECommentAction";
+import { IAddCommentPayload } from "../../models/IAddCommentPayload";
+
+// Reducer
+export const ListItemCommentsStateReducer = (
+ state: IListItemCommentsState,
+ action: { type: EListItemCommentsStateTypes; payload: unknown }
+): IListItemCommentsState => {
+ switch (action.type) {
+ case EListItemCommentsStateTypes.SET_ERROR_INFO:
+ return { ...state, errorInfo: action.payload as IErrorInfo };
+ case EListItemCommentsStateTypes.SET_LIST_ITEM_COMMENTS:
+ return { ...state, comments: action.payload as IComment[] };
+ case EListItemCommentsStateTypes.SET_IS_LOADING:
+ return { ...state, isLoading: action.payload as boolean };
+ case EListItemCommentsStateTypes.SET_IS_SCROLLING:
+ return { ...state, isScrolling: action.payload as boolean };
+ case EListItemCommentsStateTypes.SET_DATA_PAGE_INFO:
+ return { ...state, pageInfo: action.payload as IPageInfo };
+ case EListItemCommentsStateTypes.SET_COMMENT_ACTION:
+ return { ...state, commentAction: action.payload as ECommentAction };
+ case EListItemCommentsStateTypes.SET_ADD_COMMENT:
+ return { ...state, commentToAdd: action.payload as IAddCommentPayload };
+ case EListItemCommentsStateTypes.SET_SELECTED_COMMENT:
+ return { ...state, selectedComment: action.payload as IComment };
+ default:
+ return state;
+ }
+};
diff --git a/src/controls/listItemComments/components/ListItemCommentsStateProvider/index.ts b/src/controls/listItemComments/components/ListItemCommentsStateProvider/index.ts
new file mode 100644
index 000000000..bac3ec4a4
--- /dev/null
+++ b/src/controls/listItemComments/components/ListItemCommentsStateProvider/index.ts
@@ -0,0 +1,5 @@
+export * from './EListItemCommentsStateTypes';
+export * from './IListItemCommentsState';
+export * from './IListItemCommentsStateContext';
+export * from './ListItemCommentsStateProvider';
+export * from './ListItemCommentsStateReducer';
diff --git a/src/controls/listItemComments/components/index.ts b/src/controls/listItemComments/components/index.ts
new file mode 100644
index 000000000..e9d903390
--- /dev/null
+++ b/src/controls/listItemComments/components/index.ts
@@ -0,0 +1,4 @@
+export * from './AddComment';
+export * from './ConfirmDelete';
+export * from './ErrorInfo';
+export * from './ListItemCommentsStateProvider';
diff --git a/src/controls/listItemComments/hooks/index.ts b/src/controls/listItemComments/hooks/index.ts
new file mode 100644
index 000000000..bf68cd8ba
--- /dev/null
+++ b/src/controls/listItemComments/hooks/index.ts
@@ -0,0 +1,2 @@
+export * from './useMsGraphAPI';
+export * from './useSpAPI';
diff --git a/src/controls/listItemComments/hooks/useMsGraphAPI.ts b/src/controls/listItemComments/hooks/useMsGraphAPI.ts
new file mode 100644
index 000000000..0992933fd
--- /dev/null
+++ b/src/controls/listItemComments/hooks/useMsGraphAPI.ts
@@ -0,0 +1,91 @@
+import { AppContext } from "../common";
+import { useContext, useCallback } from "react";
+import { MSGraphClientFactory, MSGraphClient } from "@microsoft/sp-http";
+import { Person } from "@microsoft/microsoft-graph-types";
+import { IUserInfo, IUsersResults } from "../models/IUsersResults";
+
+interface returnObject {
+ getUsers: (search: string) => Promise;
+ getUsersNextPage: (nextLink: string) => Promise;
+ getSuggestions: () => Promise;
+}
+
+export const useMsGraphAPI = (): returnObject => {
+ const { serviceScope } = useContext(AppContext);
+ let _msGraphClient: MSGraphClient = undefined;
+ serviceScope.whenFinished(async () => {
+ _msGraphClient = await serviceScope.consume(MSGraphClientFactory.serviceKey).getClient();
+ });
+ const getSuggestions = useCallback(async (): Promise => {
+ if (!_msGraphClient) return;
+ const _users: IUserInfo[] = [];
+
+ const suggestedUsersResults = (await _msGraphClient
+ .api(`me/people`)
+ .header("ConsistencyLevel", "eventual")
+ .filter(`personType/class eq 'Person' and personType/subclass eq 'OrganizationUser'`)
+ .orderby(`displayName`)
+ .get()) as any;
+ console.log("rs", suggestedUsersResults);
+ const _sugestions: Person[] = suggestedUsersResults.value as Person[];
+ for (const sugestion of _sugestions) {
+ _users.push({
+ displayName: sugestion.displayName,
+ givenName: sugestion.givenName,
+ id: sugestion.id,
+ mail: sugestion.scoredEmailAddresses[0].address,
+ });
+ }
+
+ const returnInfo: IUsersResults = {
+ users: _users,
+ hasMore: false,
+ nextLink: undefined,
+ };
+ return returnInfo;
+ }, [serviceScope, MSGraphClientFactory]);
+
+ const getUsers = useCallback(
+ async (search: string): Promise => {
+ if (!_msGraphClient || !search) return;
+ let _filter = "";
+
+ if (search.length) {
+ _filter = `mail ne null AND (startswith(mail,'${search}') OR startswith(displayName,'${search}'))`;
+ }
+
+ const usersResults = await _msGraphClient
+ .api(`/users`)
+ .header("ConsistencyLevel", "eventual")
+ .filter(_filter)
+ .orderby(`displayName`)
+ .count(true)
+ .top(25)
+ .get();
+
+ const returnInfo: IUsersResults = {
+ users: usersResults.value,
+ hasMore: usersResults["@odata.nextLink"] ? true : false,
+ nextLink: usersResults["@odata.nextLink"] ?? undefined,
+ };
+ return returnInfo;
+ },
+ [serviceScope, MSGraphClientFactory]
+ );
+
+ const getUsersNextPage = useCallback(
+ async (nextLink: string): Promise => {
+ if (!_msGraphClient) return;
+ const usersResults = await _msGraphClient.api(`${nextLink}`).get();
+ const returnInfo: IUsersResults = {
+ users: usersResults.value,
+ hasMore: usersResults["@odata.nextLink"] ? true : false,
+ nextLink: usersResults["@odata.nextLink"] ?? undefined,
+ };
+ return returnInfo;
+ },
+ [serviceScope, MSGraphClientFactory]
+ );
+
+ return { getUsers, getUsersNextPage, getSuggestions };
+};
diff --git a/src/controls/listItemComments/hooks/useSpAPI.ts b/src/controls/listItemComments/hooks/useSpAPI.ts
new file mode 100644
index 000000000..fffb47961
--- /dev/null
+++ b/src/controls/listItemComments/hooks/useSpAPI.ts
@@ -0,0 +1,106 @@
+import { AppContext } from "../common";
+import { useContext, useCallback } from "react";
+import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from "@microsoft/sp-http";
+import { IlistItemCommentsResults } from "./../models";
+import { IAddCommentPayload } from "../models/IAddCommentPayload";
+import { IComment } from "../components/Comments/IComment";
+import { PageContext } from "@microsoft/sp-page-context";
+interface returnObject {
+ getListItemComments: () => Promise;
+ getNextPageOfComments: (nextLink: string) => Promise;
+ addComment: (comment: IAddCommentPayload) => Promise;
+ deleteComment: (commentId: number) => Promise;
+}
+
+export const useSpAPI = (): returnObject => {
+ const { serviceScope, webUrl, listId, itemId, numberCommentsPerPage } = useContext(AppContext);
+ let _webUrl: string = "";
+ serviceScope.whenFinished(async () => {
+ _webUrl = serviceScope.consume(PageContext.serviceKey).web.absoluteUrl;
+ });
+ //https://contoso.sharepoint.com/sites/ThePerspective/_api/web/lists(@a1)/GetItemById(@a2)/Comments(@a3)?@a1=%27%7BE738C4B3%2D6CFF%2D493A%2DA8DA%2DDBBF4732E3BF%7D%27&@a2=%2729%27&@a3=%273%27
+
+ const deleteComment = useCallback(
+ async (commentId: number): Promise => {
+ const spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
+ if (!spHttpClient) return;
+ const _endPointUrl = `${
+ webUrl ?? _webUrl
+ }/_api/web/lists(@a1)/GetItemById(@a2)/Comments(@a3)?@a1='${listId}'&@a2='${itemId}'&@a3='${commentId}'`;
+ const spOpts: ISPHttpClientOptions = {
+ method: "DELETE",
+ };
+ const _deleteResults: SPHttpClientResponse = await spHttpClient.fetch(
+ `${_endPointUrl}`,
+ SPHttpClient.configurations.v1,
+ spOpts
+ );
+ return;
+ },
+ [serviceScope]
+ );
+
+ const addComment = useCallback(
+ async (comment: IAddCommentPayload): Promise => {
+ const spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
+ if (!spHttpClient) return;
+ const _endPointUrl = `${
+ webUrl ?? _webUrl
+ }/_api/web/lists(@a1)/GetItemById(@a2)/Comments()?@a1='${listId}'&@a2='${itemId}'`;
+ const spOpts: ISPHttpClientOptions = {
+ body: `{ "text": "${comment.text}", "mentions": ${JSON.stringify(comment.mentions)}}`,
+ };
+ const _listResults: SPHttpClientResponse = await spHttpClient.post(
+ `${_endPointUrl}`,
+ SPHttpClient.configurations.v1,
+ spOpts
+ );
+ const _commentResults: IComment = (await _listResults.json()) as IComment;
+ return _commentResults;
+ },
+ [serviceScope]
+ );
+
+ const getListItemComments = useCallback(async (): Promise => {
+ const spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
+ if (!spHttpClient) return;
+ const _endPointUrl = `${
+ webUrl ?? _webUrl
+ }/_api/web/lists(@a1)/GetItemById(@a2)/GetComments()?@a1='${listId}'&@a2='${itemId}'&$top=${
+ numberCommentsPerPage ?? 10
+ }`;
+ const _listResults: SPHttpClientResponse = await spHttpClient.get(
+ `${_endPointUrl}`,
+ SPHttpClient.configurations.v1
+ );
+ const _commentsResults = (await _listResults.json()) as any;
+ const _returnComments: IlistItemCommentsResults = {
+ comments: _commentsResults.value,
+ hasMore: _commentsResults["@odata.nextLink"] ? true : false,
+ nextLink: _commentsResults["@odata.nextLink"] ?? undefined,
+ };
+ return _returnComments;
+ }, [serviceScope]);
+
+ const getNextPageOfComments = useCallback(
+ async (nextLink: string): Promise => {
+ const spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
+ if (!spHttpClient || !nextLink) return;
+ const _endPointUrl = nextLink;
+ const _listResults: SPHttpClientResponse = await spHttpClient.get(
+ `${_endPointUrl}`,
+ SPHttpClient.configurations.v1
+ );
+ const _commentsResults = (await _listResults.json()) as any;
+ const _returnComments: IlistItemCommentsResults = {
+ comments: _commentsResults.value,
+ hasMore: _commentsResults["@odata.nextLink"] ? true : false,
+ nextLink: _commentsResults["@odata.nextLink"] ?? undefined,
+ };
+ return _returnComments;
+ },
+ [serviceScope]
+ );
+
+ return { getListItemComments, getNextPageOfComments, addComment, deleteComment };
+};
diff --git a/src/controls/listItemComments/index.ts b/src/controls/listItemComments/index.ts
new file mode 100644
index 000000000..9e69126d4
--- /dev/null
+++ b/src/controls/listItemComments/index.ts
@@ -0,0 +1,5 @@
+export * from './ListItemComments';
+export * from './common';
+export * from './components';
+export * from './hooks';
+export * from './models';
diff --git a/src/controls/listItemComments/models/IAddCommentPayload.ts b/src/controls/listItemComments/models/IAddCommentPayload.ts
new file mode 100644
index 000000000..ab4de1b23
--- /dev/null
+++ b/src/controls/listItemComments/models/IAddCommentPayload.ts
@@ -0,0 +1,8 @@
+export interface IAddCommentPayload {
+ text: string;
+ mentions: IAddMention[];
+}
+interface IAddMention {
+ email: string;
+ name: string;
+}
diff --git a/src/controls/listItemComments/models/IPageInfo.ts b/src/controls/listItemComments/models/IPageInfo.ts
new file mode 100644
index 000000000..766af35e1
--- /dev/null
+++ b/src/controls/listItemComments/models/IPageInfo.ts
@@ -0,0 +1,4 @@
+export interface IPageInfo {
+ hasMore: boolean;
+ nextLink: string;
+}
diff --git a/src/controls/listItemComments/models/IUsersResults.ts b/src/controls/listItemComments/models/IUsersResults.ts
new file mode 100644
index 000000000..6fe5d10b2
--- /dev/null
+++ b/src/controls/listItemComments/models/IUsersResults.ts
@@ -0,0 +1,12 @@
+export interface IUsersResults {
+ users: IUserInfo[];
+ hasMore?: boolean;
+ nextLink?: string;
+}
+export interface IUserInfo {
+ displayName: string;
+ givenName?: string;
+ mail: string;
+ userPrincipalName?: string;
+ id: string;
+}
diff --git a/src/controls/listItemComments/models/IlistItemCommentsResults.ts b/src/controls/listItemComments/models/IlistItemCommentsResults.ts
new file mode 100644
index 000000000..9ec6992df
--- /dev/null
+++ b/src/controls/listItemComments/models/IlistItemCommentsResults.ts
@@ -0,0 +1,6 @@
+import { IComment } from "../components/Comments/IComment";
+export interface IlistItemCommentsResults {
+ comments: IComment[];
+ hasMore?: boolean;
+ nextLink?: string;
+}
diff --git a/src/controls/listItemComments/models/index.ts b/src/controls/listItemComments/models/index.ts
new file mode 100644
index 000000000..5bd12155a
--- /dev/null
+++ b/src/controls/listItemComments/models/index.ts
@@ -0,0 +1,4 @@
+export * from './IAddCommentPayload';
+export * from './IPageInfo';
+export * from './IUsersResults';
+export * from './IlistItemCommentsResults';
diff --git a/src/controls/listItemComments/utils/utils.ts b/src/controls/listItemComments/utils/utils.ts
new file mode 100644
index 000000000..ea9ce4b1e
--- /dev/null
+++ b/src/controls/listItemComments/utils/utils.ts
@@ -0,0 +1,118 @@
+import { SPComponentLoader } from "@microsoft/sp-loader";
+const DEFAULT_PERSONA_IMG_HASH: string = "7ad602295f8386b7615b582d87bcc294";
+const DEFAULT_IMAGE_PLACEHOLDER_HASH: string = "4a48f26592f4e1498d7a478a4c48609c";
+const MD5_MODULE_ID: string = "8494e7d7-6b99-47b2-a741-59873e42f16f";
+const PROFILE_IMAGE_URL: string = "/_layouts/15/userphoto.aspx?size=M&accountname=";
+
+export const getScrollPosition = (_dataListContainerRef: any) => {
+ const { scrollTop, scrollHeight, clientHeight } = _dataListContainerRef;
+ const percentNow = (scrollTop / (scrollHeight - clientHeight)) * 100;
+ return percentNow;
+};
+
+export const b64toBlob = async (b64Data: any, contentType: string, sliceSize?: number): Promise => {
+ contentType = contentType || "image/png";
+ sliceSize = sliceSize || 512;
+
+ let byteCharacters: string = atob(b64Data);
+ let byteArrays = [];
+
+ for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
+ let slice = byteCharacters.slice(offset, offset + sliceSize);
+
+ let byteNumbers = new Array(slice.length);
+ for (let i = 0; i < slice.length; i++) {
+ byteNumbers[i] = slice.charCodeAt(i);
+ }
+
+ let byteArray = new Uint8Array(byteNumbers);
+ byteArrays.push(byteArray);
+ }
+
+ let blob = new Blob(byteArrays, { type: contentType });
+ return blob;
+};
+export const blobToBase64 = (blob: Blob): Promise => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onerror = reject;
+ reader.onload = (_) => {
+ resolve(reader.result as string);
+ };
+ reader.readAsDataURL(blob);
+ });
+};
+
+export const getImageBase64 = async (pictureUrl: string): Promise => {
+ console.log(pictureUrl);
+ return new Promise((resolve, reject) => {
+ let image = new Image();
+ image.addEventListener("load", () => {
+ let tempCanvas = document.createElement("canvas");
+ (tempCanvas.width = image.width),
+ (tempCanvas.height = image.height),
+ tempCanvas.getContext("2d").drawImage(image, 0, 0);
+ let base64Str;
+ try {
+ base64Str = tempCanvas.toDataURL("image/png");
+ } catch (e) {
+ return "";
+ }
+ base64Str = base64Str.replace(/^data:image\/png;base64,/, "");
+ resolve(base64Str);
+ });
+ image.src = pictureUrl;
+ });
+};
+/**
+ * Get MD5Hash for the image url to verify whether user has default image or custom image
+ * @param url
+ */
+export const getMd5HashForUrl = async (url: string) => {
+ return new Promise(async (resolve, reject) => {
+ // tslint:disable-next-line: no-use-before-declare
+ const library: any = await loadSPComponentById(MD5_MODULE_ID);
+ try {
+ const md5Hash = library.Md5Hash;
+ if (md5Hash) {
+ const convertedHash = md5Hash(url);
+ resolve(convertedHash);
+ }
+ } catch (error) {
+ resolve(url);
+ }
+ });
+};
+
+/**
+ * Load SPFx component by id, SPComponentLoader is used to load the SPFx components
+ * @param componentId - componentId, guid of the component library
+ */
+export const loadSPComponentById = async (componentId: string) => {
+ return new Promise((resolve, reject) => {
+ SPComponentLoader.loadComponentById(componentId)
+ .then((component: any) => {
+ resolve(component);
+ })
+ .catch((error) => {});
+ });
+};
+/**
+ * Gets user photo
+ * @param userId
+ * @returns user photo
+ */
+export const getUserPhoto = async (userId): Promise => {
+ const personaImgUrl = PROFILE_IMAGE_URL + userId;
+
+ // tslint:disable-next-line: no-use-before-declare
+ const url: string = await getImageBase64(personaImgUrl);
+ // tslint:disable-next-line: no-use-before-declare
+ const newHash = await getMd5HashForUrl(url);
+
+ if (newHash !== DEFAULT_PERSONA_IMG_HASH && newHash !== DEFAULT_IMAGE_PLACEHOLDER_HASH) {
+ return "data:image/png;base64," + url;
+ } else {
+ return "undefined";
+ }
+};
diff --git a/src/loc/bg-bg.ts b/src/loc/bg-bg.ts
index f7932872e..91c042b69 100644
--- a/src/loc/bg-bg.ts
+++ b/src/loc/bg-bg.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "No comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/ca-es.ts b/src/loc/ca-es.ts
index 9834fbedf..e0a105953 100644
--- a/src/loc/ca-es.ts
+++ b/src/loc/ca-es.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "No comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/da-dk.ts b/src/loc/da-dk.ts
index 76cb143f2..828c3650e 100644
--- a/src/loc/da-dk.ts
+++ b/src/loc/da-dk.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "No comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/de-de.ts b/src/loc/de-de.ts
index b6fa5c679..05d433d99 100644
--- a/src/loc/de-de.ts
+++ b/src/loc/de-de.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "No comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/el-gr.ts b/src/loc/el-gr.ts
index 690ff9604..6d3b3ce8a 100644
--- a/src/loc/el-gr.ts
+++ b/src/loc/el-gr.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "No comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/en-us.ts b/src/loc/en-us.ts
index 2cba70cbf..7d1055ac2 100644
--- a/src/loc/en-us.ts
+++ b/src/loc/en-us.ts
@@ -2,6 +2,8 @@ declare var define: any;
define([], () => {
return {
+
+
"Save": "Save",
"Cancel": "Cancel",
@@ -380,8 +382,11 @@ define([], () => {
DynamicFormTermPanelTitle: "Select Term",
DynamicFormEnterURLPlaceholder: "Enter a URL",
DynamicFormEnterDescriptionPlaceholder: "Alternative text",
-
customDisplayName: "Use this location:",
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
OrgAssetsLinkLabel: "Your organisation"
};
});
diff --git a/src/loc/es-es.ts b/src/loc/es-es.ts
index c8fa58fd8..23d4b34c5 100644
--- a/src/loc/es-es.ts
+++ b/src/loc/es-es.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/et-ee.ts b/src/loc/et-ee.ts
index 9d8b950f9..702d3bbc4 100644
--- a/src/loc/et-ee.ts
+++ b/src/loc/et-ee.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/eu-es.ts b/src/loc/eu-es.ts
index b5078a9e5..a0af3969c 100644
--- a/src/loc/eu-es.ts
+++ b/src/loc/eu-es.ts
@@ -253,6 +253,10 @@ define([], () => {
LinkFileInstructions: "Paste a link to a file in OneDrive for Business or SharePoint Online",
LinkHeader: "From a link",
LinkImageInstructions: "Paste a link to an image in OneDrive for Business or SharePoint Online",
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
ListLayoutAriaLabel: "View options. {0} {1} .",
ListLayoutCompact: "Compact view",
ListLayoutCompactDescription: "View items and details in a compact list",
diff --git a/src/loc/fi-fi.ts b/src/loc/fi-fi.ts
index 726a2ca57..478c98eec 100644
--- a/src/loc/fi-fi.ts
+++ b/src/loc/fi-fi.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/fr-ca.ts b/src/loc/fr-ca.ts
index 7f79fe882..efef030f6 100644
--- a/src/loc/fr-ca.ts
+++ b/src/loc/fr-ca.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/fr-fr.ts b/src/loc/fr-fr.ts
index 7f79fe882..efef030f6 100644
--- a/src/loc/fr-fr.ts
+++ b/src/loc/fr-fr.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/it-it.ts b/src/loc/it-it.ts
index e835aae26..7f80c8f29 100644
--- a/src/loc/it-it.ts
+++ b/src/loc/it-it.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/ja-jp.ts b/src/loc/ja-jp.ts
index 53ae4fac4..0ca8ff3f3 100644
--- a/src/loc/ja-jp.ts
+++ b/src/loc/ja-jp.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/lt-lt.ts b/src/loc/lt-lt.ts
index 127b0fc47..2d9d5bbcb 100644
--- a/src/loc/lt-lt.ts
+++ b/src/loc/lt-lt.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/lv-lv.ts b/src/loc/lv-lv.ts
index a3f5b8bf4..4f83bd1b0 100644
--- a/src/loc/lv-lv.ts
+++ b/src/loc/lv-lv.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/mystrings.d.ts b/src/loc/mystrings.d.ts
index c13e16e54..b62a39f58 100644
--- a/src/loc/mystrings.d.ts
+++ b/src/loc/mystrings.d.ts
@@ -1,4 +1,9 @@
declare interface IControlStrings {
+ ListItemCommentsLabel: string;
+ ListItemCommentsNoCommentsLabel: string;
+ ListItemCommentDIalogDeleteSubText: string;
+ ListItemCommentsDialogDeleteTitle: string;
+
MyTeamsMessageError:string;
MyTeamsNoTeamsMessage:string;
MyTeamsLoadingMessage:string;
diff --git a/src/loc/nb-no.ts b/src/loc/nb-no.ts
index a54b424dc..dc7e71558 100644
--- a/src/loc/nb-no.ts
+++ b/src/loc/nb-no.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/nl-nl.ts b/src/loc/nl-nl.ts
index 63421b18a..5633c3529 100644
--- a/src/loc/nl-nl.ts
+++ b/src/loc/nl-nl.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/pl-pl.ts b/src/loc/pl-pl.ts
index c656a78cb..5e2485490 100644
--- a/src/loc/pl-pl.ts
+++ b/src/loc/pl-pl.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/pt-pt.ts b/src/loc/pt-pt.ts
index 2e773a560..b2d55e2d5 100644
--- a/src/loc/pt-pt.ts
+++ b/src/loc/pt-pt.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Tem a certeza que quer eliminar este comentário?",
+ ListItemCommentsDialogDeleteTitle: "Confirmar Eliminar Comentário",
+ ListItemCommentsLabel: "Comentários",
+ ListItemCommentsNoCommentsLabel: "Sem Comentários",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
MyTeamsNoTeamsMessage: "Neste momento não tens nenhum Team",
diff --git a/src/loc/ro-ro.ts b/src/loc/ro-ro.ts
index edd5c5976..2e4ad30a6 100644
--- a/src/loc/ro-ro.ts
+++ b/src/loc/ro-ro.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/ru-ru.ts b/src/loc/ru-ru.ts
index 850346c37..3c8db88bc 100644
--- a/src/loc/ru-ru.ts
+++ b/src/loc/ru-ru.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/sk-sk.ts b/src/loc/sk-sk.ts
index fd9181457..f1efcc483 100644
--- a/src/loc/sk-sk.ts
+++ b/src/loc/sk-sk.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/sr-latn-rs.ts b/src/loc/sr-latn-rs.ts
index e0924bf6a..356857a4c 100644
--- a/src/loc/sr-latn-rs.ts
+++ b/src/loc/sr-latn-rs.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/sv-se.ts b/src/loc/sv-se.ts
index 0ea7a7dfe..2432de18b 100644
--- a/src/loc/sv-se.ts
+++ b/src/loc/sv-se.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/tr-tr.ts b/src/loc/tr-tr.ts
index 5b47cd3ec..6dde89a0e 100644
--- a/src/loc/tr-tr.ts
+++ b/src/loc/tr-tr.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/vi-vn.ts b/src/loc/vi-vn.ts
index c71bbbb21..f543479f2 100644
--- a/src/loc/vi-vn.ts
+++ b/src/loc/vi-vn.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/zh-cn.ts b/src/loc/zh-cn.ts
index 11354e9b4..478716630 100644
--- a/src/loc/zh-cn.ts
+++ b/src/loc/zh-cn.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/loc/zh-tw.ts b/src/loc/zh-tw.ts
index d08d79928..0624dae45 100644
--- a/src/loc/zh-tw.ts
+++ b/src/loc/zh-tw.ts
@@ -2,6 +2,10 @@ declare var define: any;
define([], () => {
return {
+ ListItemCommentDIalogDeleteSubText: "Are you sure that you want to delete this comment?",
+ ListItemCommentsDialogDeleteTitle: "Confirm Delete Comment",
+ ListItemCommentsLabel: "Comments",
+ ListItemCommentsNoCommentsLabel: "There is no Comments",
MyTeamsLoadingMessage: "loading your teams",
MyTeamsMessageDontHaveTeams: "You don't have any teams",
MyTeamsMessageError: "Something went wrong while loading your teams, please try later or refresh browser",
diff --git a/src/webparts/controlsTest/ControlsTestWebPart.ts b/src/webparts/controlsTest/ControlsTestWebPart.ts
index 4741f51b4..148cca944 100644
--- a/src/webparts/controlsTest/ControlsTestWebPart.ts
+++ b/src/webparts/controlsTest/ControlsTestWebPart.ts
@@ -10,7 +10,7 @@ import {
} from '@microsoft/sp-property-pane';
import * as strings from 'ControlsTestWebPartStrings';
-import ControlsTest from './components/ControlsTest';
+ import { TestControl, ITestControlProps } from './components/TestControl';
import { IControlsTestProps } from './components/IControlsTestProps';
import { IControlsTestWebPartProps } from './IControlsTestWebPartProps';
import {
@@ -18,6 +18,7 @@ import {
ThemeChangedEventArgs,
ThemeProvider,
} from "@microsoft/sp-component-base";
+import ControlsTest from './components/ControlsTest';
/**
* Web part to test the React controls
*/
@@ -66,11 +67,18 @@ export default class ControlsTestWebPart extends BaseClientSideWebPart = React.createElement(
+ TestControl,
+ {
+ context: this.context,
+ }
+ ); */
- public render(): void {
- const element: React.ReactElement = React.createElement(
- ControlsTest,
+ const element: React.ReactElement = React.createElement(
+
+ ControlsTest,
{
themeVariant: this._themeVariant,
diff --git a/src/webparts/controlsTest/components/TestControl.tsx b/src/webparts/controlsTest/components/TestControl.tsx
new file mode 100644
index 000000000..5cc4735e2
--- /dev/null
+++ b/src/webparts/controlsTest/components/TestControl.tsx
@@ -0,0 +1,25 @@
+import { WebPartContext } from "@microsoft/sp-webpart-base";
+import { Stack } from "@fluentui/react/lib/Stack";
+import * as React from "react";
+import { ListItemComments } from "../../../controls/listItemComments";
+export interface ITestControlProps {
+ context: WebPartContext;
+}
+
+export const TestControl: React.FunctionComponent = (
+ props: React.PropsWithChildren
+) => {
+ return (
+ <>
+
+
+
+ >
+ );
+};