Commit aeaf6ec2 authored by Curtis Adam's avatar Curtis Adam
Browse files

Merge branch '608-add-a-theme-switcher-to-the-profile-view' into 'staging'

Add a theme switcher to the profile settings

Closes #608

See merge request arsnova/cards!1154
parents cc64e72e fcff48a1
......@@ -3595,7 +3595,7 @@ $themes: (
background-color: $button_difficulty3 !important;
}
#setCardsetFormModal, #showSelectLearningUnitModal, #setLeitnerProgressCardsetFilter, #useCasesModal {
#setCardsetFormModal, #showSelectLearningUnitModal, #setLeitnerProgressCardsetFilter, #useCasesModal, #appThemeSelector {
ul > li {
background-color: $cardset_form_list_background !important;
border-bottom-color: $cardset_form_border !important;
......
{
"themes": {
"profile": {
"label": "Theme",
"default": "(Voreinstellung)",
"saved": "(Gespeichert)"
},
"list": {
"arsnova": "ARSnova.cards",
"linux": "Linux.cards"
}
}
}
......@@ -15,6 +15,7 @@ import {Utilities} from "../../util/utilities";
import {CardType} from "../../util/cardTypes";
import {LeitnerUtilities} from "../../util/leitner";
import {LeitnerTasks} from "../subscriptions/leitnerTasks";
import {ServerStyle} from "../../util/styles";
Meteor.methods({
updateUsersVisibility: function (visible, id) {
......@@ -118,6 +119,16 @@ Meteor.methods({
}
});
},
updateUserTheme: function (theme) {
if (ServerStyle.getAppThemes().length > 1) {
check(theme, String);
Meteor.users.update(Meteor.userId(), {
$set: {
savedTheme: theme
}
});
}
},
updateUsersProfileState: function (completed, id) {
check(completed, Boolean);
check(id, String);
......@@ -150,7 +161,6 @@ Meteor.methods({
lvl: 1,
lastOnAt: new Date(),
daysInRow: 0,
selectedColorTheme: "default",
mailNotification: false,
webNotification: false,
"profile.locale": "de",
......@@ -440,20 +450,6 @@ Meteor.methods({
);
}
},
/** Function saves the given colorTheme to the given user
* @param {string} selectedColorTheme - The id of the selected color theme
* @param {string} id - The id of the user
* */
updateColorTheme: function (selectedColorTheme, id) {
check(selectedColorTheme, String);
check(id, String);
Meteor.users.update(id, {
$set: {
"selectedColorTheme": selectedColorTheme
}
});
},
/** Function saves the given read message of the day ids to the given user
* @param {string} motds - new Array of motd ids
* @param {string} id - The id of the user
......
......@@ -244,7 +244,6 @@ let initTestNotificationsUser = function () {
"lvl": 17,
"lastOnAt": (new Date().setFullYear(2017, 9, 5)),
"daysInRow": 0,
"selectedColorTheme": "default",
"mailNotification": true,
"webNotification": false,
"profile": {
......@@ -294,7 +293,6 @@ let initDemoCardsetUser = function () {
"lvl": 0,
"lastOnAt": (new Date().setFullYear(2017, 9, 5)),
"daysInRow": 0,
"selectedColorTheme": "default",
"mailNotification": true,
"webNotification": false,
"profile": {
......
......@@ -203,11 +203,15 @@ module.exports = {
"randomCardsSelection": false
},
"themes": {
"defaultID": 0, // The default theme id from the list
"default": "arsnova", // The default theme from the list
"list": [
{
"theme": "arsnova", // The color theme
"backgrounds": "arsnova" // The background images found in ./backgrounds.js
},
{
"theme": "linux", // The color theme
"backgrounds": "linux" // The background images found in ./backgrounds.js
}
] // The list available to the theme switcher dropdown menu
},
......
......@@ -203,7 +203,7 @@ module.exports = {
"randomCardsSelection": false
},
"themes": {
"defaultID": 0, // The default theme id from the list
"default": "arsnova", // The default theme from the list
"list": [
{
"theme": "arsnova", // The color theme
......
......@@ -203,7 +203,7 @@ module.exports = {
"randomCardsSelection": false
},
"themes": {
"defaultID": 0, // The default theme id from the list
"default": "linux", // The default theme from the list
"list": [
{
"theme": "linux", // The color theme
......
......@@ -203,7 +203,7 @@ module.exports = {
"randomCardsSelection": false
},
"themes": {
"defaultID": 0, // The default theme id from the list
"default": "arsnova", // The default theme from the list
"list": [
{
"theme": "arsnova", // The color theme
......
......@@ -203,7 +203,7 @@ module.exports = {
"randomCardsSelection": false
},
"themes": {
"defaultID": 0, // The default theme id from the list
"default": "arsnova", // The default theme from the list
"list": [
{
"theme": "arsnova", // The color theme
......
......@@ -7,7 +7,7 @@ import {Meteor} from "meteor/meteor";
import {FlowRouter} from "meteor/ostrio:flow-router-extra";
import {LoginTasks} from "../../../util/login";
import {checkForNewMessages} from "../../../ui/messageOfTheDay/messageOfTheDay";
import {BackgroundChanger} from "../../../util/backgroundChanger";
import {ThemeChanger} from "../../../util/themeChanger";
import {Utilities} from "../../../util/utilities";
......@@ -49,7 +49,6 @@ var linksWithNoLoginRequirement = function () {
var isSignedIn = function () {
if (!(Meteor.user() || Meteor.loggingIn()) && !MainNavigation.isGuestLoginActive()) {
Session.set("theme", ServerStyle.getDefaultTheme());
if (MainNavigation.getLoginTarget() === undefined) {
if (linksWithNoLoginRequirement().includes(FlowRouter.getRouteName())) {
MainNavigation.setLoginTarget(false);
......@@ -92,7 +91,7 @@ export let checkMotds = function () {
});
};
FlowRouter.triggers.enter([Utilities.setActiveLanguage, BackgroundChanger.setTheme]);
FlowRouter.triggers.enter([Utilities.setActiveLanguage, ThemeChanger.setTheme]);
FlowRouter.triggers.exit(function (context) {
Session.set('previousRouteName', context.route.name);
......
......@@ -133,7 +133,6 @@ Meteor.users.after.insert(function (userId, doc) {
lvl: 1,
lastOnAt: new Date(),
daysInRow: 0,
selectedColorTheme: "default",
mailNotification: ServerStyle.newUser("mail"),
webNotification: ServerStyle.newUser("web"),
"profile.locale": "de",
......@@ -148,7 +147,6 @@ Meteor.users.after.insert(function (userId, doc) {
lvl: 1,
lastOnAt: new Date(),
daysInRow: 0,
selectedColorTheme: "default",
mailNotification: ServerStyle.newUser("mail"),
webNotification: ServerStyle.newUser("web"),
"profile.locale": "de"
......
......@@ -82,7 +82,7 @@ ul.nav-list li.active {
margin-bottom: 7px;
}
.setCardTypeDropdownCaret, .setCardTypeUseCaseDropdownCaret, .setLearningIndexDropdownCaret, .setTranscriptBonusLectureDropdownCaret {
.setCardTypeDropdownCaret, .setCardTypeUseCaseDropdownCaret, .setLearningIndexDropdownCaret, .setTranscriptBonusLectureDropdownCaret, .setAppThemeDropdownCaret {
width: 25px !important;
padding: 0 !important;
text-align: center !important;
......
......@@ -19,7 +19,7 @@
}
}
#setCardsetFormModal, #showSelectLearningUnitModal, #setLeitnerProgressCardsetFilter, #useCasesModal .dropdown {
#setCardsetFormModal, #showSelectLearningUnitModal, #setLeitnerProgressCardsetFilter, #useCasesModal .dropdown, #appThemeSelector .dropdown {
ul > li {
padding-top: $size_cardset_form_list_padding !important;
padding-bottom: $size_cardset_form_list_padding !important;
......@@ -28,6 +28,7 @@
a {
font-size: $font_size_cardset_form_list !important;
font-weight: $font_weight_cardset_form_list !important;
text-decoration: none;
}
}
ul > li:last-child {
......
......@@ -37,7 +37,7 @@ import {Fullscreen} from "../../util/fullscreen";
import {CardsetVisuals} from "../../util/cardsetVisuals";
import tippy from "tippy.js";
import 'tippy.js/animations/scale-extreme.css';
import {BackgroundChanger} from "../../util/backgroundChanger";
import {ThemeChanger} from "../../util/themeChanger";
import {LockScreen} from "../../util/lockScreen";
import {SweetAlertMessages} from "../../util/sweetAlert";
import {Utilities} from "../../util/utilities";
......@@ -46,7 +46,7 @@ Meteor.subscribe("notifications");
Meteor.subscribe("serverStatistics");
Meteor.subscribe("collegesCourses");
Session.setDefault("theme", ServerStyle.getDefaultTheme());
Session.setDefault("theme", ServerStyle.getDefaultThemeID());
Session.setDefault("fullscreen", false);
Session.setDefault("previousRouteName", undefined);
Session.setDefault("connectionStatus", 2);
......@@ -190,7 +190,7 @@ Meteor.startup(function () {
MainNavigation.enableKeyEvents();
CardNavigation.enableKeyEvents();
});
BackgroundChanger.setTheme();
ThemeChanger.setTheme();
MarkdeepContent.initializeStylesheet();
document.title = ServerStyle.getLastAppTitle();
windowResizeSensor = $(window).resize(function () {
......
......@@ -4,7 +4,7 @@ import {Filter} from "../../../../../util/filter";
import {Route} from "../../../../../util/route";
import {WordcloudCanvas} from "../../../../../util/wordcloudCanvas";
import ResizeSensor from "../../../../../../client/thirdParty/resizeSensor/ResizeSensor";
import {BackgroundChanger} from "../../../../../util/backgroundChanger";
import {ThemeChanger} from "../../../../../util/themeChanger";
Session.setDefault('filterDisplayWordcloud', false);
......@@ -38,11 +38,11 @@ Template.mainNavigationTopItemDisplayModeButton.events({
if (Session.get('filterDisplayWordcloud')) {
Filter.resetMaxItemCounter();
WordcloudCanvas.disableWordcloud();
BackgroundChanger.setTheme();
ThemeChanger.setTheme();
} else {
Filter.resetMaxItemCounter();
WordcloudCanvas.enableWordcloud();
BackgroundChanger.changeToStyle("wordcloud");
ThemeChanger.changeToBackgroundStyle("wordcloud");
}
}
});
......
......@@ -94,8 +94,42 @@
<span id="errorGivenName" class="help-block name"></span>
</div>
</div>
{{#if gotMultipleThemes}}
<div id="appThemeSelector" class="form-group">
<label id="setCardTypeLabel" class="col-md-4 control-label" for="setAppTheme">{{_
"themes.profile.label"}}:</label>
<form>
<div class="dropdown">
<div class="btn-group col-xs-12 col-md-8">
<button type="button"
class="btn btn-raised btn-default setAppTheme setAppThemeDropdown longButton"
data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false"
value="{{cardType}}">
<span class="setAppThemeDropdownText">{{getActiveTheme}}</span>
</button>
<button type="button"
class="btn btn-raised dropdown-toggle setAppThemeDropdown setAppThemeDropdownCaret"
data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span class="flex-content"><span class="caret"></span>
<span class="sr-only"></span></span>
</button>
<ul class="dropdown-menu btn-raised longButton">
{{#each getAppThemes}}
<li class="themeSelection" value="{{this.theme}}" data-id="{{this.theme}}">
<a rel="noopener noreferrer" href="#" data-id="{{this.theme}}">{{getAppThemeName this.theme}}</a>
</li>
{{/each}}
</ul>
<span id="helpSetAppTheme" class="text-warning"></span>
</div>
</div>
</form>
</div>
{{/if}}
{{#if isNotificationEnabled}}
<div class="form-group">
<div id="notificationFormGroup" class="form-group">
<label class="col-md-4 control-label">{{_ "confirmLearn-form.notification"}}:</label>
<form>
<fieldset>
......
......@@ -3,7 +3,6 @@
import {Meteor} from "meteor/meteor";
import {Template} from "meteor/templating";
import {Session} from "meteor/session";
import {ColorThemes} from "../../../api/subscriptions/colorThemes";
import {BertAlertVisuals} from "../../../util/bertAlertVisuals";
import "../modal/deleteProfile.js";
import "../view/public.js";
......@@ -11,6 +10,7 @@ import "./settings.html";
import {ServerSettings} from "../../../util/settings";
import {ServerStyle} from "../../../util/styles";
import {UserPermissions} from "../../../util/permissions";
import {ThemeChanger} from "../../../util/themeChanger";
/*
* ############################################################################
......@@ -29,16 +29,6 @@ Template.profileSettings.helpers({
gotMultipleRoles: function (count) {
return count >= 2;
},
/** Function returns all colorThemes from the databse */
getColorThemes: function () {
return ColorThemes.find();
},
/** Function returns "selected" when the value of the selectedColorTheme and the input _id are the same */
getSelectedColorThemes: function () {
if (this._id === Meteor.users.findOne(Meteor.userId()).selectedColorTheme) {
return "selected";
}
},
/** Function returns "selected" when the value of the selectedLanguage and the input _id are the same */
getSelectedLanguage: function (id) {
if (id === Meteor.users.findOne(Meteor.userId()).profile.locale) {
......@@ -66,6 +56,27 @@ Template.profileSettings.helpers({
},
isDisabledSaveLanguage: function () {
return Session.get("languageSettings");
},
gotMultipleThemes: function () {
return ServerStyle.getAppThemes().length > 1;
},
getAppThemes: function () {
return ServerStyle.getAppThemes();
},
getActiveTheme: function () {
let activeTheme = ServerStyle.getActiveTheme();
return TAPi18n.__(`themes.list.${activeTheme.theme}`);
},
getAppThemeName: function (theme) {
let string = TAPi18n.__(`themes.list.${theme}`);
if (theme === ServerStyle.getDefaultThemeID()) {
string += ` ${TAPi18n.__(`themes.profile.default`)}`;
}
let savedTheme = ServerStyle.getSavedTheme();
if (savedTheme !== undefined && theme === savedTheme.theme) {
string += ` ${TAPi18n.__(`themes.profile.saved`)}`;
}
return string;
}
});
......@@ -73,7 +84,7 @@ Template.profileSettings.helpers({
Template.profileSettings.onDestroyed(function () {
// Go back to last saved Theme
if (Meteor.user()) {
Session.set("theme", ServerStyle.getDefaultTheme());
Session.set("theme", ServerStyle.getActiveTheme());
Session.set("language", Meteor.user().profile.locale);
}
});
......@@ -86,6 +97,10 @@ Template.profileSettings.onCreated(function () {
});
Template.profileSettings.events({
"click .themeSelection": function (event) {
Session.set("theme", $(event.target).data('id'));
ThemeChanger.displayTheme();
},
"click #profilepublicoption1": function () {
Meteor.call("updateUsersVisibility", true, Meteor.userId());
},
......@@ -267,6 +282,10 @@ Template.profileSettings.events({
Meteor.call("updateUsersGivenName", givenname, user_id);
Meteor.call("updateUsersProfileState", true, user_id);
Meteor.call("updateUsersNotification", mailNotification, webNotification, user_id);
if (ServerStyle.getAppThemes().length > 1) {
Meteor.call("updateUserTheme", Session.get("theme"));
$('#appThemeSelector').addClass('has-success');
}
BertAlertVisuals.displayBertAlert(TAPi18n.__('profile.saved'), 'success', 'growl-top-left');
} else {
......@@ -299,11 +318,13 @@ Template.profileSettings.events({
$('#inputName').parent().parent().removeClass('has-success');
$('#inputBirthName').parent().parent().removeClass('has-success');
$('#inputGivenName').parent().parent().removeClass('has-success');
$('#appThemeSelector').removeClass('has-success');
$('#errorName').html('');
$('#errorBirthName').html('');
$('#errorGivenName').html('');
Session.set("profileSettingsSave", true);
Session.set("profileSettingsCancel", true);
ThemeChanger.setTheme();
BertAlertVisuals.displayBertAlert(TAPi18n.__('profile.canceled'), 'danger', 'growl-top-left');
}
});
......@@ -8,6 +8,7 @@ import * as RouteNames from "../util/routeNames.js";
import {FlowRouter} from "meteor/ostrio:flow-router-extra";
import {EDU, FREE, GUEST, LECTURER, PRO, SERVER_VERSION} from "../config/serverStyle/style/global/const";
import {CardsetNavigation} from "./cardsetNavigation";
import {Session} from "meteor/session";
export let ServerStyle = class ServerStyle {
......@@ -243,9 +244,29 @@ export let ServerStyle = class ServerStyle {
return config.help.markdeepFormatingCardsetID;
}
static getDefaultTheme () {
static getDefaultThemeID () {
return this.getConfig().themes.default;
}
static getSavedTheme () {
if (Meteor.user().savedTheme !== undefined) {
let config = this.getConfig().themes;
return config.list.filter(object => {
return object.theme === Meteor.user().savedTheme;
})[0];
}
}
static getActiveTheme () {
let config = this.getConfig().themes;
return config.list[config.defaultID].theme;
return config.list.filter(object => {
return object.theme === Session.get('theme');
})[0];
}
static getAppThemes () {
let config = this.getConfig().themes;
return config.list;
}
static adjustForLandingPageBackground (backgrounds, target = "") {
......@@ -272,8 +293,7 @@ export let ServerStyle = class ServerStyle {
static getBackground (type) {
let backgrounds;
let backgroundObject = "";
let config = this.getConfig().themes;
backgrounds = backgroundsConf[config.list[config.defaultID].backgrounds];
backgrounds = backgroundsConf[this.getActiveTheme().backgrounds];
if (Route.isCardsetGroup() && !CardsetNavigation.doesCardsetExist(FlowRouter.current().params._id)) {
backgroundObject = this.adjustForInternalBackground(backgrounds, backgrounds.notFound);
} else {
......
......@@ -4,9 +4,9 @@ import {Session} from "meteor/session";
import {Meteor} from "meteor/meteor";
import {MainNavigation} from "./mainNavigation";
export let BackgroundChanger = class BackgroundChanger {
static changeToStyle (name, cssClass = undefined) {
BackgroundChanger.setBackground(ServerStyle.getBackground(name), cssClass);
export let ThemeChanger = class ThemeChanger {
static changeToBackgroundStyle (name, cssClass = undefined) {
ThemeChanger.setBackground(ServerStyle.getBackground(name), cssClass);
}
static setBackground (backgroundObject, cssClass = undefined) {
......@@ -26,26 +26,26 @@ export let BackgroundChanger = class BackgroundChanger {
static landingPageBackgrounds () {
if (Route.isDemo() || Route.isMakingOf()) {
if (Route.isPresentationViewList()) {
BackgroundChanger.changeToStyle("demoIndex", "presentation-list");
ThemeChanger.changeToBackgroundStyle("demoIndex", "presentation-list");
} else {
BackgroundChanger.changeToStyle("demo", "demo");
ThemeChanger.changeToBackgroundStyle("demo", "demo");
}
} else if (Route.isAGB()) {
BackgroundChanger.changeToStyle("agb");
ThemeChanger.changeToBackgroundStyle("agb");
} else if (Route.isDatenschutz()) {
BackgroundChanger.changeToStyle("datenschutz");
ThemeChanger.changeToBackgroundStyle("datenschutz");
} else if (Route.isImpressum()) {
BackgroundChanger.changeToStyle("impressum");
ThemeChanger.changeToBackgroundStyle("impressum");
} else if (Route.isAbout()) {
BackgroundChanger.changeToStyle("about");
ThemeChanger.changeToBackgroundStyle("about");
} else if (Route.isLearning()) {
BackgroundChanger.changeToStyle("learning");
ThemeChanger.changeToBackgroundStyle("learning");
} else if (Route.isFaq()) {
BackgroundChanger.changeToStyle("faq");
ThemeChanger.changeToBackgroundStyle("faq");
} else if (Route.isHelp()) {
BackgroundChanger.changeToStyle("help");
ThemeChanger.changeToBackgroundStyle("help");
} else {
BackgroundChanger.changeToStyle("landing-page");
ThemeChanger.changeToBackgroundStyle("landing-page");
}
}
......@@ -55,17 +55,24 @@ export let BackgroundChanger = class BackgroundChanger {
} else {
Session.set('displayMainNavigation', true);
}
if (Meteor.user()) {
// If there is no selectedColorTheme the Session var "theme" will stay NULL.
if (Meteor.users.findOne(Meteor.userId())) {
if (Meteor.users.findOne(Meteor.userId()).selectedColorTheme) {
Session.set("theme", ServerStyle.getDefaultTheme());
}
if (ServerStyle.getAppThemes().length > 1 && Meteor.user()) {
//Check if the user got a saved theme or if the saved theme is still available
let savedTheme = ServerStyle.getSavedTheme();
if (savedTheme !== undefined) {
Session.set("theme", savedTheme.theme);
} else {
let defaultThemeID = ServerStyle.getDefaultThemeID();
Session.set("theme", defaultThemeID);
Meteor.call("updateUserTheme", defaultThemeID);
}
} else {
// When user logged out, go back to default Theme
Session.set("theme", ServerStyle.getDefaultTheme());
Session.set("theme", ServerStyle.getDefaultThemeID());
}
ThemeChanger.displayTheme();
}
static displayTheme () {
let themeId;
if (Meteor.user() || MainNavigation.isGuestLoginActive()) {
if (Session.get('fullscreen') && !Route.isPresentationList()) {
......@@ -86,62 +93,65 @@ export let BackgroundChanger = class BackgroundChanger {
}
html.attr('id', themeId);
html.attr('class', Session.get("theme"));
this.setBackgroundStyle();
}
static setBackgroundStyle () {
//Background
if (Route.isLandingPageRoutes()) {
BackgroundChanger.landingPageBackgrounds();
ThemeChanger.landingPageBackgrounds();
} else if (Meteor.user() || MainNavigation.isGuestLoginActive()) {
if (Route.isBackend()) {
BackgroundChanger.changeToStyle("backend", "backend");
ThemeChanger.changeToBackgroundStyle("backend", "backend");
} else {
let internal = 'internal';
if (Route.isProfile()) {
if (Route.isProfileSettings()) {
BackgroundChanger.changeToStyle("profileSettings", internal);
ThemeChanger.changeToBackgroundStyle("profileSettings", internal);
} else if (Route.isProfileMembership()) {
BackgroundChanger.changeToStyle("profileMembership", internal);
ThemeChanger.changeToBackgroundStyle("profileMembership", internal);
} else if (Route.isProfileRequests()) {
BackgroundChanger.changeToStyle("profileRequests", internal);
ThemeChanger.changeToBackgroundStyle("profileRequests", internal);
} else {
BackgroundChanger.changeToStyle("profileBilling", internal);
ThemeChanger.changeToBackgroundStyle("profileBilling", internal);
}
} else if (Route.isPublic()) {
BackgroundChanger.changeToStyle("pool", internal);
ThemeChanger.changeToBackgroundStyle("pool", internal);
} else if (Route.isWorkload()) {
BackgroundChanger.changeToStyle("workload", internal);
ThemeChanger.changeToBackgroundStyle("workload", internal);
} else if (Route.isPersonal()) {
BackgroundChanger.changeToStyle("personal", internal);
ThemeChanger.changeToBackgroundStyle("personal", internal);
} else if (Route.isMyTranscripts() || Route.isMyBonusTranscripts()) {
BackgroundChanger.changeToStyle("transcripts", internal);
ThemeChanger.changeToBackgroundStyle("transcripts", internal);