Skip to content

Commit

Permalink
feat: Add Reduced Sidebar Menu Option - Meeds-io/MIPs#159
Browse files Browse the repository at this point in the history
  • Loading branch information
boubaker committed Nov 26, 2024
1 parent 63f234b commit 7d09a7d
Show file tree
Hide file tree
Showing 43 changed files with 1,019 additions and 1,019 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class SidebarConfiguration implements Cloneable {

private SidebarMode defaultMode;

private SidebarMode userMode;

private List<SidebarMode> allowedModes;

private List<SidebarItem> items;
Expand All @@ -44,6 +46,7 @@ public class SidebarConfiguration implements Cloneable {
public SidebarConfiguration clone() { // NOSONAR
return new SidebarConfiguration(allowUserCustomHome,
defaultMode,
userMode,
new ArrayList<>(allowedModes),
new ArrayList<>(items));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ private NavigationConfiguration buildDefaultNavigationConfiguration() {
getDefaultTopbarApplications());
SidebarConfiguration sidebarConfiguration = new SidebarConfiguration(allowUserCustomHome,
defaultMode,
null,
Arrays.asList(SidebarMode.values()),
getDefaultNavigations());
return new NavigationConfiguration(topbarConfiguration, sidebarConfiguration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import io.meeds.social.navigation.constant.SidebarItemType;
import io.meeds.social.navigation.constant.SidebarMode;
import io.meeds.social.navigation.model.NavigationConfiguration;
import io.meeds.social.navigation.model.SidebarConfiguration;
import io.meeds.social.navigation.model.SidebarItem;
Expand Down Expand Up @@ -100,7 +102,33 @@ public TopbarConfiguration getTopbarConfiguration(String username, Locale locale
* settings
*/
public SidebarConfiguration getSidebarConfiguration(String username, Locale locale) {
return getConfiguration(username, locale, true).getSidebar();
SidebarConfiguration sidebarConfiguration = getConfiguration(username, locale, true).getSidebar();
if (StringUtils.isNotBlank(username)) {
SidebarMode mode = getSidebarUserMode(username, sidebarConfiguration);
sidebarConfiguration.setUserMode(mode);
}
return sidebarConfiguration;
}

/**
* Retrieves the preferred mode of sidebar by a user
*
* @param username User name as identifier
* @return preferred {@link SidebarMode} or default if not set by user yet
*/
public SidebarMode getSidebarUserMode(String username) {
NavigationConfiguration configuration = navigationConfigurationStorage.getConfiguration(defaultTopbarApplications);
return getSidebarUserMode(username, configuration.getSidebar());
}

/**
* Updates the preferred mode of sidebar by the user
*
* @param username User name as identifier
* @param mode Preferred {@link SidebarMode} by the user
*/
public void updateSidebarUserMode(String username, SidebarMode mode) {
navigationConfigurationStorage.updateSidebarUserMode(username, mode);
}

/**
Expand All @@ -112,6 +140,16 @@ public void updateConfiguration(NavigationConfiguration navigationConfiguration)
navigationConfigurationStorage.updateConfiguration(navigationConfiguration);
}

private SidebarMode getSidebarUserMode(String username, SidebarConfiguration sidebarConfiguration) {
SidebarMode mode = navigationConfigurationStorage.getSidebarUserMode(username);
if (sidebarConfiguration != null
&& (mode == null
|| !sidebarConfiguration.getAllowedModes().contains(mode))) {
mode = sidebarConfiguration.getDefaultMode();
}
return mode;
}

@SneakyThrows
private SidebarItem expandSidebarItem(SidebarItem item, String username, Locale locale) {
return getPlugin(item.getType()).resolveProperties(item, username, locale);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.exoplatform.commons.api.settings.data.Context;
import org.exoplatform.commons.api.settings.data.Scope;

import io.meeds.social.navigation.constant.SidebarMode;
import io.meeds.social.navigation.constant.TopbarItemType;
import io.meeds.social.navigation.model.NavigationConfiguration;
import io.meeds.social.navigation.model.TopbarApplication;
Expand All @@ -41,10 +42,12 @@ public class NavigationConfigurationStorage {

private static final String SETTING_KEY = "configuration";

private static final Scope SETTING_GLOBAL_SCOPE = Scope.GLOBAL.id("NavigationConfiguration");
private static final String SETTING_USER_MODE_KEY = "sidebarMode";

private static final Context SETTING_GLOBAL_CONTEXT = Context.GLOBAL.id("NavigationConfiguration");

private static final Scope SETTING_SCOPE = Scope.APPLICATION.id("NavigationConfiguration");

private static final NavigationConfiguration NULL_VALUE = new NavigationConfiguration();

@Autowired
Expand All @@ -62,16 +65,31 @@ public NavigationConfiguration getConfiguration(List<TopbarApplication> defaultA
public void updateConfiguration(NavigationConfiguration navigationConfiguration) {
try {
settingService.set(SETTING_GLOBAL_CONTEXT,
SETTING_GLOBAL_SCOPE,
SETTING_SCOPE,
SETTING_KEY,
SettingValue.create(JsonUtils.toJsonString(navigationConfiguration)));
} finally {
this.navigationConfiguration = null;
}
}

public SidebarMode getSidebarUserMode(String username) {
SettingValue<?> settingValue = settingService.get(Context.USER.id(username),
SETTING_SCOPE,
SETTING_USER_MODE_KEY);
return settingValue == null || settingValue.getValue() == null ? null :
SidebarMode.valueOf(settingValue.getValue().toString());
}

public void updateSidebarUserMode(String username, SidebarMode mode) {
settingService.set(Context.USER.id(username),
SETTING_SCOPE,
SETTING_USER_MODE_KEY,
SettingValue.create(mode.name()));
}

private NavigationConfiguration retrieveNavigationConfiguration(List<TopbarApplication> defaultApplications) {
SettingValue<?> settingValue = settingService.get(SETTING_GLOBAL_CONTEXT, SETTING_GLOBAL_SCOPE, SETTING_KEY);
SettingValue<?> settingValue = settingService.get(SETTING_GLOBAL_CONTEXT, SETTING_SCOPE, SETTING_KEY);
if (settingValue == null || settingValue.getValue() == null) {
return NULL_VALUE;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import io.meeds.social.navigation.constant.SidebarMode;
import io.meeds.social.navigation.model.NavigationConfiguration;
import io.meeds.social.navigation.model.SidebarConfiguration;
import io.meeds.social.navigation.model.TopbarConfiguration;
import io.meeds.social.navigation.service.NavigationConfigurationService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand Down Expand Up @@ -71,17 +74,27 @@ public TopbarConfiguration getTopbarConfiguration(HttpServletRequest request) {
}

@GetMapping("/sidebar")
@Secured("users")
@Operation(summary = "Retrieve Sidebar settings",
method = "GET",
description = "This retrieves the configuration of Sidebar switch user roles")
@Operation(summary = "Retrieve Sidebar settings", method = "GET", description = "This retrieves the configuration of Sidebar switch user roles")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
})
public SidebarConfiguration getSidebarConfiguration(HttpServletRequest request) {
return navigationConfigurationService.getSidebarConfiguration(request.getRemoteUser(), request.getLocale());
}

@PutMapping(path = "/sidebar/mode", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@Secured("users")
@Operation(summary = "Retrieve Sidebar settings", method = "GET", description = "This retrieves the configuration of Sidebar switch user roles")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
})
public void updateSidebarUserMode(HttpServletRequest request,
@Parameter(description = "User preferred Sidebar Mode")
@RequestParam("mode")
SidebarMode mode) {
navigationConfigurationService.updateSidebarUserMode(request.getRemoteUser(), mode);
}

@PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
@Secured("administrators")
@Operation(summary = "Updates Topbar and Sidebar settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ menu.role.navigation.first.level=First Level Navigation
menu.role.navigation.second.level=Second Level Navigation
menu.expand=Keep the menu opened
menu.collapse=Collapse the menu
menu.reduce=Keep icons only
menu.createNewSpace=Create a space
menu.seeMySpaces=See my spaces
menu.spaces.joinOrCreateSpace=Join or create a space
Expand Down
37 changes: 15 additions & 22 deletions webapp/src/main/webapp/WEB-INF/jsp/portlet/hamburgerMenu.jsp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<%@page import="io.meeds.social.navigation.constant.SidebarMode"%>
<%@page import="io.meeds.social.navigation.service.NavigationConfigurationService"%>
<%@page import="org.exoplatform.social.core.identity.model.Identity"%>
<%@page import="io.meeds.social.util.JsonUtils"%>
<%@page import="org.exoplatform.social.notification.service.SpaceWebNotificationService"%>
Expand All @@ -12,44 +14,38 @@
<%@page import="org.exoplatform.commons.api.settings.SettingService"%>
<%@page import="org.exoplatform.container.ExoContainerContext"%>
<%@page import="io.meeds.social.space.template.service.SpaceTemplateService"%>
<%@ page import="org.exoplatform.social.webui.Utils"%>
<%@page import="org.exoplatform.social.webui.Utils"%>
<%
boolean canCreateSpace = ExoContainerContext.getService(SpaceTemplateService.class).canCreateSpace(request.getRemoteUser());
SettingValue stickySettingValue = ExoContainerContext.getService(SettingService.class).get(Context.USER.id(request.getRemoteUser()), Scope.APPLICATION.id("HamburgerMenu"), "Sticky");
boolean sticky = stickySettingValue == null ? Boolean.parseBoolean(System.getProperty("io.meeds.userPrefs.HamburgerMenu.sticky", "false")) : Boolean.parseBoolean(stickySettingValue.getValue().toString());
PortalRequestContext rcontext = (PortalRequestContext) PortalRequestContext.getCurrentInstance();
PortalHttpServletResponseWrapper responseWrapper = (PortalHttpServletResponseWrapper) rcontext.getResponse();
if (rcontext.getRequest().getParameter("sticky") != null) {
sticky = StringUtils.equals("true", rcontext.getRequest().getParameter("sticky"));
}
UserPortalConfigService portalConfigService = ExoContainerContext.getService(UserPortalConfigService.class);
SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class);
SidebarMode mode = ExoContainerContext.getService(NavigationConfigurationService.class).getSidebarUserMode(request.getRemoteUser());
Identity viewerIdentity = Utils.getViewerIdentity();
String avatarUrl = viewerIdentity == null ? "" : viewerIdentity.getProfile().getAvatarUrl();
Map<Long, Long> unreadPerSpace = spaceWebNotificationService.countUnreadItemsBySpace(request.getRemoteUser());
Map<Long, Long> unreadPerSpace = ExoContainerContext.getService(SpaceWebNotificationService.class)
.countUnreadItemsBySpace(request.getRemoteUser());
String defaultHomePath = "/portal/" + rcontext.getPortalOwner();
String defaultUserPath = defaultHomePath;
String defaultUserPath;
if (StringUtils.equals(rcontext.getPortalOwner(), "public")) {
defaultUserPath = "/portal/public";
} else {
defaultUserPath = portalConfigService.getUserHomePage(request.getRemoteUser());
if (defaultUserPath == null) {
defaultUserPath = portalConfigService.computePortalPath(rcontext.getRequest());
if (defaultUserPath == null) {
defaultUserPath = defaultHomePath;
}
}
}
if (defaultUserPath == null) {
defaultUserPath = "/portal/" + rcontext.getPortalOwner();
}
responseWrapper.addHeader("Link", "</social/rest/navigation/settings/sidebar>; rel=preload; as=fetch; crossorigin=use-credentials", false);
((PortalHttpServletResponseWrapper) rcontext.getResponse()).addHeader("Link", "</social/rest/navigation/settings/sidebar>; rel=preload; as=fetch; crossorigin=use-credentials", false);
%>
<div class="VuetifyApp">
<div id="HamburgerNavigationMenu" data-app="true" class="v-application HamburgerNavigationMenu v-application--is-ltr theme--light" id="app" color="transaprent" flat="">
<div class="v-application--wrap">
<% if (sticky) { %>
<% if (mode != SidebarMode.HIDDEN) { %>
<script type="text/javascript">
if (!window.siteStickyMenuLoaded) {
window.siteStickyMenuLoaded = true;
Expand Down Expand Up @@ -81,10 +77,7 @@
<% } %>
</div>
<script type="text/javascript">
if (!window.siteStickyMenuInitialized) {
window.siteStickyMenuInitialized = true;
require(['PORTLET/social/HamburgerMenu'], app => app.init(<%=canCreateSpace%>, '<%=defaultUserPath%>', <%=unreadPerSpace == null ? "{}" : JsonUtils.toJsonString(unreadPerSpace)%>, '<%=avatarUrl == null ? "" : avatarUrl%>'));
}
require(['PORTLET/social/HamburgerMenu'], app => app.init('<%=mode%>', '<%=defaultUserPath%>', <%=unreadPerSpace == null ? "{}" : JsonUtils.toJsonString(unreadPerSpace)%>, '<%=avatarUrl == null ? "" : avatarUrl%>'));
</script>
</div>
</div>
4 changes: 2 additions & 2 deletions webapp/src/main/webapp/WEB-INF/jsp/portlet/topbarLogo.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@
<div class="d-flex mx-0 pa-0">
<% if (space == null) { %>
<% if (logoPath != null) { %>
<a id="UserHomePortalLink" href="<%=portalPath%>" class="pe-3 logoContainer">
<a id="UserHomePortalLinkLogo" href="<%=portalPath%>" class="pe-3 logoContainer">
<img src="<%=logoPath%>" height="36px" width="auto" class="<%=imageClass%>" alt="<%=logoTitle%>">
</a>
<% } %>
<a href="<%=portalPath%>" title="<%=logoTitle%>"
<a id="UserHomePortalLinkName" href="<%=portalPath%>" title="<%=logoTitle%>"
class="ps-2 align-self-center brandingContainer <%=titleClass%>">
<div class="logoTitle text-body font-weight-bold menu-text-color text-truncate">
<%= logoTitle%>
Expand Down
49 changes: 0 additions & 49 deletions webapp/src/main/webapp/skin/less/portlet/HamburgerMenu/Style.less
Original file line number Diff line number Diff line change
Expand Up @@ -59,57 +59,8 @@
color: @toolbarLightIconColor !important;
margin: 0 auto;
}
.accountTitleWrapper {
border-bottom: 1px solid #f9f9f9;
.accountTitleItem {
max-width: 100%;
&:before {
display: none!important;
}
&:hover {
&:before {
opacity: 0;
}
}
&:focus {
&:before {
opacity: 0;
}
}
.accountTitleLabel {
color: @toolbarLightLinkHoverColor;
font-size: 0.9rem!important;
}
.externalFlagClass {
color: @baseColorLight;
font-weight: normal;
}
}
}
.v-list-item--active:before {
content: '';
width: 8px;
background:@secondaryColor;
position: absolute;
left: 0 ~'; /** orientation=lt */ ';
right: 0 ~'; /** orientation=rt */ ';
opacity: 1 !important;
}
}

#HamburgerMenuSpaceLeftNavigationActions i.v-icon {
font-size: 20px !important;
}

@media (min-width: @minDesktop) {
.HamburgerMenuSticky {
#UITopBarContainerParent {
position: relative;
}
/* added for page loading phase */
#ParentSiteLeftContainer {
min-width: 310px;
background-color: @lightWhite;
}
}
}
Loading

0 comments on commit 7d09a7d

Please sign in to comment.