Skip to content

Commit

Permalink
Support password and verification for daily limit (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheepzh committed Jun 21, 2023
1 parent 9c78213 commit 3f43a49
Show file tree
Hide file tree
Showing 32 changed files with 1,017 additions and 342 deletions.
13 changes: 12 additions & 1 deletion src/app/components/limit/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@
* https://opensource.org/licenses/MIT
*/

import { Operation, Plus } from "@element-plus/icons-vue"
import { Operation, Plus, SetUp } from "@element-plus/icons-vue"
import { Ref, h, defineComponent, ref } from "vue"
import InputFilterItem from "@app/components/common/input-filter-item"
import SwitchFilterItem from "@app/components/common/switch-filter-item"
import ButtonFilterItem from "@app/components/common/button-filter-item"
import { t } from "@app/locale"
import { getAppPageUrl } from "@util/constant/url"
import { OPTION_ROUTE } from "@app/router/constants"
import { createTabAfterCurrent } from "@api/chrome/tab"

const urlPlaceholder = t(msg => msg.limit.conditionFilter)
const onlyEnabledLabel = t(msg => msg.limit.filterDisabled)
const addButtonText = t(msg => msg.button.create)
const testButtonText = t(msg => msg.limit.button.test)
const optionButtonText = t(msg => msg.limit.button.option)
const optionPageUrl = getAppPageUrl(false, OPTION_ROUTE, { i: 'dailyLimit' })

const emits = {
create: () => true,
Expand Down Expand Up @@ -60,6 +65,12 @@ const _default = defineComponent({
icon: Operation,
onClick: () => ctx.emit('test')
}),
h(ButtonFilterItem, {
text: optionButtonText,
icon: SetUp,
type: 'primary',
onClick: () => createTabAfterCurrent(optionPageUrl)
}),
h(ButtonFilterItem, {
text: addButtonText,
type: "success",
Expand Down
68 changes: 68 additions & 0 deletions src/app/components/limit/table/column/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { t, tN } from "@app/locale"
import { locale } from "@i18n"
import { VerificationPair } from "@service/limit-service/verification/common"
import verificationProcessor from "@service/limit-service/verification/processor"
import { ElMessageBox, ElMessage } from "element-plus"
import { h, VNode } from "vue"

/**
* Judge wether verification is required
*
* @returns T/F
*/
export function judgeVerificationRequired(item: timer.limit.Item): boolean {
const { waste, time } = item || {}
return !!(waste > time * 1000)
}

const PROMT_TXT_CSS: Partial<CSSStyleDeclaration> = {
userSelect: 'none',
}

/**
* @returns null if verification not required,
* or promise with resolve invocked only if verification code or password correct
*/
export async function processVerification(option: timer.option.DailyLimitOption): Promise<void> {
const { limitLevel, limitPassword, limitVerifyDifficulty } = option
let answerValue: string
let messageNodes: (VNode | string)[]
let incrorectMessage: string
if (limitLevel === 'password' && limitPassword) {
answerValue = limitPassword
messageNodes = [t(msg => msg.limit.verification.pswInputTip)]
incrorectMessage = t(msg => msg.limit.verification.incorrectPsw)
} else if (limitLevel === 'verification') {
const pair: VerificationPair = verificationProcessor.generate(limitVerifyDifficulty, locale)
const { prompt, promptParam, answer } = pair || {}
answerValue = typeof answer === 'function' ? t(msg => answer(msg.limit.verification)) : answer
incrorectMessage = t(msg => msg.limit.verification.incorrectAnswer)
if (prompt) {
const promptTxt = typeof prompt === 'function'
? t(msg => prompt(msg.limit.verification), { ...promptParam, answer: answerValue })
: prompt
messageNodes = tN(msg => msg.limit.verification.inputTip, { prompt: h('b', promptTxt) })
} else {
messageNodes = tN(msg => msg.limit.verification.inputTip2, { answer: h('b', answerValue) })
}
}
return messageNodes?.length && answerValue
? new Promise(resolve =>
ElMessageBox({
boxType: 'prompt',
type: 'warning',
title: '',
message: h('div', { style: PROMT_TXT_CSS }, messageNodes),
showInput: true,
showCancelButton: true,
showClose: true,
}).then(data => {
const { value } = data
if (value === answerValue) {
return resolve()
}
ElMessage.error(incrorectMessage)
})
)
: null
}
18 changes: 16 additions & 2 deletions src/app/components/limit/table/column/delay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@ import { InfoFilled } from "@element-plus/icons-vue"
import { ElIcon, ElSwitch, ElTableColumn, ElTooltip } from "element-plus"
import { defineComponent, h } from "vue"
import { t } from "@app/locale"
import { judgeVerificationRequired, processVerification } from "./common"
import optionService from "@service/option-service"

const label = t(msg => msg.limit.item.delayAllowed)
const tooltip = t(msg => msg.limit.item.delayAllowedInfo)

async function handleChange(row: timer.limit.Item, newVal: boolean, callback: () => void) {
let promise: Promise<void> = null
if (newVal && judgeVerificationRequired(row)) {
// Open delay for limited rules, so verification is required
const option = await optionService.getAllOption()
promise = processVerification(option)
}
promise
? promise.then(callback).catch(() => { })
: callback()
}

const _default = defineComponent({
name: "LimitDelayColumn",
emits: {
Expand All @@ -26,10 +40,10 @@ const _default = defineComponent({
}, {
default: ({ row }: { row: timer.limit.Item }) => h(ElSwitch, {
modelValue: row.allowDelay,
onChange(val: boolean) {
onChange: (val: boolean) => handleChange(row, val, () => {
row.allowDelay = val
ctx.emit("rowChange", row, val)
}
})
}),
header: () => h('div', [
label,
Expand Down
18 changes: 16 additions & 2 deletions src/app/components/limit/table/column/enabled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@
import { ElSwitch, ElTableColumn } from "element-plus"
import { defineComponent, h } from "vue"
import { t } from "@app/locale"
import { judgeVerificationRequired, processVerification } from "./common"
import optionService from "@service/option-service"

const label = t(msg => msg.limit.item.enabled)

async function handleChange(row: timer.limit.Item, newVal: boolean, callback: () => void) {
let promise: Promise<void> = null
if (!newVal && judgeVerificationRequired(row)) {
// Disable limited rules, so verification is required
const option = await optionService.getAllOption()
promise = processVerification(option)
}
promise
? promise.then(callback).catch(() => { })
: callback()
}

const _default = defineComponent({
name: "LimitEnabledColumn",
emits: {
Expand All @@ -25,10 +39,10 @@ const _default = defineComponent({
}, {
default: ({ row }: { row: timer.limit.Item }) => h(ElSwitch, {
modelValue: row.enabled,
onChange(val: boolean) {
onChange: (val: boolean) => handleChange(row, val, () => {
row.enabled = val
ctx.emit("rowChange", row, val)
}
})
})
})
}
Expand Down
40 changes: 31 additions & 9 deletions src/app/components/limit/table/column/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,40 @@ import { Delete, Edit } from "@element-plus/icons-vue"
import { ElButton, ElMessageBox, ElTableColumn } from "element-plus"
import { defineComponent, h } from "vue"
import { t } from "@app/locale"
import optionService from "@service/option-service"
import { judgeVerificationRequired, processVerification } from "./common"

const label = t(msg => msg.limit.item.operation)
const deleteButtonText = t(msg => msg.button.delete)
const modifyButtonText = t(msg => msg.button.modify)

async function handleDelete(row: timer.limit.Item, callback: () => void) {
let promise = undefined
if (judgeVerificationRequired(row)) {
const option = await optionService.getAllOption() as timer.option.DailyLimitOption
promise = processVerification(option)
}
if (!promise) {
const message = t(msg => msg.limit.message.deleteConfirm, { cond: row.cond })
promise = ElMessageBox.confirm(message, { type: 'warning' })
}
promise.then(callback).catch(() => { /** Do nothing */ })
}

async function handleModify(row: timer.limit.Item, callback: () => void) {
let promise: Promise<void> = undefined
if (judgeVerificationRequired(row)) {
const option = await optionService.getAllOption() as timer.option.DailyLimitOption
promise = processVerification(option)
promise
? promise.then(callback).catch(() => { })
: callback()
} else {
callback()
}
}

const _default = defineComponent({
name: "LimitOperationColumn",
emits: {
rowDelete: (_row: timer.limit.Item, _cond: string) => true,
rowModify: (_row: timer.limit.Item) => true,
Expand All @@ -31,19 +59,13 @@ const _default = defineComponent({
type: 'danger',
size: 'small',
icon: Delete,
onClick() {
const { cond } = row
const message = t(msg => msg.limit.message.deleteConfirm, { cond })
ElMessageBox.confirm(message, { type: 'warning' })
.then(() => ctx.emit("rowDelete", row, cond))
.catch(() => { /** Do nothing */ })
}
onClick: () => handleDelete(row, () => ctx.emit("rowDelete", row, row.cond))
}, () => deleteButtonText),
h(ElButton, {
type: 'primary',
size: 'small',
icon: Edit,
onClick: () => ctx.emit('rowModify', row),
onClick: () => handleModify(row, () => ctx.emit('rowModify', row)),
}, () => modifyButtonText)
]
})
Expand Down
1 change: 0 additions & 1 deletion src/app/components/limit/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import LimitEnabledColumn from "./column/enabled"
import LimitOperationColumn from "./column/operation"

const _default = defineComponent({
name: "LimitTable",
props: {
data: Array as PropType<timer.limit.Item[]>
},
Expand Down
Loading

0 comments on commit 3f43a49

Please sign in to comment.