feat: добавлена поддержка шаблонов $0 и \$0 для наград в задачах (v3.1.1)
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 44s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 44s
This commit is contained in:
@@ -7290,17 +7290,47 @@ func (a *App) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
rewardStrings[reward.Position] = rewardStr
|
rewardStrings[reward.Position] = rewardStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подставляем в reward_message основной задачи
|
// Функция для замены плейсхолдеров в сообщении награды
|
||||||
var mainTaskMessage string
|
replaceRewardPlaceholders := func(message string, rewardStrings map[int]string) string {
|
||||||
if task.RewardMessage != nil && *task.RewardMessage != "" {
|
result := message
|
||||||
mainTaskMessage = *task.RewardMessage
|
// Сначала сохраняем экранированные плейсхолдеры \$0, \$1 и т.д. во временные маркеры
|
||||||
// Заменяем плейсхолдеры ${0}, ${1}, и т.д.
|
escapedMarkers := make(map[string]string)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
escaped := fmt.Sprintf(`\$%d`, i)
|
||||||
|
marker := fmt.Sprintf(`__ESCAPED_DOLLAR_%d__`, i)
|
||||||
|
if strings.Contains(result, escaped) {
|
||||||
|
escapedMarkers[marker] = escaped
|
||||||
|
result = strings.ReplaceAll(result, escaped, marker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Заменяем ${0}, ${1}, и т.д.
|
||||||
for i := 0; i < 100; i++ { // Максимум 100 плейсхолдеров
|
for i := 0; i < 100; i++ { // Максимум 100 плейсхолдеров
|
||||||
placeholder := fmt.Sprintf("${%d}", i)
|
placeholder := fmt.Sprintf("${%d}", i)
|
||||||
if rewardStr, ok := rewardStrings[i]; ok {
|
if rewardStr, ok := rewardStrings[i]; ok {
|
||||||
mainTaskMessage = strings.ReplaceAll(mainTaskMessage, placeholder, rewardStr)
|
result = strings.ReplaceAll(result, placeholder, rewardStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Затем заменяем $0, $1, и т.д. (экранированные уже защищены маркерами)
|
||||||
|
// Используем регулярное выражение для поиска $N, где после N не идет еще одна цифра
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
if rewardStr, ok := rewardStrings[i]; ok {
|
||||||
|
// Паттерн: $N, где после N не идет еще одна цифра (чтобы не заменить $10 при поиске $1)
|
||||||
|
pattern := fmt.Sprintf(`\$%d(?!\d)`, i)
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
result = re.ReplaceAllString(result, rewardStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Восстанавливаем экранированные доллары из временных маркеров
|
||||||
|
for marker, escaped := range escapedMarkers {
|
||||||
|
result = strings.ReplaceAll(result, marker, escaped)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подставляем в reward_message основной задачи
|
||||||
|
var mainTaskMessage string
|
||||||
|
if task.RewardMessage != nil && *task.RewardMessage != "" {
|
||||||
|
mainTaskMessage = replaceRewardPlaceholders(*task.RewardMessage, rewardStrings)
|
||||||
} else {
|
} else {
|
||||||
// Если reward_message пустой, используем имя задачи
|
// Если reward_message пустой, используем имя задачи
|
||||||
mainTaskMessage = task.Name
|
mainTaskMessage = task.Name
|
||||||
@@ -7394,13 +7424,7 @@ func (a *App) completeTaskHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Подставляем в reward_message подзадачи
|
// Подставляем в reward_message подзадачи
|
||||||
subtaskMessage := subtaskRewardMessage.String
|
subtaskMessage := replaceRewardPlaceholders(subtaskRewardMessage.String, subtaskRewardStrings)
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
placeholder := fmt.Sprintf("${%d}", i)
|
|
||||||
if rewardStr, ok := subtaskRewardStrings[i]; ok {
|
|
||||||
subtaskMessage = strings.ReplaceAll(subtaskMessage, placeholder, rewardStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subtaskMessages = append(subtaskMessages, subtaskMessage)
|
subtaskMessages = append(subtaskMessages, subtaskMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "3.0.1",
|
"version": "3.1.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -268,10 +268,40 @@ function TaskForm({ onNavigate, taskId }) {
|
|||||||
|
|
||||||
const findMaxPlaceholderIndex = (message) => {
|
const findMaxPlaceholderIndex = (message) => {
|
||||||
if (!message) return -1
|
if (!message) return -1
|
||||||
const matches = message.match(/\$\{(\d+)\}/g)
|
// Находим все варианты плейсхолдеров: ${0}, $0, но не \$0
|
||||||
if (!matches) return -1
|
const indices = []
|
||||||
const indices = matches.map(m => parseInt(m.match(/\d+/)[0]))
|
|
||||||
return Math.max(...indices)
|
// Ищем ${N}
|
||||||
|
const matchesCurly = message.match(/\$\{(\d+)\}/g) || []
|
||||||
|
matchesCurly.forEach(match => {
|
||||||
|
const numMatch = match.match(/\d+/)
|
||||||
|
if (numMatch) {
|
||||||
|
indices.push(parseInt(numMatch[0]))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ищем $N (но не \$N)
|
||||||
|
// Используем глобальный поиск и проверяем, что перед $ нет обратного слэша
|
||||||
|
let searchIndex = 0
|
||||||
|
while (true) {
|
||||||
|
const index = message.indexOf('$', searchIndex)
|
||||||
|
if (index === -1) break
|
||||||
|
|
||||||
|
// Проверяем, что перед $ нет обратного слэша
|
||||||
|
if (index === 0 || message[index - 1] !== '\\') {
|
||||||
|
// Проверяем, что после $ идет цифра
|
||||||
|
const afterDollar = message.substring(index + 1)
|
||||||
|
const digitMatch = afterDollar.match(/^(\d+)/)
|
||||||
|
if (digitMatch) {
|
||||||
|
// Проверяем, что после цифры не идет еще одна цифра (чтобы не захватить $10 при поиске $1)
|
||||||
|
const num = parseInt(digitMatch[0])
|
||||||
|
indices.push(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchIndex = index + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return indices.length > 0 ? Math.max(...indices) : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -551,7 +581,7 @@ function TaskForm({ onNavigate, taskId }) {
|
|||||||
id="reward_message"
|
id="reward_message"
|
||||||
value={rewardMessage}
|
value={rewardMessage}
|
||||||
onChange={(e) => setRewardMessage(e.target.value)}
|
onChange={(e) => setRewardMessage(e.target.value)}
|
||||||
placeholder="Используйте ${0}, ${1} для указания проектов"
|
placeholder="Используйте ${0}, $0 для указания проектов (\\$0 для экранирования)"
|
||||||
className="form-textarea"
|
className="form-textarea"
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user