mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 09:40:20 -05:00
Updates
This commit is contained in:
1 parent
331d1e347b
commit
a6b2938e1c
8 files changed
+130
-2
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { isSubmitKey } from '/models/lib/editorSubmitKey';
|
||||
import { titleSortIndexes } from '/models/lib/multilineTitles';
|
||||
import { titleTagText } from '/models/lib/titleTags';
|
||||
import { titlesFromComposer } from '/client/components/forms/multilineTitleChoice';
|
||||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { TAPi18n } from '/imports/i18n';
|
||||
@@ -996,6 +997,12 @@ Template.addCardForm.onRendered(function () {
|
||||
|
||||
autosize($textarea);
|
||||
|
||||
// #1273: a per-user choice, off by default.
|
||||
const keepTitleTags = () => {
|
||||
const user = ReactiveCache.getCurrentUser();
|
||||
return !!(user && typeof user.hasKeepTitleTags === 'function' && user.hasKeepTitleTags());
|
||||
};
|
||||
|
||||
$textarea.escapeableTextComplete(
|
||||
[
|
||||
// User mentions
|
||||
@@ -1022,7 +1029,8 @@ Template.addCardForm.onRendered(function () {
|
||||
},
|
||||
replace(user) {
|
||||
toggleValueInReactiveArray(tpl.members, user._id);
|
||||
return '';
|
||||
// #1273: Member Settings can keep the "@member" text in the title.
|
||||
return titleTagText('member', user, keepTitleTags());
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
@@ -1056,7 +1064,7 @@ Template.addCardForm.onRendered(function () {
|
||||
},
|
||||
replace(label) {
|
||||
toggleValueInReactiveArray(tpl.labels, label._id);
|
||||
return '';
|
||||
return titleTagText('label', label, keepTitleTags());
|
||||
},
|
||||
index: 1,
|
||||
},
|
||||
|
||||
@@ -312,6 +312,13 @@ template(name="changeSettingsPopup")
|
||||
| {{_ 'show-desktop-drag-handles'}}
|
||||
if isShowDesktopDragHandles
|
||||
i.fa.fa-check
|
||||
//- #1273: keep the #label / @member text in a new card's title.
|
||||
li
|
||||
a.js-toggle-keep-title-tags(title="{{_ 'keep-title-tags-description'}}")
|
||||
i.fa.fa-hashtag
|
||||
| {{_ 'keep-title-tags'}}
|
||||
if isKeepTitleTags
|
||||
i.fa.fa-check
|
||||
li
|
||||
a.js-toggle-submit-on-enter(title="{{_ 'submit-on-enter-description'}}")
|
||||
i.fa.fa-level-down
|
||||
|
||||
@@ -462,6 +462,10 @@ Template.changeLanguagePopup.events({
|
||||
});
|
||||
|
||||
Template.changeSettingsPopup.helpers({
|
||||
isKeepTitleTags() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
return currentUser ? currentUser.hasKeepTitleTags() : false;
|
||||
},
|
||||
isSubmitOnEnter() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
return currentUser ? currentUser.hasSubmitOnEnter() : false;
|
||||
@@ -517,6 +521,9 @@ Template.changeSettingsPopup.events({
|
||||
window.localStorage.setItem('showDesktopDragHandles', show ? 'true' : 'false');
|
||||
}
|
||||
},
|
||||
'click .js-toggle-keep-title-tags'() {
|
||||
if (ReactiveCache.getCurrentUser()) Meteor.call('toggleKeepTitleTags');
|
||||
},
|
||||
'click .js-toggle-submit-on-enter'() {
|
||||
// Saved to the user profile (Member Settings only; requires a logged-in user).
|
||||
if (ReactiveCache.getCurrentUser()) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
// #1273: what a new card's title keeps when a #label or @member is picked
|
||||
// while it is typed. By default the "#label" / "@member" text is replaced by
|
||||
// the label or member itself (nothing is left in the title); with the Member
|
||||
// Settings switch on, the text stays too, as it was typed. Pure:
|
||||
// tests/titleTags.test.cjs.
|
||||
|
||||
function titleTagText(kind, item, keep) {
|
||||
if (!keep || !item) return '';
|
||||
if (kind === 'member') return item.username ? `@${item.username} ` : '';
|
||||
if (kind === 'label') {
|
||||
const name = item.name || item.color;
|
||||
return name ? `#${name} ` : '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
module.exports = { titleTagText };
|
||||
@@ -447,6 +447,15 @@ Users.attachSchema(
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
'profile.keepTitleTags': {
|
||||
/**
|
||||
* #1273: when a #label or @member is picked while typing a new card's
|
||||
* title, keep the "#label" / "@member" text in the title too. Off by
|
||||
* default: the text is replaced by the label or member, as before.
|
||||
*/
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
'profile.checklistDingSound': {
|
||||
/**
|
||||
* #5427: per-user preference - play a short synthesized "ding" when a
|
||||
@@ -2094,6 +2103,11 @@ Users.helpers({
|
||||
return profile.submitOnEnter || false;
|
||||
},
|
||||
|
||||
// #1273: keep the #label / @member text in a new card's title.
|
||||
hasKeepTitleTags() {
|
||||
return !!((this.profile || {}).keepTitleTags);
|
||||
},
|
||||
|
||||
// #5427: does this user want a short "ding" sound when a checklist item is
|
||||
// checked off? Off by default.
|
||||
hasChecklistDingSound() {
|
||||
|
||||
@@ -2079,6 +2079,14 @@
|
||||
{
|
||||
"key": "activity-recovery-bulk-result",
|
||||
"since": "2026-10-14"
|
||||
},
|
||||
{
|
||||
"key": "keep-title-tags",
|
||||
"since": "2026-10-14"
|
||||
},
|
||||
{
|
||||
"key": "keep-title-tags-description",
|
||||
"since": "2026-10-14"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -954,6 +954,15 @@ Meteor.methods({
|
||||
|
||||
// Per-user "submit editors on plain Enter" preference (Member Settings).
|
||||
// Off by default; when on, plain Enter submits and Shift+Enter is a newline.
|
||||
// #1273: keep the #label / @member text in a new card's title (Member
|
||||
// Settings; off by default).
|
||||
async toggleKeepTitleTags() {
|
||||
if (!this.userId) throw new Meteor.Error('not-logged-in', 'User must be logged in');
|
||||
const user = await Users.findOneAsync(this.userId);
|
||||
if (!user) throw new Meteor.Error('user-not-found', 'User not found');
|
||||
await Users.updateAsync(this.userId, { $set: { 'profile.keepTitleTags': !((user.profile || {}).keepTitleTags) } });
|
||||
},
|
||||
|
||||
async toggleSubmitOnEnter() {
|
||||
if (!this.userId) throw new Meteor.Error('not-logged-in', 'User must be logged in');
|
||||
const user = await Users.findOneAsync(this.userId);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
// #1273: a Member Settings switch keeps the "#label" / "@member" text in a
|
||||
// new card's title when the label or member is picked from the composer's
|
||||
// autocomplete. Off (the default) keeps today's behaviour: the text goes,
|
||||
// and the card gets the label or member.
|
||||
// Run: node tests/titleTags.test.cjs
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { titleTagText } = require('../models/lib/titleTags.js');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const read = file => fs.readFileSync(path.join(ROOT, file), 'utf8');
|
||||
|
||||
// Positive: with the switch on, the typed text stays.
|
||||
assert.equal(titleTagText('member', { username: 'xet7' }, true), '@xet7 ');
|
||||
assert.equal(titleTagText('label', { name: 'Bug', color: 'red' }, true), '#Bug ');
|
||||
assert.equal(titleTagText('label', { name: '', color: 'red' }, true), '#red ', 'an unnamed label is its colour');
|
||||
console.log(' ok - on: the #label / @member text stays in the title');
|
||||
|
||||
// Negative: off (or unset) removes it, as before; nothing odd is ever inserted.
|
||||
for (const keep of [false, undefined, null, 0]) {
|
||||
assert.equal(titleTagText('member', { username: 'xet7' }, keep), '');
|
||||
assert.equal(titleTagText('label', { name: 'Bug' }, keep), '');
|
||||
}
|
||||
assert.equal(titleTagText('member', {}, true), '', 'no "@undefined"');
|
||||
assert.equal(titleTagText('label', {}, true), '', 'no "#undefined"');
|
||||
assert.equal(titleTagText('label', null, true), '');
|
||||
assert.equal(titleTagText('other', { name: 'x' }, true), '');
|
||||
console.log(' ok - off, or with nothing to name, nothing is left in the title');
|
||||
|
||||
// Wiring: both composer strategies use it, with the user's setting.
|
||||
const body = read('client/components/lists/listBody.js');
|
||||
assert.match(body, /toggleValueInReactiveArray\(tpl\.members, user\._id\);[\s\S]{0,200}return titleTagText\('member', user, keepTitleTags\(\)\);/);
|
||||
assert.match(body, /toggleValueInReactiveArray\(tpl\.labels, label\._id\);[\s\S]{0,200}return titleTagText\('label', label, keepTitleTags\(\)\);/);
|
||||
assert.match(body, /const keepTitleTags = \(\) => \{[\s\S]{0,200}user\.hasKeepTitleTags\(\)/);
|
||||
console.log(' ok - both autocomplete strategies follow the setting');
|
||||
|
||||
// The setting: schema, helper, an own-profile toggle, and the UI (UI guard).
|
||||
const users = read('models/users.js');
|
||||
assert.match(users, /'profile\.keepTitleTags': \{[\s\S]{0,400}type: Boolean,\s*optional: true,/);
|
||||
assert.match(users, /hasKeepTitleTags\(\) \{\s*return !!\(\(this\.profile \|\| \{\}\)\.keepTitleTags\);/);
|
||||
const server = read('server/models/users.js');
|
||||
assert.match(server, /async toggleKeepTitleTags\(\) \{\s*if \(!this\.userId\) throw[\s\S]{0,300}Users\.updateAsync\(this\.userId, \{ \$set: \{ 'profile\.keepTitleTags':/,
|
||||
'only the signed-in user\'s own profile is changed');
|
||||
const jade = read('client/components/users/userHeader.jade');
|
||||
assert.match(jade, /a\.js-toggle-keep-title-tags\(title="\{\{_ 'keep-title-tags-description'\}\}"\)[\s\S]{0,120}\{\{_ 'keep-title-tags'\}\}\s*\n\s*if isKeepTitleTags\s*\n\s*i\.fa\.fa-check/);
|
||||
const js = read('client/components/users/userHeader.js');
|
||||
assert.match(js, /'click \.js-toggle-keep-title-tags'\(\) \{\s*if \(ReactiveCache\.getCurrentUser\(\)\) Meteor\.call\('toggleKeepTitleTags'\);/);
|
||||
const en = JSON.parse(read('imports/i18n/data/en.i18n.json'));
|
||||
assert.ok(en['keep-title-tags'] && en['keep-title-tags-description']);
|
||||
console.log(' ok - Member Settings has the switch, off by default');
|
||||
|
||||
console.log('\ntitleTags: all tests passed');
|
||||
Reference in new issue
Block a user