diff --git a/cypress/e2e/item/bookmarks/bookmarks.cy.ts b/cypress/e2e/item/bookmarks/bookmarks.cy.ts
index 95bd4a796..e84f24797 100644
--- a/cypress/e2e/item/bookmarks/bookmarks.cy.ts
+++ b/cypress/e2e/item/bookmarks/bookmarks.cy.ts
@@ -1,4 +1,6 @@
import {
+ GuestFactory,
+ ItemLoginSchemaFactory,
PackedFolderItemFactory,
PackedItemBookmarkFactory,
} from '@graasp/sdk';
@@ -14,6 +16,7 @@ import {
BOOKMARK_ICON_SELECTOR,
CREATE_ITEM_BUTTON_ID,
ITEM_SEARCH_INPUT_ID,
+ PREVENT_GUEST_MESSAGE_ID,
SORTING_ORDERING_SELECTOR_ASC,
SORTING_ORDERING_SELECTOR_DESC,
SORTING_SELECT_SELECTOR,
@@ -38,6 +41,16 @@ const addToBookmark = (itemId: string) => {
};
describe('Bookmarked Item', () => {
+ it('Show message for guest', () => {
+ const item = PackedFolderItemFactory();
+ const guest = GuestFactory({
+ itemLoginSchema: ItemLoginSchemaFactory({ item }),
+ });
+ cy.setUpApi({ items: [item], currentMember: guest });
+ cy.visit(BOOKMARKED_ITEMS_PATH);
+ cy.get(`#${PREVENT_GUEST_MESSAGE_ID}`).should('be.visible');
+ });
+
describe('Member has no bookmarked items', () => {
beforeEach(() => {
cy.setUpApi({
diff --git a/cypress/e2e/item/home/home.cy.ts b/cypress/e2e/item/home/home.cy.ts
index 34911fcef..2a555f409 100644
--- a/cypress/e2e/item/home/home.cy.ts
+++ b/cypress/e2e/item/home/home.cy.ts
@@ -1,4 +1,6 @@
import {
+ GuestFactory,
+ ItemLoginSchemaFactory,
PackedFolderItemFactory,
PackedLocalFileItemFactory,
} from '@graasp/sdk';
@@ -14,6 +16,7 @@ import {
DROPZONE_SELECTOR,
HOME_LOAD_MORE_BUTTON_SELECTOR,
ITEM_SEARCH_INPUT_ID,
+ PREVENT_GUEST_MESSAGE_ID,
SORTING_ORDERING_SELECTOR_ASC,
SORTING_ORDERING_SELECTOR_DESC,
SORTING_SELECT_SELECTOR,
@@ -211,4 +214,15 @@ describe('Home', () => {
});
});
});
+
+ it('Show message for guest', () => {
+ const item = PackedFolderItemFactory();
+ const guest = GuestFactory({
+ itemLoginSchema: ItemLoginSchemaFactory({ item }),
+ });
+ cy.setUpApi({ items: [item], currentMember: guest });
+ cy.visit(HOME_PATH);
+
+ cy.get(`#${PREVENT_GUEST_MESSAGE_ID}`).should('be.visible');
+ });
});
diff --git a/cypress/e2e/item/publish/viewPublished.cy.ts b/cypress/e2e/item/publish/viewPublished.cy.ts
index fd8528fe4..abb5c7250 100644
--- a/cypress/e2e/item/publish/viewPublished.cy.ts
+++ b/cypress/e2e/item/publish/viewPublished.cy.ts
@@ -1,4 +1,8 @@
-import { PackedFolderItemFactory } from '@graasp/sdk';
+import {
+ GuestFactory,
+ ItemLoginSchemaFactory,
+ PackedFolderItemFactory,
+} from '@graasp/sdk';
import { SortingOptions } from '@/components/table/types';
import { BUILDER } from '@/langs/constants';
@@ -8,6 +12,7 @@ import { PUBLISHED_ITEMS_PATH } from '../../../../src/config/paths';
import {
CREATE_ITEM_BUTTON_ID,
ITEM_SEARCH_INPUT_ID,
+ PREVENT_GUEST_MESSAGE_ID,
PUBLISHED_ITEMS_ERROR_ALERT_ID,
PUBLISHED_ITEMS_ID,
SORTING_ORDERING_SELECTOR_ASC,
@@ -26,6 +31,16 @@ const items = [
const publishedItemData = items.map(({ published }) => published);
describe('Published Items', () => {
+ it('Show message for guest', () => {
+ const item = PackedFolderItemFactory();
+ const guest = GuestFactory({
+ itemLoginSchema: ItemLoginSchemaFactory({ item }),
+ });
+ cy.setUpApi({ items: [item], currentMember: guest });
+ cy.visit(PUBLISHED_ITEMS_PATH);
+ cy.get(`#${PREVENT_GUEST_MESSAGE_ID}`).should('be.visible');
+ });
+
describe('Member has no published items', () => {
it('Show empty table', () => {
cy.setUpApi({
diff --git a/cypress/e2e/item/trash/viewTrash.cy.ts b/cypress/e2e/item/trash/viewTrash.cy.ts
index 3f2884f07..9ae82072f 100644
--- a/cypress/e2e/item/trash/viewTrash.cy.ts
+++ b/cypress/e2e/item/trash/viewTrash.cy.ts
@@ -1,4 +1,9 @@
-import { PackedRecycledItemDataFactory } from '@graasp/sdk';
+import {
+ GuestFactory,
+ ItemLoginSchemaFactory,
+ PackedFolderItemFactory,
+ PackedRecycledItemDataFactory,
+} from '@graasp/sdk';
import { SortingOptions } from '@/components/table/types';
import { BUILDER } from '@/langs/constants';
@@ -8,6 +13,7 @@ import { RECYCLE_BIN_PATH } from '../../../../src/config/paths';
import {
CREATE_ITEM_BUTTON_ID,
ITEM_SEARCH_INPUT_ID,
+ PREVENT_GUEST_MESSAGE_ID,
RECYCLED_ITEMS_ERROR_ALERT_ID,
RECYCLED_ITEMS_ROOT_CONTAINER,
SORTING_ORDERING_SELECTOR_ASC,
@@ -24,6 +30,16 @@ const recycledItemData = [
];
describe('View trash', () => {
+ it('Show message for guest', () => {
+ const item = PackedFolderItemFactory();
+ const guest = GuestFactory({
+ itemLoginSchema: ItemLoginSchemaFactory({ item }),
+ });
+ cy.setUpApi({ items: [item], currentMember: guest });
+ cy.visit(RECYCLE_BIN_PATH);
+ cy.get(`#${PREVENT_GUEST_MESSAGE_ID}`).should('be.visible');
+ });
+
describe('Member has no recycled items', () => {
it('Show empty table', () => {
cy.setUpApi({
diff --git a/cypress/e2e/item/view/viewFolder.cy.ts b/cypress/e2e/item/view/viewFolder.cy.ts
index 2a261213e..1a45ce200 100644
--- a/cypress/e2e/item/view/viewFolder.cy.ts
+++ b/cypress/e2e/item/view/viewFolder.cy.ts
@@ -1,10 +1,19 @@
-import { PackedFolderItemFactory } from '@graasp/sdk';
+import {
+ GuestFactory,
+ ItemLoginSchemaFactory,
+ PackedFolderItemFactory,
+ PermissionLevel,
+} from '@graasp/sdk';
import { SortingOptionsForFolder } from '../../../../src/components/table/types';
import i18n from '../../../../src/config/i18n';
import { buildItemPath } from '../../../../src/config/paths';
import {
CREATE_ITEM_BUTTON_ID,
+ ITEM_HEADER_ID,
+ ITEM_MENU_BOOKMARK_BUTTON_CLASS,
+ ITEM_MENU_FLAG_BUTTON_CLASS,
+ ITEM_MENU_SHORTCUT_BUTTON_CLASS,
ITEM_SEARCH_INPUT_ID,
NAVIGATION_HOME_ID,
SORTING_ORDERING_SELECTOR_ASC,
@@ -28,8 +37,63 @@ const child4 = PackedFolderItemFactory({ parentItem });
const children = [child1, child2, child3, child4];
const items = [parentItem, item1, ...children];
+describe('View folder as guest', () => {
+ it('Show limited features', () => {
+ const item = PackedFolderItemFactory(
+ {},
+ { permission: PermissionLevel.Read },
+ );
+ const guest = GuestFactory({
+ itemLoginSchema: ItemLoginSchemaFactory({
+ item,
+ }),
+ });
+ cy.setUpApi({
+ items: [item],
+ currentMember: guest,
+ });
+ cy.visit(buildItemPath(item.id));
+
+ // no add button
+ cy.get(`#${CREATE_ITEM_BUTTON_ID}`).should('not.exist');
+
+ // menu item only contains flag
+ cy.get(`#${ITEM_HEADER_ID} [data-testid="MoreVertIcon"]`).click();
+ cy.get(`.${ITEM_MENU_FLAG_BUTTON_CLASS}`).should('be.visible');
+ cy.get(`.${ITEM_MENU_SHORTCUT_BUTTON_CLASS}`).should('not.exist');
+ });
+});
+
+describe('View folder as reader', () => {
+ it('Show limited features', () => {
+ const item = PackedFolderItemFactory(
+ {},
+ { permission: PermissionLevel.Read },
+ );
+ cy.setUpApi({
+ items: [item],
+ });
+ cy.visit(buildItemPath(item.id));
+
+ // no add button
+ cy.get(`#${CREATE_ITEM_BUTTON_ID}`).should('not.exist');
+
+ // menu item contains flag, duplicate, shortcut, bookmark
+ cy.get(`#${ITEM_HEADER_ID} [data-testid="MoreVertIcon"]`).click();
+ cy.get(`.${ITEM_MENU_FLAG_BUTTON_CLASS}`).should('be.visible');
+ cy.get(`.${ITEM_MENU_SHORTCUT_BUTTON_CLASS}`).should('be.visible');
+ cy.get(`.${ITEM_MENU_BOOKMARK_BUTTON_CLASS}`).should('be.visible');
+ });
+});
+
+describe('view Folder as admin', () => {
+ beforeEach(() => {
+ cy.setUpApi({
+ items,
+ });
+ i18n.changeLanguage(CURRENT_USER.extra.lang as string);
+ });
-describe('View Folder', () => {
it('View folder on map by default', () => {
cy.setUpApi({
items,
@@ -52,13 +116,6 @@ describe('View Folder', () => {
cy.get(`#${CREATE_ITEM_BUTTON_ID}`).should('be.visible');
});
- beforeEach(() => {
- cy.setUpApi({
- items,
- });
- i18n.changeLanguage(CURRENT_USER.extra.lang as string);
- });
-
it('visit item by id', () => {
const { id } = parentItem;
cy.visit(buildItemPath(id, { mode: ItemLayoutMode.Grid }));
diff --git a/package.json b/package.json
index e70ee3222..a2d12fd56 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,11 @@
"@emotion/styled": "11.13.0",
"@graasp/chatbox": "3.3.0",
"@graasp/map": "1.18.0",
- "@graasp/query-client": "3.22.3",
+ "@graasp/query-client": "3.22.4",
"@graasp/sdk": "4.29.1",
"@graasp/stylis-plugin-rtl": "2.2.0",
"@graasp/translations": "1.37.0",
- "@graasp/ui": "5.0.1",
+ "@graasp/ui": "5.1.0",
"@mui/icons-material": "5.16.4",
"@mui/lab": "5.0.0-alpha.172",
"@mui/material": "5.16.4",
diff --git a/src/components/App.tsx b/src/components/App.tsx
index 17ee47c72..5ce466170 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -1,9 +1,17 @@
+import { Trans } from 'react-i18next';
import { Outlet, Route, Routes, useLocation } from 'react-router-dom';
import { buildSignInPath, saveUrlForRedirection } from '@graasp/sdk';
-import { CustomInitialLoader, withAuthorization } from '@graasp/ui';
+import {
+ CustomInitialLoader,
+ PreventGuestWrapper,
+ SignedInWrapper,
+} from '@graasp/ui';
import { DOMAIN, GRAASP_AUTH_HOST } from '@/config/env';
+import { useBuilderTranslation } from '@/config/i18n';
+import { PREVENT_GUEST_MESSAGE_ID } from '@/config/selectors';
+import { BUILDER } from '@/langs/constants';
import {
BOOKMARKED_ITEMS_PATH,
@@ -18,7 +26,7 @@ import {
REDIRECT_PATH,
buildItemPath,
} from '../config/paths';
-import { hooks } from '../config/queryClient';
+import { hooks, mutations } from '../config/queryClient';
import Main from './main/Main';
import Redirect from './main/Redirect';
import BookmarkedItemsScreen from './pages/BookmarkedItemsScreen';
@@ -26,6 +34,7 @@ import MapItemsScreen from './pages/MapItemsScreen';
import PublishedItemsScreen from './pages/PublishedItemsScreen';
import RecycledItemsScreen from './pages/RecycledItemsScreen';
import HomeScreen from './pages/home/HomeScreen';
+import ItemLoginWrapper from './pages/item/ItemLoginWrapper';
import ItemPageLayout from './pages/item/ItemPageLayout';
import ItemScreen from './pages/item/ItemScreen';
import ItemScreenLayout from './pages/item/ItemScreenLayout';
@@ -36,44 +45,18 @@ import LibrarySettingsPage from './pages/item/LibrarySettingsPage';
const { useItemFeedbackUpdates, useCurrentMember } = hooks;
const App = (): JSX.Element => {
+ const { t: translateBuilder } = useBuilderTranslation();
+ const { mutate: signOut } = mutations.useSignOut();
const { pathname } = useLocation();
- const { data: currentMember, isLoading } = useCurrentMember();
+ const { data: currentAccount, isLoading } = useCurrentMember();
// registers the item updates through websockets
- useItemFeedbackUpdates?.(currentMember?.id);
+ useItemFeedbackUpdates?.(currentAccount?.id);
if (isLoading) {
return ;
}
- const withAuthorizationProps = {
- currentMember,
- redirectionLink: buildSignInPath({
- host: GRAASP_AUTH_HOST,
- redirectionUrl: window.location.toString(),
- }),
- onRedirect: () => {
- // save current url for later redirection after sign in
- saveUrlForRedirection(pathname, DOMAIN);
- },
- };
- const HomeWithAuthorization = withAuthorization(
- HomeScreen,
- withAuthorizationProps,
- );
- const FavoriteWithAuthorization = withAuthorization(
- BookmarkedItemsScreen,
- withAuthorizationProps,
- );
- const PublishedWithAuthorization = withAuthorization(
- PublishedItemsScreen,
- withAuthorizationProps,
- );
- const RecycleWithAuthorization = withAuthorization(
- RecycledItemsScreen,
- withAuthorizationProps,
- );
-
return (
} />
@@ -84,16 +67,64 @@ const App = (): JSX.Element => {
}
>
- } />
+ {/* pages with personal info */}
}
- />
+ element={
+ // redirect to sign in if not signed in
+ {
+ // save current url for later redirection after sign in
+ saveUrlForRedirection(pathname, DOMAIN);
+ }}
+ >
+ signOut()}
+ errorText={translateBuilder(BUILDER.ERROR_MESSAGE)}
+ text={
+ }}
+ />
+ }
+ >
+
+
+
+ }
+ >
+ } />
+ }
+ />
+ }
+ />
+ } />
+
+
+ {/* item pages - can be public */}
}
- />
- }>
+ path={buildItemPath()}
+ element={
+
+
+
+ }
+ >
} />
}>
} />
@@ -101,9 +132,9 @@ const App = (): JSX.Element => {
} />
- } />
- } />
- } />
+
+ {/* redirection to home */}
+ } />
} />
} />
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index c74dabd0c..6d8695271 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -10,6 +10,7 @@ import { ToastContainer } from 'react-toastify';
import { CssBaseline } from '@mui/material';
+import { AccountType } from '@graasp/sdk';
import { langs } from '@graasp/translations';
import { ThemeProvider } from '@graasp/ui';
@@ -36,7 +37,11 @@ const ThemeWrapper = () => {
langs={langs}
languageSelectSx={{ mb: 2, mr: 2 }}
i18n={i18nConfig}
- defaultDirection={i18nConfig.dir(currentMember?.extra?.lang)}
+ defaultDirection={i18nConfig.dir(
+ currentMember?.type === AccountType.Individual
+ ? currentMember?.extra?.lang
+ : 'ltr',
+ )}
>
diff --git a/src/components/common/BookmarkButton.tsx b/src/components/common/BookmarkButton.tsx
index 1f7e70981..aa0a80504 100644
--- a/src/components/common/BookmarkButton.tsx
+++ b/src/components/common/BookmarkButton.tsx
@@ -15,6 +15,7 @@ type Props = {
type?: ActionButtonVariant;
onClick?: () => void;
size?: IconButtonProps['size'];
+ className?: string;
};
const isItemBookmarked = (
@@ -27,17 +28,13 @@ const BookmarkButton = ({
size,
type,
onClick,
+ className,
}: Props): JSX.Element | null => {
- const { data: member } = hooks.useCurrentMember();
const { data: bookmarks } = hooks.useBookmarkedItems();
const { t: translateBuilder } = useBuilderTranslation();
const addFavorite = mutations.useAddBookmarkedItem();
const deleteFavorite = mutations.useRemoveBookmarkedItem();
- if (!member) {
- return null;
- }
-
const isFavorite = isItemBookmarked(item, bookmarks);
const handleFavorite = () => {
@@ -56,6 +53,7 @@ const BookmarkButton = ({
return (
{
const { t: translateBuilder } = useBuilderTranslation();
const { mutateAsync: signOut } = mutations.useSignOut();
- const renderAvatar = (m?: CompleteMember | null) => (
-
- );
-
const redirectPath = buildSignInPath({
host: GRAASP_AUTH_HOST,
redirectionUrl: window.location.toString(),
@@ -52,7 +48,7 @@ const UserSwitchWrapper = ({ ButtonContent }: Props): JSX.Element => {
signOutMenuItemId={HEADER_MEMBER_MENU_SIGN_OUT_BUTTON_ID}
seeProfileButtonId={HEADER_MEMBER_MENU_SEE_PROFILE_BUTTON_ID}
buildMemberMenuItemId={buildMemberMenuItemId}
- renderAvatar={renderAvatar}
+ avatar={}
/>
);
};
diff --git a/src/components/context/CurrentUserContext.tsx b/src/components/context/CurrentUserContext.tsx
index dc46a742d..5ccbf83f9 100644
--- a/src/components/context/CurrentUserContext.tsx
+++ b/src/components/context/CurrentUserContext.tsx
@@ -1,5 +1,8 @@
import { ReactNode, createContext, useContext, useEffect } from 'react';
+import { AccountType } from '@graasp/sdk';
+import { DEFAULT_LANG } from '@graasp/translations';
+
import i18n from '../../config/i18n';
import { hooks } from '../../config/queryClient';
@@ -19,12 +22,15 @@ export const CurrentUserContextProvider = ({ children }: Props): ReactNode => {
const { data } = useCurrentMember();
// update language depending on user setting
- const lang = data?.extra?.lang;
useEffect(() => {
+ const lang =
+ data?.type === AccountType.Individual
+ ? data?.extra?.lang || DEFAULT_LANG
+ : DEFAULT_LANG;
if (lang !== i18n.language) {
i18n.changeLanguage(lang);
}
- }, [lang]);
+ }, [data]);
return children;
};
diff --git a/src/components/item/FolderContent.tsx b/src/components/item/FolderContent.tsx
index a50ffc5e6..d0fa2a453 100644
--- a/src/components/item/FolderContent.tsx
+++ b/src/components/item/FolderContent.tsx
@@ -1,3 +1,5 @@
+import { useOutletContext } from 'react-router-dom';
+
import { Alert, Box, Stack, Typography } from '@mui/material';
import {
@@ -30,6 +32,7 @@ import {
import { useDragSelection } from '../main/list/useDragSelection';
import { DesktopMap } from '../map/DesktopMap';
import NoItemFilters from '../pages/NoItemFilters';
+import { OutletType } from '../pages/item/type';
import SortingSelect from '../table/SortingSelect';
import { SortingOptionsForFolder } from '../table/types';
import { useSorting } from '../table/useSorting';
@@ -43,19 +46,22 @@ type Props = {
searchText: string;
items?: PackedItem[];
sortBy: SortingOptionsForFolder;
+ canWrite?: boolean;
};
-const Content = ({ item, searchText, items, sortBy }: Props) => {
+const Content = ({
+ item,
+ searchText,
+ items,
+ sortBy,
+ canWrite = false,
+}: Props) => {
const { mode } = useLayoutContext();
const { itemTypes } = useFilterItemsContext();
const { selectedIds, clearSelection, toggleSelection } =
useSelectionContext();
const DragSelection = useDragSelection();
- const enableEditing = item.permission
- ? PermissionLevelCompare.lte(PermissionLevel.Write, item.permission)
- : false;
-
if (mode === ItemLayoutMode.Map) {
return (
@@ -75,7 +81,7 @@ const Content = ({ item, searchText, items, sortBy }: Props) => {
onCardClick={toggleSelection}
onMove={clearSelection}
/>
- {Boolean(enableEditing && !searchText && !itemTypes?.length) && (
+ {Boolean(canWrite && !searchText && !itemTypes?.length) && (
{
const { t: translateBuilder } = useBuilderTranslation();
const { selectedIds } = useSelectionContext();
const itemSearch = useItemSearch();
+ const { canWrite } = useOutletContext();
const {
data: children,
@@ -136,13 +143,13 @@ const FolderContent = ({ item }: { item: PackedItem }): JSX.Element => {
ordering: Ordering.ASC,
});
- const sortedChildren = children?.sort(sortFn);
-
const sortingOptions = Object.values(SortingOptionsForFolder).sort((t1, t2) =>
translateEnums(t1).localeCompare(translateEnums(t2)),
);
if (children) {
+ const sortedChildren = children.sort(sortFn);
+
return (
<>
@@ -160,49 +167,62 @@ const FolderContent = ({ item }: { item: PackedItem }): JSX.Element => {
spacing={1}
>
{itemSearch.input}
-
+ {canWrite && (
+
+ )}
-
- {selectedIds.length && sortedChildren?.length ? (
-
- ) : (
-
-
-
- {sortBy && setSortBy && (
-
- )}
-
+ {sortedChildren.length ? (
+
+ {selectedIds.length ? (
+
+ ) : (
+
+
+
+ {sortBy && setSortBy && (
+
+ )}
+
+
-
- )}
-
+ )}
+
+ ) : null}
+
+ {/* reader empty message */}
+ {!sortedChildren.length && !canWrite ? (
+
+ {translateBuilder(BUILDER.EMPTY_FOLDER_MESSAGE)}
+
+ ) : null}
+
(
(
diff --git a/src/components/item/header/Actions.tsx b/src/components/item/header/Actions.tsx
index a0ce8406a..1a243df68 100644
--- a/src/components/item/header/Actions.tsx
+++ b/src/components/item/header/Actions.tsx
@@ -4,6 +4,7 @@ import { MoreVert } from '@mui/icons-material';
import { Divider, IconButton, Menu } from '@mui/material';
import {
+ AccountType,
ItemType,
PackedItem,
PermissionLevel,
@@ -13,6 +14,7 @@ import { ActionButton } from '@graasp/ui';
import useModalStatus from '@/components/hooks/useModalStatus';
import { hooks } from '@/config/queryClient';
+import { ITEM_MENU_BOOKMARK_BUTTON_CLASS } from '@/config/selectors';
import BookmarkButton from '../../common/BookmarkButton';
import CollapseButton from '../../common/CollapseButton';
@@ -77,34 +79,47 @@ const Actions = ({ item }: Props): JSX.Element | null => {
diff --git a/src/components/pages/home/HomeScreen.tsx b/src/components/pages/home/HomeScreen.tsx
index cce7a4c8d..64fd7b315 100644
--- a/src/components/pages/home/HomeScreen.tsx
+++ b/src/components/pages/home/HomeScreen.tsx
@@ -1,6 +1,14 @@
import { useState } from 'react';
-import { Alert, Box, LinearProgress, Stack } from '@mui/material';
+import {
+ Alert,
+ Box,
+ Container,
+ LinearProgress,
+ Button as MuiButton,
+ Stack,
+ Typography,
+} from '@mui/material';
import { Button } from '@graasp/ui';
@@ -197,28 +205,47 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
const HomeScreen = (): JSX.Element => {
const { t: translateBuilder } = useBuilderTranslation();
+ const { data: currentMember } = hooks.useCurrentMember();
const itemSearch = useItemSearch();
+ if (currentMember) {
+ return (
+
+ {itemSearch.input}
+
+
+ }
+ >
+
+
+
+
+ );
+ }
+
+ // not logged in - redirection
return (
-
- {itemSearch.input}
-
-
- }
- >
-
-
-
-
+
+
+
+
+ {translateBuilder(BUILDER.REDIRECTION_TEXT)}
+
+
+ {translateBuilder(BUILDER.REDIRECTION_BUTTON)}
+
+
+
+
);
};
diff --git a/src/components/pages/item/ItemLoginWrapper.tsx b/src/components/pages/item/ItemLoginWrapper.tsx
index f7a2f7cde..285b4b8ed 100644
--- a/src/components/pages/item/ItemLoginWrapper.tsx
+++ b/src/components/pages/item/ItemLoginWrapper.tsx
@@ -5,7 +5,6 @@ import { ItemLoginAuthorization } from '@graasp/ui';
import { hooks, mutations } from '@/config/queryClient';
import {
ITEM_LOGIN_SIGN_IN_BUTTON_ID,
- ITEM_LOGIN_SIGN_IN_MEMBER_ID_ID,
ITEM_LOGIN_SIGN_IN_PASSWORD_ID,
ITEM_LOGIN_SIGN_IN_USERNAME_ID,
} from '@/config/selectors';
@@ -14,29 +13,43 @@ import ItemForbiddenScreen from '../../main/list/ItemForbiddenScreen';
const { useItem, useCurrentMember, useItemLoginSchemaType } = hooks;
-const ItemLoginWrapper = (WrappedComponent: () => JSX.Element): JSX.Element => {
+const ItemLoginWrapper = ({
+ children,
+}: {
+ children: JSX.Element;
+}): JSX.Element => {
const { mutate: itemLoginSignIn } = mutations.usePostItemLogin();
+ const { data: currentAccount, isLoading: isCurrentAccountLoading } =
+ useCurrentMember();
const { itemId } = useParams();
+ const { data: item, isLoading: isItemLoading } = useItem(itemId);
+ const { data: itemLoginSchemaType, isLoading: isItemLoginSchemaTypeLoading } =
+ useItemLoginSchemaType({ itemId });
- const ForbiddenContent = ;
+ const forbiddenContent = ;
if (!itemId) {
- return ForbiddenContent;
+ return forbiddenContent;
}
- const AuthComponent = ItemLoginAuthorization({
- signIn: itemLoginSignIn,
- itemId,
- useCurrentMember,
- useItem,
- useItemLoginSchemaType,
- ForbiddenContent,
- memberIdInputId: ITEM_LOGIN_SIGN_IN_MEMBER_ID_ID,
- usernameInputId: ITEM_LOGIN_SIGN_IN_USERNAME_ID,
- signInButtonId: ITEM_LOGIN_SIGN_IN_BUTTON_ID,
- passwordInputId: ITEM_LOGIN_SIGN_IN_PASSWORD_ID,
- })(WrappedComponent);
- return ;
+ return (
+
+ {children}
+
+ );
};
export default ItemLoginWrapper;
diff --git a/src/components/pages/item/ItemPageLayout.tsx b/src/components/pages/item/ItemPageLayout.tsx
index cd237759b..0ec3f8504 100644
--- a/src/components/pages/item/ItemPageLayout.tsx
+++ b/src/components/pages/item/ItemPageLayout.tsx
@@ -14,7 +14,6 @@ import { buildItemPath } from '@/config/paths';
import ErrorAlert from '../../common/ErrorAlert';
import { useLayoutContext } from '../../context/LayoutContext';
-import WrappedAuthItemScreen from './ItemLoginWrapper';
import { OutletType } from './type';
const ItemPageLayout = (): JSX.Element => {
@@ -50,7 +49,4 @@ const ItemPageLayout = (): JSX.Element => {
return ;
};
-const WrappedItemScreen = (): JSX.Element =>
- WrappedAuthItemScreen(ItemPageLayout);
-
-export default WrappedItemScreen;
+export default ItemPageLayout;
diff --git a/src/components/pages/item/ItemScreenLayout.tsx b/src/components/pages/item/ItemScreenLayout.tsx
index cb2bf25b5..e1fe58297 100644
--- a/src/components/pages/item/ItemScreenLayout.tsx
+++ b/src/components/pages/item/ItemScreenLayout.tsx
@@ -7,7 +7,6 @@ import { Loader } from '@graasp/ui';
import { hooks } from '../../../config/queryClient';
import ErrorAlert from '../../common/ErrorAlert';
import { useLayoutContext } from '../../context/LayoutContext';
-import WrappedAuthItemScreen from './ItemLoginWrapper';
const { useItem } = hooks;
@@ -44,7 +43,4 @@ const ItemScreenLayout = (): JSX.Element => {
return ;
};
-const WrappedItemScreen = (): JSX.Element =>
- WrappedAuthItemScreen(ItemScreenLayout);
-
-export default WrappedItemScreen;
+export default ItemScreenLayout;
diff --git a/src/components/table/ItemActions.tsx b/src/components/table/ItemActions.tsx
index 644211bc3..8fde34324 100644
--- a/src/components/table/ItemActions.tsx
+++ b/src/components/table/ItemActions.tsx
@@ -1,6 +1,8 @@
import { Stack } from '@mui/material';
-import { DiscriminatedItem } from '@graasp/sdk';
+import { AccountType, DiscriminatedItem } from '@graasp/sdk';
+
+import { hooks } from '@/config/queryClient';
import BookmarkButton from '../common/BookmarkButton';
import DownloadButton from '../main/DownloadButton';
@@ -10,11 +12,17 @@ type Props = {
};
// items and memberships match by index
-const ItemActions = ({ data: item }: Props): JSX.Element => (
-
-
-
-
-);
+const ItemActions = ({ data: item }: Props): JSX.Element => {
+ const { data: currentMember } = hooks.useCurrentMember();
+
+ return (
+
+ {currentMember?.type === AccountType.Individual && (
+
+ )}
+
+
+ );
+};
export default ItemActions;
diff --git a/src/config/selectors.ts b/src/config/selectors.ts
index f37416687..d9eaf3cf8 100644
--- a/src/config/selectors.ts
+++ b/src/config/selectors.ts
@@ -71,7 +71,6 @@ export const ITEM_LOGIN_SIGN_IN_PASSWORD_ID = 'itemLoginSignInPassword';
export const ITEM_LOGIN_SIGN_IN_BUTTON_ID = 'itemLoginSignInButton';
export const ITEM_SCREEN_MAIN_ID = 'itemScreenMain';
export const ITEM_LOGIN_SCREEN_FORBIDDEN_ID = 'itemLoginScreenForbidden';
-export const ITEM_LOGIN_SIGN_IN_MEMBER_ID_ID = 'itemLoginSignInMemberId';
export const ITEM_LOGIN_SIGN_IN_MODE_ID = 'itemLoginSignInMode';
export const ITEM_MAIN_CLASS = 'itemMain';
export const HOME_ERROR_ALERT_ID = 'homeErrorAlert';
@@ -81,6 +80,7 @@ export const PUBLISHED_ITEMS_ERROR_ALERT_ID = 'publishedItemsErrorAlert';
export const RECYCLED_ITEMS_ERROR_ALERT_ID = 'recycledItemsErrorAlert';
export const ITEM_MENU_SHORTCUT_BUTTON_CLASS = 'itemMenuShortcutButton';
export const ITEM_MENU_DUPLICATE_BUTTON_CLASS = 'itemMenuDuplicateButton';
+export const ITEM_MENU_BOOKMARK_BUTTON_CLASS = 'itemMenuBookmarkButton';
export const ITEM_MENU_FAVORITE_BUTTON_CLASS = 'itemMenuFavoriteButton';
export const ITEM_MENU_FLAG_BUTTON_CLASS = 'itemMenuFlagButton';
export const buildFlagListItemId = (type: string): string =>
@@ -434,3 +434,4 @@ export const RECYCLE_BIN_RESTORE_MANY_ITEMS_BUTTON_ID =
export const COPY_MANY_ITEMS_BUTTON_SELECTOR = `.lucide-copy`;
export const MOVE_MANY_ITEMS_BUTTON_SELECTOR = `.lucide-move`;
export const DELETE_SINGLE_ITEM_BUTTON_SELECTOR = `.lucide-trash`;
+export const PREVENT_GUEST_MESSAGE_ID = 'preventGuestMessage';
diff --git a/src/langs/ar.json b/src/langs/ar.json
index 660802369..ef41c7402 100644
--- a/src/langs/ar.json
+++ b/src/langs/ar.json
@@ -70,7 +70,6 @@
"EDIT_ITEM_ERROR_MESSAGE": "العنصر غير صالح",
"EDIT_ITEM_MODAL_TITLE": "تعديل العنصر",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "نص بديل (لأغراض إمكانية الوصول - accessibility)",
- "EMPTY_ITEM_MESSAGE": "هذا العنصر فارغ.",
"ERROR_MESSAGE": "حدث خطأ.",
"BOOKMARKED_ITEM_ADD_TEXT": "اضافة الى قائمة المفضّلات",
"BOOKMARKED_ITEM_REMOVE_TEXT": "إزالة من قائمة المفضّلات",
diff --git a/src/langs/constants.ts b/src/langs/constants.ts
index d28b95b74..fdd1aa198 100644
--- a/src/langs/constants.ts
+++ b/src/langs/constants.ts
@@ -68,7 +68,7 @@ export const BUILDER = {
EDIT_ITEM_ERROR_MESSAGE: 'EDIT_ITEM_ERROR_MESSAGE',
EDIT_ITEM_MODAL_TITLE: 'EDIT_ITEM_MODAL_TITLE',
EDIT_ITEM_IMAGE_ALT_TEXT_LABEL: 'EDIT_ITEM_IMAGE_ALT_TEXT_LABEL',
- EMPTY_ITEM_MESSAGE: 'EMPTY_ITEM_MESSAGE',
+ EMPTY_FOLDER_MESSAGE: 'EMPTY_FOLDER_MESSAGE',
ERROR_MESSAGE: 'ERROR_MESSAGE',
BOOKMARKED_ITEM_ADD_TEXT: 'BOOKMARKED_ITEM_ADD_TEXT',
BOOKMARKED_ITEM_REMOVE_TEXT: 'BOOKMARKED_ITEM_REMOVE_TEXT',
@@ -570,4 +570,8 @@ export const BUILDER = {
MEMBER_VALIDATION_TITLE: 'MEMBER_VALIDATION_TITLE',
MEMBER_VALIDATION_DESCRIPTION: 'MEMBER_VALIDATION_DESCRIPTION',
MEMBER_VALIDATION_LINK_TEXT: 'MEMBER_VALIDATION_LINK_TEXT',
+ GUEST_LIMITATION_TEXT: 'GUEST_LIMITATION_TEXT',
+ GUEST_SIGN_OUT_BUTTON: 'GUEST_SIGN_OUT_BUTTON',
+ REDIRECTION_TEXT: 'REDIRECTION_TEXT',
+ REDIRECTION_BUTTON: 'REDIRECTION_BUTTON',
};
diff --git a/src/langs/de.json b/src/langs/de.json
index 5ebc239f1..26eb55add 100644
--- a/src/langs/de.json
+++ b/src/langs/de.json
@@ -72,7 +72,6 @@
"EDIT_ITEM_ERROR_MESSAGE": "Element ist ungültig",
"EDIT_ITEM_MODAL_TITLE": "Objekt bearbeiten",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "Alternativer Text (aus Gründen der Barrierefreiheit)",
- "EMPTY_ITEM_MESSAGE": "Dieser Artikel ist leer.",
"ERROR_MESSAGE": "Es ist ein Fehler aufgetreten.",
"BOOKMARKED_ITEM_ADD_TEXT": "Zu den Favoriten hinzufügen",
"BOOKMARKED_ITEM_REMOVE_TEXT": "Von Favoriten entfernen",
diff --git a/src/langs/en.json b/src/langs/en.json
index c34330627..37a19c0b8 100644
--- a/src/langs/en.json
+++ b/src/langs/en.json
@@ -72,7 +72,7 @@
"EDIT_ITEM_ERROR_MESSAGE": "Item is invalid",
"EDIT_ITEM_MODAL_TITLE": "Edit Item",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "Alternative text (for accessibility purposes)",
- "EMPTY_ITEM_MESSAGE": "This item is empty.",
+ "EMPTY_FOLDER_MESSAGE": "This folder is empty.",
"ERROR_MESSAGE": "An error occured.",
"BOOKMARKED_ITEM_ADD_TEXT": "Add to Bookmarks",
"BOOKMARKED_ITEM_REMOVE_TEXT": "Remove from Bookmarks",
@@ -473,5 +473,7 @@
"item.order": "Order",
"MEMBER_VALIDATION_TITLE": "Account email needs to be verified",
"MEMBER_VALIDATION_DESCRIPTION": "In order to use all features of Graasp you need to validate your account email. You will find more information in the link below.",
- "MEMBER_VALIDATION_LINK_TEXT": "Learn more"
+ "MEMBER_VALIDATION_LINK_TEXT": "Learn more",
+ "GUEST_LIMITATION_TEXT": "You are currently using Graasp with the guest account <1>{{name}}1>. In order to use all features of Graasp, you have to log out and create a Graasp account.",
+ "GUEST_SIGN_OUT_BUTTON": " Log out and Create a Graasp account"
}
diff --git a/src/langs/es.json b/src/langs/es.json
index 7369f94a3..5b79a16f9 100644
--- a/src/langs/es.json
+++ b/src/langs/es.json
@@ -71,7 +71,6 @@
"EDIT_ITEM_ERROR_MESSAGE": "El artículo no es válido",
"EDIT_ITEM_MODAL_TITLE": "Editar artículo",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "Texto alternativo (para fines de accesibilidad)",
- "EMPTY_ITEM_MESSAGE": "Este artículo está vacío.",
"ERROR_MESSAGE": "Ocurrió un error.",
"BOOKMARKED_ITEM_ADD_TEXT": "Agregar a los favoritos",
"BOOKMARKED_ITEM_REMOVE_TEXT": "Quitar de favoritos",
diff --git a/src/langs/fr.json b/src/langs/fr.json
index 64a24a188..f77130976 100644
--- a/src/langs/fr.json
+++ b/src/langs/fr.json
@@ -71,7 +71,7 @@
"EDIT_ITEM_ERROR_MESSAGE": "L'élément n'est pas valide",
"EDIT_ITEM_MODAL_TITLE": "Modifier l'élément",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "Texte alternatif (pour améliorer l'accessibilité)",
- "EMPTY_ITEM_MESSAGE": "Cet élément est vide.",
+ "EMPTY_FOLDER_MESSAGE": "Ce dossier est vide.",
"ERROR_MESSAGE": "Une erreur est survenue.",
"BOOKMARKED_ITEM_ADD_TEXT": "Ajouter aux signets",
"BOOKMARKED_ITEM_REMOVE_TEXT": "Retirer des signets",
diff --git a/src/langs/it.json b/src/langs/it.json
index ba603cf6e..1d6fb4e93 100644
--- a/src/langs/it.json
+++ b/src/langs/it.json
@@ -71,7 +71,6 @@
"EDIT_ITEM_ERROR_MESSAGE": "L'elemento non è valido",
"EDIT_ITEM_MODAL_TITLE": "Modifica elemento",
"EDIT_ITEM_IMAGE_ALT_TEXT_LABEL": "Testo alternativo (per accessibilità)",
- "EMPTY_ITEM_MESSAGE": "Questo elemento è vuoto.",
"ERROR_MESSAGE": "Si è verificato un errore.",
"BOOKMARKED_ITEM_ADD_TEXT": "Aggiungi ai preferiti",
"BOOKMARKED_ITEM_REMOVE_TEXT": "Rimuovi dai preferiti",
diff --git a/yarn.lock b/yarn.lock
index 5dc32201b..215e7734d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1672,19 +1672,19 @@ __metadata:
languageName: node
linkType: hard
-"@graasp/query-client@npm:3.22.3":
- version: 3.22.3
- resolution: "@graasp/query-client@npm:3.22.3"
+"@graasp/query-client@npm:3.22.4":
+ version: 3.22.4
+ resolution: "@graasp/query-client@npm:3.22.4"
dependencies:
"@tanstack/react-query": "npm:4.36.1"
"@tanstack/react-query-devtools": "npm:4.36.1"
- axios: "npm:1.7.5"
+ axios: "npm:1.7.7"
http-status-codes: "npm:2.3.0"
peerDependencies:
"@graasp/sdk": ^4.0.0
"@graasp/translations": "*"
react: ^18.0.0
- checksum: 10/a1cf0e884e77ca928f7b983fdde7e3b7ca55b23c93d6ec8d79cd3e4db7f675a2e153a5072a0085d8aa5fce977ef7e06de684f23d4e37737ca28ddfccf0914eb7
+ checksum: 10/e4c5958141a55aa41f5a68fc39b8cc3ac2bf123a47f8b7a4c8c05836274f962082227353ed1ad93c10ec1b25a10691eb29bcd72c31f38f7243bae2cdfd7fd250
languageName: node
linkType: hard
@@ -1723,9 +1723,9 @@ __metadata:
languageName: node
linkType: hard
-"@graasp/ui@npm:5.0.1":
- version: 5.0.1
- resolution: "@graasp/ui@npm:5.0.1"
+"@graasp/ui@npm:5.1.0":
+ version: 5.1.0
+ resolution: "@graasp/ui@npm:5.1.0"
dependencies:
http-status-codes: "npm:2.3.0"
interweave: "npm:13.1.0"
@@ -1754,7 +1754,7 @@ __metadata:
react-i18next: ^13.0.0 || ^14.0.0 || ^15.0.0
react-router-dom: ^6.11.0
stylis: ^4.1.3
- checksum: 10/2acc246b741493ea17178f37d1fb8cc6d3120a6fc90b46bbe5b911e9c3cd37d51d80546c981652ff7a59cccd26ea10f7499e543d0ba8722de5ee456e6c162186
+ checksum: 10/fcab9f3d021848e81f8779be9508a6e6efcae249a364611e4adf888410658d83dd1ecfe8b0fbcda26c3bd3f840b44e6efe73cd2e3f9af53917c4510499a3b37a
languageName: node
linkType: hard
@@ -3745,17 +3745,6 @@ __metadata:
languageName: node
linkType: hard
-"axios@npm:1.7.5":
- version: 1.7.5
- resolution: "axios@npm:1.7.5"
- dependencies:
- follow-redirects: "npm:^1.15.6"
- form-data: "npm:^4.0.0"
- proxy-from-env: "npm:^1.1.0"
- checksum: 10/6cbcfe943a84089f420a900a3a3aeb54ee94dcc9c2b81b150434896357be5d1079eff0b1bbb628597371e79f896b1bc5776df04184756ba99656ff31df9a75bf
- languageName: node
- linkType: hard
-
"axios@npm:1.7.7":
version: 1.7.7
resolution: "axios@npm:1.7.7"
@@ -6508,11 +6497,11 @@ __metadata:
"@emotion/styled": "npm:11.13.0"
"@graasp/chatbox": "npm:3.3.0"
"@graasp/map": "npm:1.18.0"
- "@graasp/query-client": "npm:3.22.3"
+ "@graasp/query-client": "npm:3.22.4"
"@graasp/sdk": "npm:4.29.1"
"@graasp/stylis-plugin-rtl": "npm:2.2.0"
"@graasp/translations": "npm:1.37.0"
- "@graasp/ui": "npm:5.0.1"
+ "@graasp/ui": "npm:5.1.0"
"@mui/icons-material": "npm:5.16.4"
"@mui/lab": "npm:5.0.0-alpha.172"
"@mui/material": "npm:5.16.4"