2023-07-14 14:15:58 +00:00
< template >
< div >
< b-row >
< b-col >
< b-table
: busy = "!raffleTableItems"
: fields = "raffleFields"
hover
: items = "raffleTableItems"
striped
primary - key = "id"
>
< template # cell ( _actions ) = " data " >
< b-button-group size = "sm" >
< b-button
v - if = "data.item.status === 'planned'"
variant = "success"
title = "Start Raffle"
@ click = "startRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'play']"
/ >
< / b-button >
< b-button
v - else - if = "data.item.status === 'active'"
variant = "warning"
title = "Close Raffle"
@ click = "closeRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'stop']"
/ >
< / b-button >
< b-button
v - else - if = "data.item.status === 'ended'"
variant = "warning"
title = "Re-Open Raffle"
@ click = "reopenRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'play']"
/ >
< / b-button >
< b-button
variant = "info"
: disabled = "data.item.status === 'planned'"
title = "Manage Entries"
@ click = "showEntryDialog(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'people-group']"
/ >
< / b-button >
< b-button
variant = "primary"
title = "Edit Raffle"
@ click = "editRaffle(data.item)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'pen']"
/ >
< / b-button >
< b-button
title = "Duplicate Raffle"
@ click = "cloneRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'clone']"
/ >
< / b-button >
2023-12-09 15:22:00 +00:00
< b-button
variant = "warning"
: disabled = "data.item.status !== 'ended'"
title = "Reset Raffle"
@ click = "resetRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'recycle']"
/ >
< / b-button >
2023-07-14 14:15:58 +00:00
< b-button
variant = "danger"
title = "Delete Raffle"
@ click = "deleteRaffle(data.item.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'minus']"
/ >
< / b-button >
< / b-button-group >
< / template >
< template # cell ( _status ) = " data " >
< font-awesome-icon
v - if = "data.item.status == 'planned'"
fixed - width
: icon = "['fas', 'pen-ruler']"
title = "Planned"
/ >
< span v -else -if = " data.item.status = = ' active ' " >
< font-awesome-icon
fixed - width
: icon = "['fas', 'spinner']"
spin - pulse
title = "In Progress"
class = "mr-1"
/ >
< small class = "text-muted" > { { raffleTimer ( data . item ) } } < / small >
< / span >
< font-awesome-icon
v - else - if = "data.item.status == 'ended'"
fixed - width
: icon = "['fas', 'stop']"
title = "Ended"
/ >
< font-awesome-icon
v - else
fixed - width
: icon = "['fas', 'question']"
/ >
< / template >
< template # head ( _actions ) = " " >
< b-button-group size = "sm" >
< b-button
variant = "success"
title = "Create New Raffle"
@ click = "newRaffle"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'plus']"
/ >
< / b-button >
< b-button
variant = "secondary"
title = "Refresh Raffle"
@ click = "fetchRaffles"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'rotate']"
/ >
< / b-button >
< / b-button-group >
< / template >
< / b-table >
< / b-col >
< / b-row >
<!-- Entries Modal -- >
< b-modal
v - if = "showRaffleEntriesModal"
scrollable
size = "lg"
: visible = "showRaffleEntriesModal"
: title = "`Entries: ${openedRaffle.title}`"
hide - footer
@ hidden = "closeEntriesModal"
>
< b-list-group
v - if = "raffleEntries.length > 0"
flush
>
< b-list-group-item class = "d-flex justify-content-between align-items-center" >
< strong > { { raffleEntries . length } } entrant ( s ) < / strong >
< b-button-group size = "sm" >
< b-button
variant = "success"
: disabled = "openedRaffle.status !== 'ended'"
@ click = "pickWinner"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'crown']"
/ >
Pick Winner
< / b-button >
< b-button
variant = "secondary"
title = "Refresh Entries"
@ click = "refreshOpenendRaffle"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'rotate']"
: spin = "openedRaffleReloading"
/ >
< / b-button >
< / b-button-group >
< / b-list-group-item >
< b-list-group-item
v - for = "entry in raffleEntries"
: key = "entry.id"
>
< div class = "d-flex align-items-center" >
< font-awesome-layers
v - if = "entry.wasPicked"
class = "mr-1"
: title = "entry.wasRedrawn ? 'Was Redrawn' : 'Has Won'"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'crown']"
: class = "entry.wasRedrawn ? 'text-muted' : 'text-warning'"
/ >
< font-awesome-icon
v - if = "entry.wasRedrawn"
fixed - width
: icon = "['fas', 'slash']"
class = "text-danger"
/ >
< / font-awesome-layers >
< font-awesome-icon
v - if = "entry.enteredAs === 'everyone'"
fixed - width
: icon = "['fas', 'user']"
title = "Does not Follow"
/ >
< font-awesome-icon
v - else - if = "entry.enteredAs === 'follower'"
fixed - width
: icon = "['fas', 'heart']"
title = "Follower"
/ >
2023-12-03 14:57:23 +00:00
< font-awesome-icon
v - else - if = "entry.enteredAs === 'reward'"
fixed - width
: icon = "['fas', 'coins']"
title = "Subscriber"
/ >
2023-07-14 14:15:58 +00:00
< font-awesome-icon
v - else - if = "entry.enteredAs === 'subscriber'"
fixed - width
: icon = "['fas', 'star']"
title = "Subscriber"
/ >
< font-awesome-icon
v - else - if = "entry.enteredAs === 'vip'"
fixed - width
: icon = "['fas', 'gem']"
title = "VIP"
/ >
< font-awesome-icon
v - else
fixed - width
: icon = "['fas', 'question']"
/ >
< span class = "ml-1" > { { entry . userDisplayName } } < / span >
< b-badge
variant = "secondary"
class = "ml-auto"
>
{ { new Date ( entry . enteredAt ) . toLocaleString ( ) } }
< / b-badge >
< / div >
< b-row >
< b-col cols = "11" >
< pre
v - if = "entry.drawResponse"
class = "mb-0 mt-2"
> < code > { { entry . drawResponse } } < / code > < / pre >
< / b-col >
< b-col cols = "1" >
< b-button
v - if = "entry.wasPicked && !entry.wasRedrawn"
variant = "danger"
size = "sm"
title = "Re-Draw Winner"
@ click = "repickWinner(entry.id)"
>
< font-awesome-icon
fixed - width
: icon = "['fas', 'recycle']"
/ >
< / b-button >
< / b-col >
< / b-row >
< / b-list-group-item >
< / b-list-group >
< p
v - else
class = "mb-0"
>
No one entered into this raffle .
< / p >
< / b-modal >
<!-- Raffle Editor -- >
< b-modal
v - if = "showRaffleEditModal"
hide - header - close
: ok - disabled = "!validateRaffle()"
ok - title = "Save"
scrollable
size = "xl"
: visible = "showRaffleEditModal"
title = "Edit Raffle"
@ hidden = "showRaffleEditModal=false"
@ ok = "saveRaffle"
>
< b-row >
<!-- Keyword / Title -- >
< b-col cols = "3" >
< b-form-group
description = "Where to do the raffle?"
label = "Channel"
label - for = "formRaffleChannel"
>
< b-input-group prepend = "#" >
< b-form-input
id = "formRaffleChannel"
v - model = "models.raffle.channel"
type = "text"
: state = "validateRaffleChannel()"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-input-group >
< / b-form-group >
< / b-col >
< b-col cols = "3" >
< b-form-group
description = "Keyword to use in chat to enter"
label = "Keyword"
label - for = "formRaffleKeyword"
>
< b-form-input
id = "formRaffleKeyword"
v - model = "models.raffle.keyword"
type = "text"
: state = "validateRaffleNonEmpty(models.raffle.keyword)"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-form-group >
< / b-col >
< b-col cols = "6" >
< b-form-group
description = "Title of the raffle (displayed in overview and available in chat)"
label = "Title"
label - for = "formRaffleTitle"
>
< b-form-input
id = "formRaffleTitle"
v - model = "models.raffle.title"
type = "text"
: state = "validateRaffleNonEmpty(models.raffle.title)"
/ >
< / b-form-group >
< / b-col >
< / b-row >
< b-row class = "mt-3" >
<!-- Allow / Multiplier / Times -- >
< b-col >
< b-card
header = "Allowed Entries"
no - body
>
< b-list-group flush >
< b-list-group-item >
< b-form-checkbox
v - model = "models.raffle.allowEveryone"
switch
: state = "validateRaffleHasAllowedEntries()"
: disabled = "models.raffle.status !== 'planned'"
>
Everyone
< / b-form-checkbox >
< / b-list-group-item >
< b-list-group-item >
< b-form
inline
class = "d-flex justify-content-between"
>
< b-form-checkbox
v - model = "models.raffle.allowFollower"
switch
: state = "validateRaffleHasAllowedEntries()"
: disabled = "models.raffle.status !== 'planned'"
>
Followers , since
< / b-form-checkbox >
< b-input-group
size = "sm"
append = "min"
class = "col-5 px-0"
>
< b-form-input
v - model = "models.raffle.minFollowAge"
class = "text-right"
type = "number"
placeholder = "min. Age"
min = "0"
: state = "validateRaffleIsNumber(models.raffle.minFollowAge)"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-input-group >
< / b-form >
< / b-list-group-item >
< b-list-group-item >
< b-form-checkbox
v - model = "models.raffle.allowSubscriber"
switch
: state = "validateRaffleHasAllowedEntries()"
: disabled = "models.raffle.status !== 'planned'"
>
Subscribers
< / b-form-checkbox >
< / b-list-group-item >
< b-list-group-item >
< b-form-checkbox
v - model = "models.raffle.allowVIP"
switch
: state = "validateRaffleHasAllowedEntries()"
: disabled = "models.raffle.status !== 'planned'"
>
VIPs
< / b-form-checkbox >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< / b-col >
< b-col >
< b-card header = "Luck Modifiers" >
< b-row >
< b-col >
< b-form-group
label = "Followers"
label - for = "formRaffleMultiFollower"
>
< b-form-input
id = "formRaffleMultiFollower"
v - model = "models.raffle.multiFollower"
type = "number"
min = "0"
step = "0.1"
size = "sm"
: state = "validateRaffleIsNumber(models.raffle.multiFollower)"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-form-group >
< / b-col >
< b-col >
< b-form-group
label = "Subs"
label - for = "formRaffleMultiSubs"
>
< b-form-input
id = "formRaffleMultiSubs"
v - model = "models.raffle.multiSubscriber"
type = "number"
min = "0"
step = "0.1"
size = "sm"
: state = "validateRaffleIsNumber(models.raffle.multiSubscriber)"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-form-group >
< / b-col >
< b-col >
< b-form-group
label = "VIPs"
label - for = "formRaffleMultiVIP"
>
< b-form-input
id = "formRaffleMultiVIP"
v - model = "models.raffle.multiVIP"
type = "number"
min = "0"
step = "0.1"
size = "sm"
: state = "validateRaffleIsNumber(models.raffle.multiVIP)"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-form-group >
< / b-col >
< / b-row >
< b-row >
< b-col >
< b-form-text >
The base amount of tickets for < strong > Everyone < / strong > is always < code > 1.0 < / code > . You can
increase chances for certain user groups . Groups are checked from right to left : If the user
is VIP , Sub and Follower they are assigned < strong > VIPkeyword < / strong > multiplier .
< / b-form-text >
< / b-col >
< / b-row >
<!-- -- >
< / b-card >
< / b-col >
< b-col >
< b-card
header = "Times"
no - body
>
< b-list-group flush >
< b-list-group-item class = "d-flex justify-content-between align-items-center" >
< span > Auto - Start * < / span >
< b-form-input
v - model = "models.raffle.autoStartAt"
class = "col-7"
type = "datetime-local"
size = "sm"
: min = "transformISOToDateTimeLocal(new Date())"
: disabled = "models.raffle.status !== 'planned'"
/ >
< / b-list-group-item >
< b-list-group-item class = "d-flex justify-content-between align-items-center" >
< span > Duration < / span >
< b-input-group
size = "sm"
append = "min"
class = "col-5 px-0"
>
< b-form-input
v - model = "models.raffle.closeAfter"
type = "number"
step = "1"
min = "0"
class = "text-right"
: state = "validateRaffleIsNumber(models.raffle.closeAfter)"
/ >
< / b-input-group >
< / b-list-group-item >
< b-list-group-item class = "d-flex justify-content-between align-items-center" >
< span > Close At * < / span >
< b-form-input
v - model = "models.raffle.closeAt"
class = "col-7"
type = "datetime-local"
size = "sm"
: min = "transformISOToDateTimeLocal(new Date())"
/ >
< / b-list-group-item >
< b-list-group-item class = "d-flex justify-content-between align-items-center" >
< span > Respond in < / span >
< b-input-group
size = "sm"
append = "sec"
class = "col-5 px-0"
>
< b-form-input
v - model = "models.raffle.waitForResponse"
type = "number"
step = "1"
min = "0"
class = "text-right"
: state = "validateRaffleIsNumber(models.raffle.waitForResponse)"
/ >
< / b-input-group >
< / b-list-group-item >
< b-list-group-item >
< b-form-text >
* Optional , when < strong > Close At < / strong > is specified , < strong > Duration < / strong > is ignored ,
when no < strong > Auto - Start < / strong > is specified the raffle must be started manually . If you
set < strong > Respond In < / strong > to 0 , no chat responses from that user are recorded after
picking them as winner .
< / b-form-text >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< / b-col >
< / b-row >
< b-row class = "mt-3" >
<!-- Texts / Entries -- >
< b-col >
< b-card
header = "Texts"
no - body
>
< b-list-group flush >
< b-list-group-item class = "" >
< div class = "d-flex align-items-center mb-1" >
< b-form-checkbox
v - model = "models.raffle.textEntryPost"
switch
class = "mr-n2"
>
Message on successful entry
< / b-form-checkbox >
< / div >
< template-editor
v - model = "models.raffle.textEntry"
/ >
< / b-list-group-item >
< b-list-group-item class = "" >
< div class = "d-flex align-items-center mb-1" >
< b-form-checkbox
v - model = "models.raffle.textEntryFailPost"
switch
class = "mr-n2"
>
Message on failed entry
< / b-form-checkbox >
< / div >
< template-editor
v - model = "models.raffle.textEntryFail"
/ >
< / b-list-group-item >
< b-list-group-item class = "" >
< div class = "d-flex align-items-center mb-1" >
< b-form-checkbox
v - model = "models.raffle.textWinPost"
switch
class = "mr-n2"
>
Message on winner draw
< / b-form-checkbox >
< / div >
< template-editor
v - model = "models.raffle.textWin"
/ >
< / b-list-group-item >
< b-list-group-item class = "" >
< div class = "d-flex justify-content-between align-items-center mb-1" >
< b-form-checkbox
v - model = "models.raffle.textReminderPost"
switch
class = "mr-n2"
>
Periodic reminder
< / b-form-checkbox >
< b-input-group
prepend = "every"
append = "min"
size = "sm"
class = "col-2 px-0"
>
< b-form-input
v - model = "models.raffle.textReminderInterval"
type = "number"
step = "1"
min = "0"
class = "text-right"
: state = "validateRaffleIsNumber(models.raffle.textReminderInterval)"
/ >
< / b-input-group >
< / div >
< template-editor
v - model = "models.raffle.textReminder"
/ >
< / b-list-group-item >
< b-list-group-item class = "" >
< div class = "d-flex align-items-center mb-1" >
< b-form-checkbox
v - model = "models.raffle.textClosePost"
switch
class = "mr-n2"
>
Message on raffle close
< / b-form-checkbox >
< / div >
< template-editor
v - model = "models.raffle.textClose"
/ >
< / b-list-group-item >
< b-list-group-item >
< b-form-text >
Available variables are < code > . user < / code > and < code > . raffle < / code > with
access to all of these configurations most notably < code > . raffle . Title < / code >
and < code > . raffle . Keyword < / code > .
< / b-form-text >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< / b-col >
< / b-row >
< / b-modal >
< / div >
< / template >
< script >
import * as constants from './const.js'
import axios from 'axios'
import TemplateEditor from './tplEditor.vue'
const ONE _MINUTE = 60000000000 // nanoseconds
const ONE _SECOND = 1000000000 // nanoseconds
export default {
components : { TemplateEditor } ,
computed : {
raffleEntries ( ) {
const entries = [ ... this . openedRaffle . entries || [ ] ]
entries . sort ( ( a , b ) => {
const isWinner = e => e . wasPicked && ! e . wasRedrawn
const wasWinnner = e => e . wasPicked && e . wasRedrawn
if ( isWinner ( a ) && ! isWinner ( b ) ) {
return - 1
} else if ( isWinner ( b ) && ! isWinner ( a ) ) {
return 1
} else if ( wasWinnner ( a ) && ! wasWinnner ( b ) ) {
return - 1
} else if ( wasWinnner ( b ) && ! wasWinnner ( a ) ) {
return 1
}
// Everything else: Order by ID DESC
return b . id - a . id
} )
return entries
} ,
raffleTableItems ( ) {
const raffles = [ ... this . raffles ]
raffles . sort ( ( a , b ) => {
const scores = { active : 0 , ended : 2 , planned : 1 }
if ( scores [ a . status ] !== scores [ b . status ] ) {
return scores [ a . status ] - scores [ b . status ]
}
return b . id - a . id
} )
return raffles
} ,
} ,
data ( ) {
return {
models : { raffle : { } } ,
now : new Date ( ) ,
openedRaffle : { } ,
openedRaffleReloading : false ,
raffleFields : [
{
class : 'col-1' ,
key : '_status' ,
label : 'Status' ,
tdClass : 'text-center align-middle' ,
thClass : 'align-middle text-center' ,
} ,
{
class : 'col-1' ,
key : 'channel' ,
label : 'Channel' ,
tdClass : 'align-middle' ,
thClass : 'align-middle' ,
} ,
{
class : 'col-1' ,
key : 'keyword' ,
label : 'Keyword' ,
tdClass : 'align-middle' ,
thClass : 'align-middle' ,
} ,
{
class : 'col-7' ,
key : 'title' ,
label : 'Title' ,
tdClass : 'align-middle' ,
thClass : 'align-middle' ,
} ,
{
class : 'col-2 text-right' ,
key : '_actions' ,
label : '' ,
tdClass : 'align-middle' ,
thClass : 'align-middle' ,
} ,
] ,
raffles : [ ] ,
reopenRaffleDuration : 60 ,
showRaffleEditModal : false ,
showRaffleEntriesModal : false ,
}
} ,
methods : {
cloneRaffle ( id ) {
return axios . put ( ` raffle/ ${ id } /clone ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle cloned' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
closeEntriesModal ( ) {
this . openedRaffle = { }
this . showRaffleEntriesModal = false
} ,
closeRaffle ( id ) {
this . $bvModal . msgBoxConfirm ( 'Do you really want to close entries for this raffle?' , {
buttonSize : 'sm' ,
cancelTitle : 'NO' ,
centered : true ,
okTitle : 'YES' ,
okVariant : 'danger' ,
size : 'sm' ,
title : 'Please Confirm' ,
} )
. then ( val => {
if ( ! val ) {
return
}
return axios . put ( ` raffle/ ${ id } /close ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle closed' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} )
} ,
deleteRaffle ( id ) {
this . $bvModal . msgBoxConfirm ( 'Do you really want to delete this raffle and all its entries?' , {
buttonSize : 'sm' ,
cancelTitle : 'NO' ,
centered : true ,
okTitle : 'YES' ,
okVariant : 'danger' ,
size : 'sm' ,
title : 'Please Confirm' ,
} )
. then ( val => {
if ( ! val ) {
return
}
return axios . delete ( ` raffle/ ${ id } ` , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle deleted' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} )
} ,
editRaffle ( raffle ) {
this . $set ( this . models , 'raffle' , raffle )
this . showRaffleEditModal = true
} ,
fetchRaffles ( ) {
return axios . get ( 'raffle/' , this . $root . axiosOptions )
. then ( resp => {
this . raffles = resp . data . map ( raffle => this . transformRaffleFromDB ( raffle ) )
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
newRaffle ( ) {
this . $set ( this . models , 'raffle' , {
/* eslint-disable sort-keys */
channel : '' ,
keyword : '!enter' ,
status : 'planned' ,
title : '' ,
allowEveryone : false ,
allowFollower : true ,
allowSubscriber : true ,
allowVIP : true ,
minFollowAge : 0 ,
multiFollower : 1.0 ,
multiSubscriber : 1.0 ,
multiVIP : 1.0 ,
autoStartAt : null ,
closeAfter : 0 ,
closeAt : null ,
waitForResponse : 30 ,
textClose : 'The raffle "{{ .raffle.Title }}" is closed now, you can no longer enter!' ,
textClosePost : false ,
textEntry : '{{ mention .user }} you are registered, good luck!' ,
textEntryPost : true ,
textEntryFail : '{{ mention .user }} couldn\'t register you, sorry.' ,
textEntryFailPost : false ,
textWin : '{{ mention .user }} you won! Please speak up in chat to claim your price!' ,
textWinPost : false ,
textReminder : 'We are currently doing a raffle until {{ dateInZone "15:04" .raffle.CloseAt "Europe/Berlin" }}: "{{ .raffle.Title }}" - type "{{ .raffle.Keyword }}" to enter!' ,
textReminderInterval : 15 ,
textReminderPost : false ,
/* eslint-enable sort-keys */
} )
this . showRaffleEditModal = true
} ,
pickWinner ( ) {
this . openedRaffleReloading = true
return axios . put ( ` raffle/ ${ this . openedRaffle . id } /pick ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Winner picked!' ) )
. catch ( ( ) => this . $root . toastError ( 'Could not pick winner!' ) )
} ,
raffleTimer ( raffle ) {
const parts = [ ]
let tte = new Date ( raffle . closeAt ) - this . now
for ( const d of [ 3600000 , 60000 , 1000 ] ) {
const pt = Math . floor ( tte / d )
parts . push ( String ( pt ) . padStart ( 2 , '0' ) )
tte -= pt * d
}
return parts . join ( ':' )
} ,
refreshOpenendRaffle ( ) {
this . openedRaffleReloading = true
return axios . get ( ` raffle/ ${ this . openedRaffle . id } ` , this . $root . axiosOptions )
. then ( resp => {
this . openedRaffle = resp . data
this . openedRaffleReloading = false
} )
} ,
reopenRaffle ( raffleId ) {
let duration = 10
const h = this . $createElement
const content = h ( 'div' , { } , [
h ( 'b-input-group' , { props : { append : 'min' } } , [
h ( 'b-form-input' , {
class : 'text-right' ,
on : {
input ( value ) {
duration = Number ( value )
} ,
} ,
props : {
min : '0' ,
step : '1' ,
type : 'number' ,
value : duration ,
} ,
} ) ,
] ) ,
h ( 'b-form-text' , { domProps : { innerHTML : 'The raffle will be re-opened and the "Close At" attribute will be set to the given duration from now.' } } ) ,
h ( 'b-form-text' , { domProps : { innerHTML : 'This will <strong>NOT</strong> clear the entrants, so don\'t use this for another raffle, use the "Duplicate Raffle" functionality for that.' } } ) ,
] )
this . $bvModal . msgBoxConfirm ( [ content ] , {
buttonSize : 'sm' ,
centered : true ,
size : 'sm' ,
title : 'Re-Open the Raffle?' ,
} )
. then ( val => {
if ( ! val ) {
return
}
return axios . put ( ` raffle/ ${ raffleId } /reopen?duration= ${ duration * 60 } ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle re-opened' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} )
} ,
repickWinner ( winnerId ) {
this . openedRaffleReloading = true
return axios . put ( ` raffle/ ${ this . openedRaffle . id } /repick/ ${ winnerId } ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Winner re-picked!' ) )
. catch ( ( ) => this . $root . toastError ( 'Could not re-pick winner!' ) )
} ,
2023-12-09 15:22:00 +00:00
resetRaffle ( id ) {
this . $bvModal . msgBoxConfirm ( 'Do you really want to reset this raffle?' , {
buttonSize : 'sm' ,
cancelTitle : 'NO' ,
centered : true ,
okTitle : 'YES' ,
okVariant : 'danger' ,
size : 'sm' ,
title : 'Please Confirm' ,
} )
. then ( val => {
if ( ! val ) {
return
}
return axios . put ( ` raffle/ ${ id } /reset ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle reset' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} )
} ,
2023-07-14 14:15:58 +00:00
saveRaffle ( ) {
if ( this . models . raffle . id ) {
return axios . put ( ` raffle/ ${ this . models . raffle . id } ` , this . transformRaffleToDB ( this . models . raffle ) , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle updated' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
}
return axios . post ( 'raffle/' , this . transformRaffleToDB ( this . models . raffle ) , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle created' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
showEntryDialog ( raffleId ) {
this . openedRaffle = { id : raffleId }
return this . refreshOpenendRaffle ( )
. then ( ( ) => {
this . showRaffleEntriesModal = true
} )
} ,
startRaffle ( id ) {
return axios . put ( ` raffle/ ${ id } /start ` , { } , this . $root . axiosOptions )
. then ( ( ) => this . $root . toastSuccess ( 'Raffle started' ) )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
transformISOToDateTimeLocal ( t ) {
const d = new Date ( t )
d . setMinutes ( d . getMinutes ( ) - d . getTimezoneOffset ( ) )
return d . toISOString ( ) . slice ( 0 , 16 )
} ,
transformRaffleFromDB ( raffle ) {
return {
/* eslint-disable sort-keys */
... raffle ,
channel : raffle . channel . replace ( /^#/ , '' ) ,
// Transform durations
closeAfter : raffle . closeAfter / ONE _MINUTE ,
minFollowAge : raffle . minFollowAge / ONE _MINUTE ,
textReminderInterval : raffle . textReminderInterval / ONE _MINUTE ,
waitForResponse : raffle . waitForResponse / ONE _SECOND ,
// Transform date values
autoStartAt : raffle . autoStartAt ? this . transformISOToDateTimeLocal ( raffle . autoStartAt ) : '' ,
closeAt : raffle . closeAt ? this . transformISOToDateTimeLocal ( raffle . closeAt ) : '' ,
/* eslint-enable sort-keys */
}
} ,
transformRaffleToDB ( raffle ) {
return {
/* eslint-disable sort-keys */
... raffle ,
channel : ` # ${ raffle . channel . replace ( /^#/ , '' ) } ` ,
// Transform durations
closeAfter : Number ( raffle . closeAfter ) * ONE _MINUTE ,
minFollowAge : Number ( raffle . minFollowAge ) * ONE _MINUTE ,
textReminderInterval : Number ( raffle . textReminderInterval ) * ONE _MINUTE ,
waitForResponse : Number ( raffle . waitForResponse ) * ONE _SECOND ,
// Transform date values
autoStartAt : ! raffle . autoStartAt ? null : new Date ( raffle . autoStartAt ) . toISOString ( ) ,
closeAt : ! raffle . closeAt ? null : new Date ( raffle . closeAt ) . toISOString ( ) ,
// Transform numeric values
multiFollower : Number ( raffle . multiFollower ) ,
multiSubscriber : Number ( raffle . multiSubscriber ) ,
multiVIP : Number ( raffle . multiVIP ) ,
/* eslint-enable sort-keys */
}
} ,
validateRaffle ( ) {
if ( this . models . raffle . status === 'ended' ) {
// You must not modify running raffles
return false
}
for ( const nf of [
'closeAfter' , 'minFollowAge' , 'textReminderInterval' , 'waitForResponse' ,
'multiFollower' , 'multiSubscriber' , 'multiVIP' ,
] ) {
// You must not put text in numeric fields
if ( this . validateRaffleIsNumber ( this . models . raffle [ nf ] ) === false ) {
return false
}
}
if ( this . validateRaffleChannel ( ) === false ) {
return false
}
for ( const nef of [ 'keyword' , 'title' ] ) {
if ( this . validateRaffleNonEmpty ( this . models . raffle [ nef ] ) === false ) {
// You must fill certain fields
return false
}
}
if ( ! this . models . raffle . closeAfter && ! this . models . raffle . closeAt ) {
return false
}
if ( this . validateRaffleHasAllowedEntries ( ) === false ) {
// You must allow someone to enter
return false
}
return true
} ,
validateRaffleChannel ( ) {
if ( ! /^[a-zA-Z0-9]{4,25}$/ . test ( this . models . raffle . channel ) ) {
return false
}
return null
} ,
validateRaffleHasAllowedEntries ( ) {
if ( this . models . raffle . allowEveryone || this . models . raffle . allowFollower || this . models . raffle . allowSubscriber || this . models . raffle . allowVIP ) {
return null
}
return false
} ,
validateRaffleIsNumber ( n ) {
if ( isNaN ( Number ( n ) ) ) {
return false
}
return null
} ,
validateRaffleNonEmpty ( str ) {
if ( ! str || str . trim ( ) . length === 0 ) {
return false
}
return null
} ,
} ,
mounted ( ) {
this . fetchRaffles ( )
this . $bus . $on ( 'raffleChanged' , ( ) => {
this . fetchRaffles ( )
if ( this . openedRaffle ? . id ) {
this . refreshOpenendRaffle ( )
}
} )
this . $bus . $on ( 'raffleEntryChanged' , ( ) => {
if ( ! this . openedRaffle ? . id ) {
// We ignore this when there is no opened raffle
return
}
this . refreshOpenendRaffle ( )
} )
window . setInterval ( ( ) => {
this . now = new Date ( )
} , 1000 )
} ,
name : 'TwitchBotEditorAppRaffle' ,
}
< / script >