mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 19:15:58 +01:00 
			
		
		
		
	feat: closes #12158, add sortable rewards
convert rewards:list to a sorted set
This commit is contained in:
		| @@ -1,10 +1,12 @@ | ||||
| 'use strict'; | ||||
|  | ||||
|  | ||||
| define('admin/extend/rewards', ['alerts'], function (alerts) { | ||||
| define('admin/extend/rewards', [ | ||||
| 	'alerts', | ||||
| 	'jquery-ui/widgets/sortable', | ||||
| ], function (alerts) { | ||||
| 	const rewards = {}; | ||||
|  | ||||
|  | ||||
| 	let available; | ||||
| 	let active; | ||||
| 	let conditions; | ||||
| @@ -45,6 +47,11 @@ define('admin/extend/rewards', ['alerts'], function (alerts) { | ||||
| 				btn.addClass('hidden'); | ||||
| 				// send disable api call | ||||
| 				return false; | ||||
| 			}) | ||||
| 			.sortable({ | ||||
| 				handle: '[component="sort/handle"]', | ||||
| 				axis: 'y', | ||||
| 				zIndex: 9999, | ||||
| 			}); | ||||
|  | ||||
| 		$('#new').on('click', newReward); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ const utils = require('../utils'); | ||||
| const rewards = module.exports; | ||||
|  | ||||
| rewards.save = async function (data) { | ||||
| 	async function save(data) { | ||||
| 	await Promise.all(data.map(async (data, index) => { | ||||
| 		if (!Object.keys(data.rewards).length) { | ||||
| 			return; | ||||
| 		} | ||||
| @@ -17,19 +17,17 @@ rewards.save = async function (data) { | ||||
| 			data.id = await db.incrObjectField('global', 'rewards:id'); | ||||
| 		} | ||||
| 		await rewards.delete(data); | ||||
| 		await db.setAdd('rewards:list', data.id); | ||||
| 		await db.sortedSetAdd('rewards:list', index, data.id); | ||||
| 		await db.setObject(`rewards:id:${data.id}`, data); | ||||
| 		await db.setObject(`rewards:id:${data.id}:rewards`, rewardsData); | ||||
| 	} | ||||
|  | ||||
| 	await Promise.all(data.map(data => save(data))); | ||||
| 	})); | ||||
| 	await saveConditions(data); | ||||
| 	return data; | ||||
| }; | ||||
|  | ||||
| rewards.delete = async function (data) { | ||||
| 	await Promise.all([ | ||||
| 		db.setRemove('rewards:list', data.id), | ||||
| 		db.sortedSetRemove('rewards:list', data.id), | ||||
| 		db.delete(`rewards:id:${data.id}`), | ||||
| 		db.delete(`rewards:id:${data.id}:rewards`), | ||||
| 	]); | ||||
| @@ -61,7 +59,8 @@ async function saveConditions(data) { | ||||
| } | ||||
|  | ||||
| async function getActiveRewards() { | ||||
| 	async function load(id) { | ||||
| 	const rewardsList = await db.getSortedSetRange('rewards:list', 0, -1); | ||||
| 	const rewardData = await Promise.all(rewardsList.map(async (id) => { | ||||
| 		const [main, rewards] = await Promise.all([ | ||||
| 			db.getObject(`rewards:id:${id}`), | ||||
| 			db.getObject(`rewards:id:${id}:rewards`), | ||||
| @@ -71,11 +70,7 @@ async function getActiveRewards() { | ||||
| 			main.rewards = rewards; | ||||
| 		} | ||||
| 		return main; | ||||
| 	} | ||||
|  | ||||
| 	const rewardsList = await db.getSetMembers('rewards:list'); | ||||
| 	rewardsList.sort((a, b) => a - b); | ||||
| 	const rewardData = await Promise.all(rewardsList.map(id => load(id))); | ||||
| 	})); | ||||
| 	return rewardData.filter(Boolean); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/upgrades/3.6.0/rewards_zsets.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/upgrades/3.6.0/rewards_zsets.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| /* eslint-disable no-await-in-loop */ | ||||
|  | ||||
| 'use strict'; | ||||
|  | ||||
| const db = require('../../database'); | ||||
|  | ||||
| module.exports = { | ||||
| 	name: 'Convert rewards:list to a sorted set', | ||||
| 	timestamp: Date.UTC(2023, 10, 10), | ||||
| 	method: async function () { | ||||
| 		const rewards = await db.getSetMembers('rewards:list'); | ||||
| 		if (rewards.length) { | ||||
| 			rewards.sort((a, b) => a - b); | ||||
| 			await db.delete('rewards:list'); | ||||
| 			await db.sortedSetAdd( | ||||
| 				'rewards:list', | ||||
| 				rewards.map((id, index) => index), | ||||
| 				rewards.map(id => id) | ||||
| 			); | ||||
| 		} | ||||
| 	}, | ||||
| }; | ||||
| @@ -16,59 +16,64 @@ | ||||
| 		<ul id="active" class="list-unstyled p-0 m-0"> | ||||
| 			{{{ each active }}} | ||||
| 			<li data-rid="{active.rid}" data-id="{active.id}"> | ||||
| 				<div class="d-flex gap-1 mb-3 flex-wrap"> | ||||
| 					<form class="main d-flex gap-1 flex-wrap"> | ||||
| 						<div class="card card-body m-0 if-block border-info border border-2"> | ||||
| 							<label class="form-label" for="condition-if-users">[[admin/extend/rewards:condition-if-users]]</label> | ||||
| 							<select id="condition-if-users" class="form-select form-select-sm" name="condition" data-selected="{active.condition}"> | ||||
| 								{{{ each conditions }}} | ||||
| 								<option value="{conditions.condition}">{conditions.name}</option> | ||||
| 								{{{ end }}} | ||||
| 							</select> | ||||
| 						</div> | ||||
| 						<div class="card card-body m-0 this-block border-warning border border-2"> | ||||
| 							<label class="form-label" for="condition-is">[[admin/extend/rewards:condition-is]]</label> | ||||
| 							<div class="d-flex gap-1 flex-nowrap"> | ||||
| 								<select id="condition-is" class="form-select form-select-sm" name="conditional" data-selected="{active.conditional}" style="max-width: 64px;"> | ||||
| 									{{{ each  conditionals }}} | ||||
| 									<option value="{conditionals.conditional}">{conditionals.name}</option> | ||||
| 									{{{ end }}} | ||||
| 								</select> | ||||
| 				<div class="d-flex gap-1 align-items-start"> | ||||
| 					<a href="#" component="sort/handle" class="btn btn-light btn-sm d-none d-md-block" style="cursor:grab;"><i class="fa fa-arrows-up-down text-muted"></i></a> | ||||
| 					<div class="d-flex flex-column flex-grow-1"> | ||||
| 						<div class="d-flex gap-1 mb-3 flex-wrap"> | ||||
| 							<form class="main d-flex gap-1 flex-wrap"> | ||||
| 								<div class="card card-body m-0 if-block border-info border border-2"> | ||||
| 									<label class="form-label" for="condition-if-users">[[admin/extend/rewards:condition-if-users]]</label> | ||||
| 									<select id="condition-if-users" class="form-select form-select-sm" name="condition" data-selected="{active.condition}"> | ||||
| 										{{{ each conditions }}} | ||||
| 										<option value="{conditions.condition}">{conditions.name}</option> | ||||
| 										{{{ end }}} | ||||
| 									</select> | ||||
| 								</div> | ||||
| 								<div class="card card-body m-0 this-block border-warning border border-2"> | ||||
| 									<label class="form-label" for="condition-is">[[admin/extend/rewards:condition-is]]</label> | ||||
| 									<div class="d-flex gap-1 flex-nowrap"> | ||||
| 										<select id="condition-is" class="form-select form-select-sm" name="conditional" data-selected="{active.conditional}" style="max-width: 64px;"> | ||||
| 											{{{ each  conditionals }}} | ||||
| 											<option value="{conditionals.conditional}">{conditionals.name}</option> | ||||
| 											{{{ end }}} | ||||
| 										</select> | ||||
|  | ||||
| 								<input class="form-control form-control-sm" type="text" name="value" value="{active.value}" style="max-width: 128px;"/> | ||||
| 										<input class="form-control form-control-sm" type="text" name="value" value="{active.value}" style="max-width: 128px;"/> | ||||
| 									</div> | ||||
| 								</div> | ||||
| 								<div class="card card-body m-0 then-block border-primary border border-2"> | ||||
| 									<label class="form-label" for="condition-then">[[admin/extend/rewards:condition-then]]</label> | ||||
| 									<select id="condition-then" class="form-select form-select-sm" name="rid" data-selected="{active.rid}"> | ||||
| 										{{{ each ../../rewards }}} | ||||
| 										<option value="{rewards.rid}">{rewards.name}</option> | ||||
| 										{{{ end }}} | ||||
| 									</select> | ||||
| 								</div> | ||||
| 							</form> | ||||
| 							<form class="rewards flex-1"> | ||||
| 								<div class="inputs card card-body m-0 h-100 reward-block border-success border border-2"><div class="d-flex h-100 align-items-center">[[admin/extend/rewards:select-reward]]</div></div> | ||||
| 							</form> | ||||
| 						</div> | ||||
|  | ||||
| 						<div class="d-flex justify-content-between align-items-center gap-2"> | ||||
| 							<form class="main d-flex gap-1 align-items-start gap-2"> | ||||
| 								<div class="d-flex flex-column gap-0"> | ||||
| 									<label class="form-label" for="claimable">[[admin/extend/rewards:max-claims]]</label> | ||||
| 									<p class="form-text mb-0"> | ||||
| 										[[admin/extend/rewards:zero-infinite]] | ||||
| 									</p> | ||||
| 								</div> | ||||
| 								<input id="claimable" class="form-control form-control-sm" type="text" name="claimable" value="{active.claimable}" placeholder="1" style="max-width: 64px;"/> | ||||
|  | ||||
| 							</form> | ||||
| 							<div class="d-flex gap-1 align-self-start"> | ||||
| 								<button class="btn btn-light btn-sm toggle text-nowrap disable {{{ if active.disabled }}}hidden{{{ end }}}"><i class="fa fa-ban text-danger"></i> [[admin/extend/rewards:disable]]</button> | ||||
|  | ||||
| 								<button class="btn btn-light btn-sm toggle text-nowrap enable {{{ if !active.disabled }}}hidden{{{ end }}}"><i class="fa fa-check text-success"></i> [[admin/extend/rewards:enable]]</button> | ||||
|  | ||||
| 								<button class="btn btn-light btn-sm text-nowrap delete"><i class="fa fa-trash text-danger"></i> [[admin/extend/rewards:delete]]</button> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="card card-body m-0 then-block border-primary border border-2"> | ||||
| 							<label class="form-label" for="condition-then">[[admin/extend/rewards:condition-then]]</label> | ||||
| 							<select id="condition-then" class="form-select form-select-sm" name="rid" data-selected="{active.rid}"> | ||||
| 								{{{ each ../../rewards }}} | ||||
| 								<option value="{rewards.rid}">{rewards.name}</option> | ||||
| 								{{{ end }}} | ||||
| 							</select> | ||||
| 						</div> | ||||
| 					</form> | ||||
| 					<form class="rewards flex-1"> | ||||
| 						<div class="inputs card card-body m-0 h-100 reward-block border-success border border-2"><div class="d-flex h-100 align-items-center">[[admin/extend/rewards:select-reward]]</div></div> | ||||
| 					</form> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="d-flex justify-content-between align-items-center gap-2"> | ||||
| 					<form class="main d-flex gap-1 align-items-start gap-2"> | ||||
| 						<div class="d-flex flex-column gap-0"> | ||||
| 							<label class="form-label" for="claimable">[[admin/extend/rewards:max-claims]]</label> | ||||
| 							<p class="form-text mb-0"> | ||||
| 								[[admin/extend/rewards:zero-infinite]] | ||||
| 							</p> | ||||
| 						</div> | ||||
| 						<input id="claimable" class="form-control form-control-sm" type="text" name="claimable" value="{active.claimable}" placeholder="1" style="max-width: 64px;"/> | ||||
|  | ||||
| 					</form> | ||||
| 					<div class="d-flex gap-1 align-self-start"> | ||||
| 						<button class="btn btn-light btn-sm toggle text-nowrap disable {{{ if active.disabled }}}hidden{{{ end }}}"><i class="fa fa-ban text-danger"></i> [[admin/extend/rewards:disable]]</button> | ||||
|  | ||||
| 						<button class="btn btn-light btn-sm toggle text-nowrap enable {{{ if !active.disabled }}}hidden{{{ end }}}"><i class="fa fa-check text-success"></i> [[admin/extend/rewards:enable]]</button> | ||||
|  | ||||
| 						<button class="btn btn-light btn-sm text-nowrap delete"><i class="fa fa-trash text-danger"></i> [[admin/extend/rewards:delete]]</button> | ||||
| 					</div> | ||||
| 				</div> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user