Added notification bell, WIP.
This commit is contained in:
		
							parent
							
								
									dc5affb4cf
								
							
						
					
					
						commit
						342ba6191e
					
				|  | @ -91,7 +91,7 @@ | |||
| 
 | ||||
| 											<div class="result-label"> | ||||
| 												{{ actor.name }} | ||||
| 												<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].join(', ') }})</template> | ||||
| 												<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].filter(Boolean).join(', ') }})</template> | ||||
| 											</div> | ||||
| 										</li> | ||||
| 									</ul> | ||||
|  |  | |||
|  | @ -78,20 +78,42 @@ | |||
| 				</button> | ||||
| 			</form> | ||||
| 
 | ||||
| 			<VDropdown | ||||
| 			<div | ||||
| 				v-if="user" | ||||
| 				class="userpanel" | ||||
| 				:class="{ searching: searchFocused }" | ||||
| 			> | ||||
| 				<VDropdown | ||||
| 					:triggers="['click']" | ||||
| 					:prevent-overflow="true" | ||||
| 				> | ||||
| 				<div | ||||
| 					class="userpanel" | ||||
| 					:class="{ searching: searchFocused }" | ||||
| 					<Icon | ||||
| 						icon="bell2" | ||||
| 						class="notifs-bell" | ||||
| 						:class="{ unread: notifications.some((notif) => !notif.isSeen) }" | ||||
| 					/> | ||||
| 
 | ||||
| 					<template #popper> | ||||
| 						<div class="menu"> | ||||
| 							<ul class="notifs nolist"> | ||||
| 								<li | ||||
| 									v-for="notif in notifications" | ||||
| 									:key="notif.id" | ||||
| 									class="notif" | ||||
| 								>{{ notif.scene.title }}</li> | ||||
| 							</ul> | ||||
| 						</div> | ||||
| 					</template> | ||||
| 				</VDropdown> | ||||
| 
 | ||||
| 				<VDropdown | ||||
| 					:triggers="['click']" | ||||
| 					:prevent-overflow="true" | ||||
| 				> | ||||
| 					<img | ||||
| 						:src="user.avatar" | ||||
| 						class="avatar" | ||||
| 					> | ||||
| 				</div> | ||||
| 
 | ||||
| 					<template #popper> | ||||
| 						<div class="menu"> | ||||
|  | @ -141,6 +163,7 @@ | |||
| 						</div> | ||||
| 					</template> | ||||
| 				</VDropdown> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<div | ||||
| 				v-else-if="allowLogin" | ||||
|  | @ -181,11 +204,14 @@ import logo from '../../assets/img/logo.svg?raw'; // eslint-disable-line import/ | |||
| const pageContext = inject('pageContext'); | ||||
| 
 | ||||
| const user = pageContext.user; | ||||
| const notifications = pageContext.notifications; | ||||
| const query = ref(pageContext.urlParsed.search.q || ''); | ||||
| const allowLogin = pageContext.env.allowLogin; | ||||
| const searchFocused = ref(false); | ||||
| const showSettings = ref(false); | ||||
| 
 | ||||
| console.log(notifications); | ||||
| 
 | ||||
| const activePage = computed(() => pageContext.urlParsed.pathname.split('/')[1]); | ||||
| const currentPath = `${pageContext.urlParsed.pathnameOriginal}${pageContext.urlParsed.searchOriginal || ''}`; | ||||
| 
 | ||||
|  | @ -316,7 +342,7 @@ function blurSearch(event) { | |||
| 	height: 100%; | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	padding: 0 1rem 0 1.5rem; | ||||
| 	padding: 0 1rem 0 0; | ||||
| 	font-size: 0; | ||||
| 	cursor: pointer; | ||||
| 
 | ||||
|  | @ -332,6 +358,31 @@ function blurSearch(event) { | |||
| 	object-fit: cover; | ||||
| } | ||||
| 
 | ||||
| .notifs-bell { | ||||
| 	padding: 0 1.25rem; | ||||
| 	height: 100%; | ||||
| 	fill: var(--shadow); | ||||
| 
 | ||||
| 	&:hover, | ||||
| 	&.unread { | ||||
| 		cursor: pointer; | ||||
| 		fill: var(--primary); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .notifs { | ||||
| 	overflow: auto; | ||||
| } | ||||
| 
 | ||||
| .notif { | ||||
| 	display: flex; | ||||
| 	padding: .5rem 1rem; | ||||
| 
 | ||||
| 	&:not(:last-child) { | ||||
| 		border-bottom: solid 1px var(--shadow-weak-30); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .login { | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ export async function onBeforeRender(pageContext) { | |||
| 			: [], | ||||
| 	]); | ||||
| 
 | ||||
| 	console.log('out alerts', alerts); | ||||
| 	console.log(pageContext.notifications); | ||||
| 
 | ||||
| 	if (!profile) { | ||||
| 		throw render(404, `Cannot find user '${pageContext.routeParams.username}'.`); | ||||
|  |  | |||
|  | @ -6,5 +6,6 @@ export default { | |||
| 		'urlParsed', | ||||
| 		'env', | ||||
| 		'user', | ||||
| 		'notifications', | ||||
| 	], | ||||
| }; | ||||
|  |  | |||
|  | @ -140,6 +140,28 @@ export async function removeAlert(alertId, reqUser) { | |||
| 		.delete(); | ||||
| } | ||||
| 
 | ||||
| export async function fetchNotifications(reqUser, options = {}) { | ||||
| 	const notifications = await knex('notifications') | ||||
| 		.select('notifications.*', 'alerts.id as alert_id', 'scenes.title as scene_title') | ||||
| 		.leftJoin('releases as scenes', 'scenes.id', 'notifications.scene_id') | ||||
| 		.leftJoin('alerts', 'alerts.id', 'notifications.alert_id') | ||||
| 		.where('notifications.user_id', reqUser.id) | ||||
| 		.limit(options.limit || 10) | ||||
| 		.orderBy('created_at', 'desc'); | ||||
| 
 | ||||
| 	return notifications.map((notification) => ({ | ||||
| 		id: notification.id, | ||||
| 		sceneId: notification.scene_id, | ||||
| 		scene: { | ||||
| 			id: notification.scene_id, | ||||
| 			title: notification.scene_title, | ||||
| 		}, | ||||
| 		alertId: notification.alert_id, | ||||
| 		isSeen: notification.seen, | ||||
| 		createdAt: notification.created_at, | ||||
| 	})); | ||||
| } | ||||
| 
 | ||||
| export async function updateNotification(notificationId, updatedNotification, reqUser) { | ||||
| 	await knex('notifications') | ||||
| 		.where('id', notificationId) | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { | |||
| 	fetchAlerts, | ||||
| 	createAlert, | ||||
| 	removeAlert, | ||||
| 	fetchNotifications, | ||||
| 	updateNotifications, | ||||
| 	updateNotification, | ||||
| } from '../alerts.js'; | ||||
|  | @ -24,6 +25,14 @@ export async function removeAlertApi(req, res) { | |||
| 	res.status(204).send(); | ||||
| } | ||||
| 
 | ||||
| export async function fetchNotificationsApi(req, res) { | ||||
| 	const notifications = await fetchNotifications(req.user, { | ||||
| 		limit: req.query.limit || 10, | ||||
| 	}); | ||||
| 
 | ||||
| 	res.send(notifications); | ||||
| } | ||||
| 
 | ||||
| export async function updateNotificationsApi(req, res) { | ||||
| 	await updateNotifications(req.body, req.user); | ||||
| 
 | ||||
|  |  | |||
|  | @ -51,6 +51,8 @@ import { | |||
| 	updateNotificationsApi, | ||||
| } from './alerts.js'; | ||||
| 
 | ||||
| import { fetchNotifications } from '../alerts.js'; | ||||
| 
 | ||||
| import initLogger from '../logger.js'; | ||||
| 
 | ||||
| const logger = initLogger(); | ||||
|  | @ -164,6 +166,8 @@ export default async function initServer() { | |||
| 	router.get('/api/tags', fetchTagsApi); | ||||
| 
 | ||||
| 	router.get('*', async (req, res, next) => { | ||||
| 		const notifications = await fetchNotifications(req.user, { limit: 20 }); | ||||
| 
 | ||||
| 		const pageContextInit = { | ||||
| 			urlOriginal: req.originalUrl, | ||||
| 			urlQuery: req.query, // vike's own query does not apply boolean parser
 | ||||
|  | @ -185,6 +189,7 @@ export default async function initServer() { | |||
| 				maxAggregateSize: config.database.manticore.maxAggregateSize, | ||||
| 				media: config.media, | ||||
| 			}, | ||||
| 			notifications, | ||||
| 		}; | ||||
| 
 | ||||
| 		const pageContext = await renderPage(pageContextInit); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue