Skip to content

Commit

Permalink
fix: Fix Recent Spaces Loading - MEED-2951 - Meeds-io/meeds#1022 (#3212)
Browse files Browse the repository at this point in the history
Prior to this change, the recent spaces menu performances offers a bad
UX since it takes a lot of time and didn't display loading effect. This
change will add a loading effect and a placeholder when empty spaces are
retrieved even while searching. In addition, the backend services was
taking lot of time to compute unread activities per space. To fix the
performances issue, the query has been modified to make sure that the
unread activities retrieval is more performant. In addition, the eTag
computing has been improved to rely on REST input queries, cache time of
results and spaces list order. This will ensure to not having to compute
unread flags each time the last visited spaces list are updated.
  • Loading branch information
boubaker authored and exo-swf committed Nov 21, 2023
1 parent 57fcf18 commit 6a0948f
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,11 @@ public Space[] load(int offset, int limit) throws Exception, IllegalArgumentExce
Space storedSpace = spaceStorage.getSpaceById(space.getId());
storedSpace.setPendingUsers(space.getPendingUsers());
return storedSpace;
}).collect(Collectors.toList());
}).toList();
}
break;
}
return listSpaces.toArray(new Space[listSpaces.size()]);
return listSpaces == null ? new Space[0] : listSpaces.toArray(new Space[listSpaces.size()]);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@
+ " INNER JOIN SOC_METADATAS sm"
+ " ON item.metadata_id = sm.metadata_id "
+ " AND sm.type = :metadataType "
+ " AND sm.audience_id = :creatorId "
+ " WHERE item.space_id = :spaceId "
+ " WHERE item.creator_id = :creatorId"
+ " AND item.space_id = :spaceId"
+ " GROUP BY item.object_type"
)
@NamedNativeQuery(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ public boolean execute(ControllerContext controllerContext) throws Exception {
&& requestSiteName.startsWith(SPACES_GROUP_PREFIX)) {
Space space = spaceService.getSpaceByGroupId(requestSiteName);
if (space != null && canAccessSpace(remoteId, space)) {
spaceService.updateSpaceAccessed(remoteId, space);
HttpSession session = controllerContext.getRequest().getSession();
String lastAccessedSpaceId = (String) session.getAttribute(SpaceAccessType.ACCESSED_SPACE_ID_KEY);
if (!StringUtils.equals(lastAccessedSpaceId, space.getId())) {
spaceService.updateSpaceAccessed(remoteId, space);
}
cleanupSession(controllerContext);
session.setAttribute(SpaceAccessType.ACCESSED_SPACE_ID_KEY, space.getId());
} else {
processSpaceAccess(controllerContext, remoteId, space);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -943,16 +943,15 @@ public void updateSpaceAccessed(String remoteId, Space space) throws SpaceStorag
// we remove all cache entries for the given userId and for space type LATEST_ACCESSED
LastAccessedSpacesCacheSelector selector = new LastAccessedSpacesCacheSelector(remoteId, space, cacheService);
try {
// Update the storage only if the user has accessed a different space
if (selector.isUpdateStore()) {
super.updateSpaceAccessed(remoteId, space);
}
exoSpacesCache.select(selector);
} catch (Exception e) {
LOG.error("Error while removing cache entries for remoteId=" + remoteId + ", space=" + space.getDisplayName() +
" and type=" + SpaceType.LATEST_ACCESSED.name() + " or type=" + SpaceType.VISITED, e);
}

// Update the storage only if the user has accessed a different space
if (selector.isUpdateStore()) {
super.updateSpaceAccessed(remoteId, space);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ public class SpaceRestResourcesV1 implements SpaceRestResources {

private static final CacheControl CACHE_CONTROL = new CacheControl();

private static final CacheControl CACHE_REVALIDATE_CONTROL = new CacheControl();

private static final Date DEFAULT_IMAGES_LAST_MODIFED = new Date();

// 7 days
Expand Down Expand Up @@ -176,6 +178,8 @@ public SpaceRestResourcesV1(ActivityRestResourcesV1 activityRestResourcesV1,
this.securitySettingService = securitySettingService;

CACHE_CONTROL.setMaxAge(CACHE_IN_SECONDS);
CACHE_REVALIDATE_CONTROL.setMaxAge(CACHE_IN_SECONDS);
CACHE_REVALIDATE_CONTROL.setMustRevalidate(true);
}

/**
Expand Down Expand Up @@ -270,40 +274,56 @@ public Response getSpaces(@Context
return Response.status(400).entity("Unrecognized space filter type").build();
}

List<DataEntity> spaceInfos = new ArrayList<>();
List<Space> spaces;
if (limit > 0) {
for (Space space : listAccess.load(offset, limit)) {
spaces = Arrays.asList(listAccess.load(offset, limit));
} else {
spaces = Collections.emptyList();
}
String eTagValue = String.valueOf(Objects.hash(spaces.stream()
.map(Space::getCacheTime)
.reduce(Long::sum)
.orElse(0l),
Objects.hash(spaces.stream()
.map(Space::getId)
.map(Long::parseLong)
.toArray()),
spaceFilter,
filterType,
offset,
limit,
returnSize,
expand,
authenticatedUser));
EntityTag eTag = new EntityTag(eTagValue);
Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
if (builder == null) {
List<DataEntity> spaceInfos = new ArrayList<>();
for (Space space : spaces) {
SpaceEntity spaceInfo = EntityBuilder.buildEntityFromSpace(space, authenticatedUser, uriInfo.getPath(), expand);
spaceInfos.add(spaceInfo.getDataEntity());
}
}

CollectionEntity collectionSpace = new CollectionEntity(spaceInfos, EntityBuilder.SPACES_TYPE, offset, limit);
if (returnSize) {
collectionSpace.setSize(listAccess.getSize());
}

if (StringUtils.isNotBlank(expand) && Arrays.asList(StringUtils.split(expand, ",")).contains(RestProperties.UNREAD)) {
SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class);
Map<Long, Long> unreadItemsPerSpace = spaceWebNotificationService.countUnreadItemsBySpace(authenticatedUser);
if (MapUtils.isNotEmpty(unreadItemsPerSpace)) {
collectionSpace.setUnreadPerSpace(unreadItemsPerSpace.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey().toString(),
e -> e.getValue())));
CollectionEntity collectionSpace = new CollectionEntity(spaceInfos, EntityBuilder.SPACES_TYPE, offset, limit);
if (returnSize) {
collectionSpace.setSize(listAccess.getSize());
}
}

String eTagValue = String.valueOf(Objects.hash(collectionSpace.hashCode(), authenticatedUser, expand));
EntityTag eTag = new EntityTag(eTagValue);
if (StringUtils.isNotBlank(expand) && Arrays.asList(StringUtils.split(expand, ",")).contains(RestProperties.UNREAD)) {
SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class);
Map<Long, Long> unreadItemsPerSpace = spaceWebNotificationService.countUnreadItemsBySpace(authenticatedUser);
if (MapUtils.isNotEmpty(unreadItemsPerSpace)) {
collectionSpace.setUnreadPerSpace(unreadItemsPerSpace.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey().toString(),
e -> e.getValue())));
}
}

Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
if (builder == null) {
builder = EntityBuilder.getResponseBuilder(collectionSpace, uriInfo, RestUtils.getJsonMediaType(), Response.Status.OK);
builder.tag(eTag);
Date date = new Date(System.currentTimeMillis());
builder.lastModified(date);
builder.expires(date);
builder.lastModified(new Date());
builder.cacheControl(CACHE_REVALIDATE_CONTROL);
}

return builder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ menu.expand=Keep the menu opened
menu.collapse=Collapse the menu
menu.createNewSpace=Create a space
menu.seeMySpaces=See my spaces
menu.spaces.joinOrCreateSpace=Join or create a space
menu.spaces.joinSpace=Join a space
menu.spaces.exploreSpaces=Explore Spaces
menu.spaces.noSpacesFound=No spaces found
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
-->
<template>
<v-container class="recentDrawer" flat>
<v-flex class="filterSpaces d-flex align-center">
<v-flex v-if="initialized || hasSpaces" class="filterSpaces d-flex align-center">
<v-list-item-icon
v-if="!displaySequentially"
class="backToMenu my-5 mx-2 icon-default-color justify-center"
Expand All @@ -44,6 +44,7 @@
<v-text-field
v-model="keyword"
:placeholder="$t('menu.spaces.recentSpaces')"
:loading="loading"
class="recentSpacesFilter border-bottom-color pt-0 mt-0"
single-line
hide-details
Expand All @@ -68,6 +69,16 @@
</v-list-item-action>
</v-list-item>
</v-flex>
<div class="position-relative">
<v-progress-linear
v-if="(!initialized && !hasSpaces) && loading"
class="position-absolute ful-width"
indeterminate />
<spaces-navigation-empty
v-if="!hasSpaces && !loading"
:keyword="keyword"
class="py-5" />
</div>
<spaces-navigation-content
:limit="itemsToShow"
:page-size="itemsToShow"
Expand All @@ -76,7 +87,9 @@
show-more-button
third-level
class="recentSpacesWrapper mt-4"
@open-space-panel="$emit('open-space-panel',$event)" />
@open-space-panel="$emit('open-space-panel',$event)"
@loading="loading = $event"
@spaces-count="hasSpaces = $event" />
</v-container>
</template>
<script>
Expand All @@ -91,12 +104,20 @@ export default {
default: false,
},
},
data () {
return {
itemsToShow: 15,
showFilter: false,
keyword: '',
};
data: () => ({
itemsToShow: 15,
showFilter: false,
loading: false,
initialized: false,
hasSpaces: false,
keyword: '',
}),
watch: {
loading() {
if (!this.loading) {
this.initialized = true;
}
},
},
methods: {
closeMenu() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
size="28"
class="me-3 ms-3 tile my-0 spaceAvatar"
tile>
<v-img :src="spaceAvatar" />
<img
:src="spaceAvatar"
:alt="spaceDisplayName"
width="28"
height="28">
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="body-2" v-text="spaceDisplayName" />
Expand Down Expand Up @@ -58,7 +62,12 @@
size="28"
class="me-3 ms-2 tile my-0 spaceAvatar"
tile>
<v-img :src="spaceAvatar" />
<img
:src="spaceAvatar"
:alt="spaceDisplayName"
class="rounded"
width="28"
height="28">
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="body-2" v-text="spaceDisplayName" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
</v-list>
<v-row v-if="canShowMore" class="mx-0 my-4 justify-center">
<v-btn
class="btn"
small
depressed
@click="loadNextPage()">
{{ $t('menu.spaces.showMore') }}
</v-btn>
Expand Down Expand Up @@ -136,9 +136,15 @@ export default {
limitToFetch() {
this.searchSpaces();
},
filteredSpaces() {
this.$emit('spaces-count', this.filteredSpaces?.length);
},
spaces() {
this.refreshSelectedSpace();
},
loadingSpaces() {
this.$emit('loading', this.loadingSpaces);
},
},
created() {
this.originalLimitToFetch = this.limitToFetch = this.limit;
Expand All @@ -165,6 +171,7 @@ export default {
}
},
searchSpaces() {
this.loadingSpaces = true;
return this.$spaceService.getSpaces('', this.offset, this.limitToFetch, 'lastVisited', 'member,managers,favorite,unread,muted')
.then(data => {
this.spaces = data && data.spaces || [];
Expand Down Expand Up @@ -204,5 +211,4 @@ export default {
},
}
};
</script>
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!--
This file is part of the Meeds project (https://meeds.io/).
Copyright (C) 2020 - 2023 Meeds Association [email protected]
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-->
<template>
<div class="d-flex flex-column align-center justify-center">
<v-icon size="36">fa-people-arrows</v-icon>
<div class="my-6">
{{ $t('menu.spaces.noSpacesFound') }}
</div>
<template v-if="!keyword">
<div v-if="$root.canAddSpaces">
{{ $t('menu.spaces.joinOrCreateSpace') }}
</div>
<div v-else>
{{ $t('menu.spaces.joinSpace') }}
</div>
</template>
<v-btn
:href="spacesLink"
:title="$t('menu.spaces.exploreSpaces')"
:class="keyword && 'mb-6' || 'my-6'"
class="btn primary">
<span class="text-none">
{{ $t('menu.spaces.exploreSpaces') }}
</span>
</v-btn>
</div>
</template>
<script>
export default {
props: {
keyword: {
type: String,
default: null,
},
},
data: () => ({
spacesLink: `${eXo.env.portal.context}/${eXo.env.portal.defaultPortal}/spaces`,
}),
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import SpacePanelHamburgerNavigation from './components/recent-spaces/SpacePanel
import SpacePanelHamburgerNavigationItem from './components/recent-spaces/SpacePanelHamburgerNavigationItem.vue';
import SpacesHamburgerNavigation from './components/recent-spaces/SpacesHamburgerNavigation.vue';
import SpacesNavigationContent from './components/recent-spaces/SpacesNavigationContent.vue';
import SpacesNavigationEmpty from './components/recent-spaces/SpacesNavigationEmpty.vue';
import SiteHamburgerNavigation from './components/site/SiteHamburgerNavigation.vue';
import UserHamburgerNavigation from './components/user/UserHamburgerNavigation.vue';
import SitesHamburger from './components/site/SitesHamburger.vue';
Expand All @@ -56,6 +57,7 @@ const components = {
'space-panel-hamburger-navigation': SpacePanelHamburgerNavigation,
'space-panel-hamburger-navigation-item': SpacePanelHamburgerNavigationItem,
'spaces-navigation-content': SpacesNavigationContent,
'spaces-navigation-empty': SpacesNavigationEmpty,
'site-hamburger-navigation': SiteHamburgerNavigation,
'user-hamburger-navigation': UserHamburgerNavigation,
'sites-hamburger': SitesHamburger,
Expand Down

0 comments on commit 6a0948f

Please sign in to comment.