- Introduced Vocab Trainer functionality, including new routes for managing languages and chapters. - Implemented database schema for vocab-related tables to ensure data integrity. - Updated navigation and UI components to include Vocab Trainer in the social network menu. - Added translations for Vocab Trainer in both German and English locales, enhancing user accessibility.
416 lines
14 KiB
JavaScript
416 lines
14 KiB
JavaScript
import User from '../models/community/user.js';
|
||
import UserParam from '../models/community/user_param.js';
|
||
import UserRight from '../models/community/user_right.js';
|
||
import UserRightType from '../models/type/user_right.js';
|
||
import UserParamType from '../models/type/user_param.js';
|
||
import FalukantUser from '../models/falukant/data/user.js';
|
||
import VocabService from '../services/vocabService.js';
|
||
|
||
const menuStructure = {
|
||
home: {
|
||
visible: ["all"],
|
||
children: {},
|
||
path: "/",
|
||
icon: "logo_mono.png"
|
||
},
|
||
friends: {
|
||
visible: ["all"],
|
||
children: {
|
||
manageFriends: {
|
||
visible: ["all"],
|
||
path: "/friends",
|
||
icon: "friends24.png"
|
||
}
|
||
},
|
||
showLoggedinFriends: 1,
|
||
icon: "friends24.png"
|
||
},
|
||
socialnetwork: {
|
||
visible: ["all"],
|
||
icon: "socialnetwork.png",
|
||
children: {
|
||
guestbook: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/guestbook"
|
||
},
|
||
blog: {
|
||
visible: ["all"],
|
||
path: "/blogs"
|
||
},
|
||
usersearch: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/search"
|
||
},
|
||
forum: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/forum",
|
||
showForums: 1
|
||
},
|
||
gallery: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/gallery"
|
||
},
|
||
vocabtrainer: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/vocab",
|
||
children: {}
|
||
},
|
||
blockedUsers: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/blocked"
|
||
},
|
||
oneTimeInvitation: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/onetimeinvitation"
|
||
},
|
||
diary: {
|
||
visible: ["all"],
|
||
path: "/socialnetwork/diary"
|
||
},
|
||
erotic: {
|
||
visible: ["over18"],
|
||
children: {
|
||
pictures: {
|
||
visible: ["over18"],
|
||
path: "/socialnetwork/erotic/pictures"
|
||
},
|
||
videos: {
|
||
visible: ["over18"],
|
||
path: "/socialnetwork/erotic/videos"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
chats: {
|
||
visible: ["over12"],
|
||
icon: "chat.png",
|
||
children: {
|
||
multiChat: {
|
||
visible: ["over12"],
|
||
action: "openMultiChat",
|
||
view: "window",
|
||
class: "multiChatDialog",
|
||
icon: "multichat24.png"
|
||
},
|
||
randomChat: {
|
||
visible: ["over12"],
|
||
action: "openRanomChat",
|
||
view: "window",
|
||
class: "randomChatDialog"
|
||
},
|
||
eroticChat: {
|
||
visible: ["over18"],
|
||
action: "openEroticChat",
|
||
view: "window",
|
||
class: "eroticChatWindow"
|
||
}
|
||
}
|
||
},
|
||
falukant: {
|
||
visible: ["all"],
|
||
icon: "falukant16.png",
|
||
children: {
|
||
create: {
|
||
visible: ["nofalukantaccount"],
|
||
path: "/falukant/create"
|
||
},
|
||
overview: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/home"
|
||
},
|
||
towns: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/branch"
|
||
},
|
||
family: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/family"
|
||
},
|
||
house: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/house"
|
||
},
|
||
nobility: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/nobility"
|
||
},
|
||
church: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/church"
|
||
},
|
||
politics: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/politics"
|
||
},
|
||
education: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/education"
|
||
},
|
||
health: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/health"
|
||
},
|
||
bank: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/bank"
|
||
},
|
||
darknet: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/darknet"
|
||
},
|
||
reputation: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/reputation"
|
||
},
|
||
moneyhistory: {
|
||
visible: ["hasfalukantaccount"],
|
||
path: "/falukant/moneyhistory"
|
||
}
|
||
}
|
||
},
|
||
minigames: {
|
||
visible: ["all"],
|
||
icon: "minigames16.png",
|
||
children: {
|
||
match3: {
|
||
visible: ["all"],
|
||
path: "/minigames/match3"
|
||
},
|
||
taxi: {
|
||
visible: ["all"],
|
||
path: "/minigames/taxi"
|
||
}
|
||
}
|
||
},
|
||
settings: {
|
||
visible: ["all"],
|
||
icon: "settings16.png",
|
||
children: {
|
||
homepage: {
|
||
visible: ["all"],
|
||
path: "/settings/homepage"
|
||
},
|
||
account: {
|
||
visible: ["all"],
|
||
path: "/settings/account"
|
||
},
|
||
personal: {
|
||
visible: ["all"],
|
||
path: "/settings/personal"
|
||
},
|
||
view: {
|
||
visible: ["all"],
|
||
path: "/settings/view"
|
||
},
|
||
interests: {
|
||
visible: ["all"],
|
||
path: "/settings/interests"
|
||
},
|
||
flirt: {
|
||
visible: ["over14"],
|
||
path: "/settings/flirt"
|
||
},
|
||
sexuality: {
|
||
visible: ["over14"],
|
||
path: "/settings/sexuality"
|
||
},
|
||
notifications: {
|
||
visible: ["all"],
|
||
path: "/settings/notifications"
|
||
}
|
||
}
|
||
},
|
||
administration: {
|
||
visible: ["anyadmin"],
|
||
children: {
|
||
contactrequests: {
|
||
visible: ["mainadmin", "contactrequests"],
|
||
path: "/admin/contacts"
|
||
},
|
||
users: {
|
||
visible: ["mainadmin", "useradministration"],
|
||
children: {
|
||
userlist: {
|
||
visible: ["mainadmin", "useradministration"],
|
||
path: "/admin/users"
|
||
},
|
||
userstatistics: {
|
||
visible: ["mainadmin"],
|
||
path: "/admin/users/statistics"
|
||
},
|
||
userrights: {
|
||
visible: ["mainadmin", "rights"],
|
||
path: "/admin/rights"
|
||
}
|
||
}
|
||
},
|
||
forum: {
|
||
visible: ["mainadmin", "forum"],
|
||
path: "/admin/forum"
|
||
},
|
||
chatrooms: {
|
||
visible: ["mainadmin", "chatrooms"],
|
||
path: "/admin/chatrooms"
|
||
},
|
||
servicesStatus: {
|
||
visible: ["mainadmin"],
|
||
path: "/admin/services/status"
|
||
},
|
||
interests: {
|
||
visible: ["mainadmin", "interests"],
|
||
path: "/admin/interests"
|
||
},
|
||
falukant: {
|
||
visible: ["mainadmin", "falukant"],
|
||
children: {
|
||
logentries: {
|
||
visible: ["mainadmin", "falukant"],
|
||
path: "/admin/falukant/logentries"
|
||
},
|
||
edituser: {
|
||
visible: ["mainadmin", "falukant"],
|
||
path: "/admin/falukant/edituser"
|
||
},
|
||
database: {
|
||
visible: ["mainadmin", "falukant"],
|
||
path: "/admin/falukant/database"
|
||
},
|
||
mapEditor: {
|
||
visible: ["mainadmin", "falukant"],
|
||
path: "/admin/falukant/map"
|
||
},
|
||
}
|
||
},
|
||
minigames: {
|
||
visible: ["mainadmin", "match3"],
|
||
children: {
|
||
match3: {
|
||
visible: ["mainadmin", "match3"],
|
||
path: "/admin/minigames/match3"
|
||
},
|
||
taxiTools: {
|
||
visible: ["mainadmin", "taxi"],
|
||
path: "/admin/minigames/taxi-tools"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
class NavigationController {
|
||
constructor() {
|
||
this.menu = this.menu.bind(this);
|
||
this.vocabService = new VocabService();
|
||
}
|
||
|
||
calculateAge(birthDate) {
|
||
const today = new Date();
|
||
const birthDateObj = new Date(birthDate);
|
||
let age = today.getFullYear() - birthDateObj.getFullYear();
|
||
const monthDiff = today.getMonth() - birthDateObj.getMonth();
|
||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDateObj.getDate())) {
|
||
age--;
|
||
}
|
||
return age;
|
||
}
|
||
|
||
async filterMenu(menu, rights, age, userId) {
|
||
const filteredMenu = {};
|
||
try {
|
||
const hasFalukantAccount = await this.hasFalukantAccount(userId);
|
||
for (const [key, value] of Object.entries(menu)) {
|
||
if (value.visible.includes("all")
|
||
|| value.visible.some(v => rights.includes(v)) || (value.visible.includes("anyadmin") && rights.length > 0)
|
||
|| (value.visible.includes("over14") && age >= 14)
|
||
|| (value.visible.includes("over12") && age >= 12)
|
||
|| (value.visible.includes("over18") && age >= 18)
|
||
|| (value.visible.includes('nofalukantaccount') && !hasFalukantAccount)
|
||
|| (value.visible.includes('hasfalukantaccount') && hasFalukantAccount)) {
|
||
const { visible, ...itemWithoutVisible } = value;
|
||
filteredMenu[key] = { ...itemWithoutVisible };
|
||
if (value.children) {
|
||
filteredMenu[key].children = await this.filterMenu(value.children, rights, age, userId);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Error filtering menu:', error);
|
||
// Return empty menu if filtering fails
|
||
return {};
|
||
}
|
||
return filteredMenu;
|
||
}
|
||
|
||
async menu(req, res) {
|
||
try {
|
||
const { userid } = req.params;
|
||
const user = await User.findOne({ where: { hashedId: userid } });
|
||
if (!user) {
|
||
return res.status(404).json({ error: 'User not found' });
|
||
}
|
||
const userRights = await UserRight.findAll({
|
||
where: { userId: user.id },
|
||
include: [{
|
||
model: UserRightType,
|
||
as: 'rightType',
|
||
required: false
|
||
}]
|
||
});
|
||
const userBirthdateParams = await UserParam.findAll({
|
||
where: { userId: user.id },
|
||
include: [
|
||
{
|
||
model: UserParamType,
|
||
as: 'paramType',
|
||
where: { description: 'birthdate' }
|
||
}
|
||
]
|
||
});
|
||
const birthDate = userBirthdateParams.length > 0 ? userBirthdateParams[0].value : (new Date()).toDateString();
|
||
const age = this.calculateAge(birthDate);
|
||
const rights = userRights.map(ur => ur.rightType?.title).filter(Boolean);
|
||
const filteredMenu = await this.filterMenu(menuStructure, rights, age, user.id);
|
||
|
||
// Dynamisches Submenü: Treffpunkt → Vokabeltrainer → (Neue Sprache + abonnierte/angelegte)
|
||
// Wichtig: "Neue Sprache" soll IMMER sichtbar sein – auch wenn die DB-Abfrage (noch) fehlschlägt.
|
||
if (filteredMenu?.socialnetwork?.children?.vocabtrainer) {
|
||
const children = {
|
||
newLanguage: { path: '/socialnetwork/vocab/new' },
|
||
};
|
||
try {
|
||
const langs = await this.vocabService.listLanguagesForMenu(user.id);
|
||
for (const l of langs) {
|
||
children[`lang_${l.id}`] = { path: `/socialnetwork/vocab/${l.id}`, label: l.name };
|
||
}
|
||
} catch (e) {
|
||
console.warn('[menu] Konnte Vokabeltrainer-Sprachen nicht laden:', e?.message || e);
|
||
}
|
||
filteredMenu.socialnetwork.children.vocabtrainer.children = children;
|
||
}
|
||
|
||
res.status(200).json(filteredMenu);
|
||
} catch (error) {
|
||
console.error('Error fetching menu:', error);
|
||
// Return a minimal working menu instead of 500 error
|
||
res.status(200).json({
|
||
home: { path: "/", icon: "logo_mono.png" },
|
||
settings: { path: "/settings", icon: "settings16.png", children: {} }
|
||
});
|
||
}
|
||
}
|
||
|
||
async hasFalukantAccount(userId) {
|
||
try {
|
||
const falukantUser = await FalukantUser.findOne({ where: { userId: userId } });
|
||
return falukantUser !== null;
|
||
} catch (error) {
|
||
console.error('Error checking falukant account:', error);
|
||
return false; // Fallback: assume no falukant account if error occurs
|
||
}
|
||
}
|
||
}
|
||
|
||
export default NavigationController;
|