Added AND/OR toggle to alerts.
This commit is contained in:
		
							parent
							
								
									238dce78b5
								
							
						
					
					
						commit
						0369446681
					
				|  | @ -1,6 +1,7 @@ | |||
| <template> | ||||
| 	<router-link | ||||
| 	<RouterLink | ||||
| 		:to="`/actor/${actor.id}/${actor.slug}`" | ||||
| 		:target="target" | ||||
| 		class="actor nolink" | ||||
| 	> | ||||
| 		<div class="avatar"> | ||||
|  | @ -18,7 +19,7 @@ | |||
| 		</div> | ||||
| 
 | ||||
| 		<span class="name">{{ actor.name }}</span> | ||||
| 	</router-link> | ||||
| 	</RouterLink> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|  | @ -36,6 +37,10 @@ export default { | |||
| 			type: Object, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		target: { | ||||
| 			type: String, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		unstashActor, | ||||
|  |  | |||
|  | @ -14,7 +14,17 @@ | |||
| 		> | ||||
| 			<div class="dialog-section"> | ||||
| 				<h3 class="dialog-heading"> | ||||
| 					When<span class="dialog-description">All to appear in the same scene</span> | ||||
| 					When | ||||
| 
 | ||||
| 					<label class="dialog-description noselect"> | ||||
| 						<template v-if="all">Scene must match <strong>all</strong> fields</template> | ||||
| 						<template v-else>Scene must match <strong>any</strong> field</template> | ||||
| 
 | ||||
| 						<Toggle | ||||
| 							:checked="all" | ||||
| 							@change="(checked) => all = checked" | ||||
| 						/> | ||||
| 					</label> | ||||
| 				</h3> | ||||
| 
 | ||||
| 				<div class="alert-section"> | ||||
|  | @ -34,7 +44,10 @@ | |||
| 							:key="`actor-${actor.id}`" | ||||
| 							class="actor" | ||||
| 						> | ||||
| 							<ActorPreview :actor="actor" /> | ||||
| 							<ActorPreview | ||||
| 								:actor="actor" | ||||
| 								target="_blank" | ||||
| 							/> | ||||
| 
 | ||||
| 							<Icon | ||||
| 								icon="cross3" | ||||
|  | @ -109,10 +122,15 @@ | |||
| 
 | ||||
| 					<div class="entities"> | ||||
| 						<div | ||||
| 							v-if="entity" | ||||
| 							v-for="(entity, index) in entities" | ||||
| 							:key="`entity-${entity.id}`" | ||||
| 							:class="{ invalid: all && index > 0 }" | ||||
| 							class="entity" | ||||
| 						> | ||||
| 							<Entity :entity="entity" /> | ||||
| 							<Entity | ||||
| 								:entity="entity" | ||||
| 								target="_blank" | ||||
| 							/> | ||||
| 
 | ||||
| 							<Icon | ||||
| 								icon="cross3" | ||||
|  | @ -121,7 +139,7 @@ | |||
| 							/> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<Tooltip v-if="!entity"> | ||||
| 						<Tooltip v-if="entities.length < 1 || !all"> | ||||
| 							<div class="entity placeholder"> | ||||
| 								Any channel | ||||
| 
 | ||||
|  | @ -278,6 +296,7 @@ | |||
| import ActorPreview from '../actors/preview.vue'; | ||||
| import Entity from '../entities/tile.vue'; | ||||
| import Checkbox from '../form/checkbox.vue'; | ||||
| import Toggle from '../form/toggle.vue'; | ||||
| import Search from './search.vue'; | ||||
| 
 | ||||
| async function addAlert() { | ||||
|  | @ -285,10 +304,11 @@ async function addAlert() { | |||
| 
 | ||||
| 	try { | ||||
| 		await this.$store.dispatch('addAlert', { | ||||
| 			all: this.all, | ||||
| 			actors: this.actors.map((actor) => actor.id), | ||||
| 			tags: this.tags.map((tag) => tag.id), | ||||
| 			matches: this.matches, | ||||
| 			entity: this.entity?.id, | ||||
| 			entities: this.entities.map((entity) => entity.id), | ||||
| 			notify: this.notify, | ||||
| 			email: this.email, | ||||
| 			stashes: this.stashes.map((stash) => stash.id), | ||||
|  | @ -309,7 +329,7 @@ function addActor(actor) { | |||
| } | ||||
| 
 | ||||
| function addEntity(entity) { | ||||
| 	this.entity = entity; | ||||
| 	this.entities = this.entities.concat(entity); | ||||
| 	this.events.emit('blur'); | ||||
| } | ||||
| 
 | ||||
|  | @ -325,8 +345,8 @@ function removeActor(actor) { | |||
| 	this.actors = this.actors.filter((listedActor) => listedActor.id !== actor.id); | ||||
| } | ||||
| 
 | ||||
| function removeEntity() { | ||||
| 	this.entity = null; | ||||
| function removeEntity(entity) { | ||||
| 	this.entities = this.entities.filter((alertEntity) => alertEntity.id !== entity.id); | ||||
| } | ||||
| 
 | ||||
| function removeTag(tag) { | ||||
|  | @ -371,6 +391,7 @@ export default { | |||
| 		Checkbox, | ||||
| 		Entity, | ||||
| 		Search, | ||||
| 		Toggle, | ||||
| 	}, | ||||
| 	emits: ['close'], | ||||
| 	data() { | ||||
|  | @ -378,7 +399,8 @@ export default { | |||
| 			error: null, | ||||
| 			actors: [], | ||||
| 			tags: [], | ||||
| 			entity: null, | ||||
| 			all: true, | ||||
| 			entities: [], | ||||
| 			matches: [], | ||||
| 			matchProperty: 'title', | ||||
| 			matchExpression: null, | ||||
|  | @ -424,9 +446,15 @@ export default { | |||
| } | ||||
| 
 | ||||
| .dialog-description { | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	color: var(--shadow); | ||||
| 	font-size: .9rem; | ||||
| 	font-weight: normal; | ||||
| 
 | ||||
| 	.toggle-container { | ||||
| 		margin-left: .5rem; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .dialog-error { | ||||
|  | @ -488,6 +516,11 @@ export default { | |||
| 	margin: 0 .5rem .5rem 0; | ||||
| } | ||||
| 
 | ||||
| .entity.invalid { | ||||
| 	opacity: .5; | ||||
| 	pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .entity .tile { | ||||
| 	width: 10rem; | ||||
| 	height: 2.5rem; | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| <template> | ||||
| 	<router-link | ||||
| 	<RouterLink | ||||
| 		:to="`/${entity.type}/${entity.slug}`" | ||||
| 		:title="entity.name" | ||||
| 		:target="target" | ||||
| 		class="tile" | ||||
| 	> | ||||
| 		<div class="tile-logo"> | ||||
|  | @ -47,7 +48,7 @@ | |||
| 			<span v-if="typeof entity.sceneTotal !== 'undefined'">{{ entity.sceneTotal }} scenes</span> | ||||
| 			<span v-if="entity.type === 'network'">{{ entity.childrenTotal }} channels</span> | ||||
| 		</span> | ||||
| 	</router-link> | ||||
| 	</RouterLink> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
|  | @ -57,6 +58,10 @@ export default { | |||
| 			type: Object, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		target: { | ||||
| 			type: String, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 	}, | ||||
| 	emits: ['load'], | ||||
| }; | ||||
|  |  | |||
|  | @ -99,7 +99,7 @@ | |||
| 
 | ||||
| 						<Icon | ||||
| 							v-if="notification.alert" | ||||
| 							v-tooltip="`You set an alert for <strong>${notification.alert.tags.map(tag => tag.name).join(', ') || 'all'}</strong> scenes with <strong>${notification.alert.actors.map(actor => actor.name).join(', ') || 'any actor'}</strong> from <strong>${notification.alert.entities.map((entity) => entity.name).join(', ') || 'any channel'}</strong> matching <strong>${notification.alert.matches.map((match) => `${match.property}: ${match.expression}`).join(', ') || 'anything'}</strong>`" | ||||
| 							v-tooltip="`You set an alert for scenes with <strong>${notification.alert.all ? 'all of' : 'any of'}</strong> <strong>${notification.alert.actors.map(actor => actor.name).join(', ') || 'any actor'}</strong> containing <strong>${notification.alert.tags.map(tag => tag.name).join(', ') || 'any tags'}</strong> from <strong>${notification.alert.entities.map((entity) => entity.name).join(', ') || 'any channel'}</strong> matching <strong>${notification.alert.matches.map((match) => `${match.property}: ${match.expression}`).join(', ') || 'any text'}</strong>`" | ||||
| 							icon="question5" | ||||
| 							@click.prevent.stop | ||||
| 						/> | ||||
|  |  | |||
|  | @ -68,6 +68,7 @@ function initUiActions(store, _router) { | |||
| 							${releaseFields} | ||||
| 						} | ||||
| 						alert { | ||||
| 							all | ||||
| 							tags: alertsTags { | ||||
| 								tag { | ||||
| 									id | ||||
|  |  | |||
|  | @ -71,6 +71,7 @@ function initUsersActions(store, _router) { | |||
| 						id | ||||
| 						notify | ||||
| 						email | ||||
| 						all | ||||
| 						stashes: alertsStashes { | ||||
| 							stash { | ||||
| 								id | ||||
|  |  | |||
|  | @ -34,5 +34,9 @@ exports.down = async (knex) => { | |||
| 		table.dropColumn('all'); | ||||
| 	}); | ||||
| 
 | ||||
| 	await knex.schema.alterTable('alerts_entities', (table) => { | ||||
| 		table.unique('alert_id'); | ||||
| 	}); | ||||
| 
 | ||||
| 	await knex.schema.dropTable('alerts_matches'); | ||||
| }; | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ async function addAlert(alert, sessionUser) { | |||
| 			user_id: sessionUser.id, | ||||
| 			notify: alert.notify, | ||||
| 			email: alert.email, | ||||
| 			all: alert.all, | ||||
| 		}) | ||||
| 		.returning('id'); | ||||
| 
 | ||||
|  | @ -45,10 +46,10 @@ async function addAlert(alert, sessionUser) { | |||
| 				alert_id: alertId, | ||||
| 				stash_id: stashId, | ||||
| 			})), false), | ||||
| 		alert.entity && bulkInsert('alerts_entities', [{ | ||||
| 		alert.entities && bulkInsert('alerts_entities', alert.entities.map((entityId) => ({ | ||||
| 			alert_id: alertId, | ||||
| 			entity_id: alert.entity, | ||||
| 		}], false), | ||||
| 			entity_id: entityId, | ||||
| 		})).slice(0, alert.all ? 1 : Infinity), false), // one scene can never match multiple entities in AND mode
 | ||||
| 	]); | ||||
| 
 | ||||
| 	return alertId; | ||||
|  | @ -121,8 +122,6 @@ async function notify(scenes) { | |||
| 		return acc; | ||||
| 	}, {}); | ||||
| 
 | ||||
| 	console.log(alertsStashesByAlertId); | ||||
| 
 | ||||
| 	const alerts = rawAlerts.map((alert) => ({ | ||||
| 		id: alert.id, | ||||
| 		userId: alert.user_id, | ||||
|  | @ -148,32 +147,45 @@ async function notify(scenes) { | |||
| 
 | ||||
| 	const triggers = alerts.flatMap((alert) => { | ||||
| 		const alertScenes = curatedScenes.filter((scene) => { | ||||
| 			console.log(scene.title, alert.tags, scene.tagIds); | ||||
| 			if (alert.all) { | ||||
| 				if (alert.actors.length > 0 && !alert.actors.every((actorId) => scene.actorIds.includes(actorId))) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 			if (alert.actors.length > 0 && !alert.actors.every((actorId) => scene.actorIds.includes(actorId))) { | ||||
| 				console.log('THROW ACTORS'); | ||||
| 				return false; | ||||
| 				if (alert.tags.length > 0 && !alert.tags.every((tagId) => scene.tagIds.includes(tagId))) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				// multiple entities can only be matched in OR mode
 | ||||
| 				if (alert.entities.length > 0 && !alert.entities.some((alertEntityId) => alertEntityId === scene.entityId || alertEntityId === scene.parentEntityId)) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				if (alert.matches.length > 0 && !alert.matches.every((match) => match.expression.test(scene[match.property]))) { | ||||
| 					return false; | ||||
| 				} | ||||
| 
 | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (alert.tags.length > 0 && !alert.tags.every((tagId) => scene.tagIds.includes(tagId))) { | ||||
| 				console.log('THROW TAGS'); | ||||
| 				return false; | ||||
| 			if (alert.actors.some((actorId) => scene.actorIds.includes(actorId))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (alert.tags.some((tagId) => scene.tagIds.includes(tagId))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			// multiple entities can only be matched in OR mode
 | ||||
| 			if (alert.entities.length > 0 && !alert.entities.some((alertEntityId) => alertEntityId === scene.entityId || alertEntityId === scene.parentEntityId)) { | ||||
| 				console.log('THROW ENTITIES'); | ||||
| 				return false; | ||||
| 			if (alert.entities.some((alertEntityId) => alertEntityId === scene.entityId || alertEntityId === scene.parentEntityId)) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			if (alert.matches.length > 0 && !alert.matches.every((match) => match.expression.test(scene[match.property]))) { | ||||
| 				console.log('THROW MATCHES'); | ||||
| 				return false; | ||||
| 			if (alert.matches.some((match) => match.expression.test(scene[match.property]))) { | ||||
| 				return true; | ||||
| 			} | ||||
| 
 | ||||
| 			console.log('OK'); | ||||
| 
 | ||||
| 			return true; | ||||
| 			return false; | ||||
| 		}); | ||||
| 
 | ||||
| 		return alertScenes.map((scene) => ({ | ||||
|  | @ -191,15 +203,11 @@ async function notify(scenes) { | |||
| 			scene_id: trigger.sceneId, | ||||
| 		})); | ||||
| 
 | ||||
| 	console.log('triggers', triggers); | ||||
| 
 | ||||
| 	const stashes = Object.values(Object.fromEntries(triggers.flatMap((trigger) => trigger.alert.stashes.map((stashId) => ({ | ||||
| 		scene_id: trigger.sceneId, | ||||
| 		stash_id: stashId, | ||||
| 	}))).map((stash) => [`${stash.stash_id}:${stash.scene_id}`, stash]))); | ||||
| 
 | ||||
| 	console.log('stashes', stashes); | ||||
| 
 | ||||
| 	await Promise.all([ | ||||
| 		bulkInsert('notifications', notifications, false), | ||||
| 		bulkInsert('stashes_scenes', stashes, false), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue