Add notifications system and support user mentions in comments (#9861)

* v-menu de/activated onKeyDown. No List yet.

* v-list

* add user suggestion

* uuids replaced

* user-popover working

* avatars flex row with usernames in suggestions

* added space to end of uuid insert

* autofocus + move caret to end of last insert

* removed unnecessary setTimeout()

* fixed filter 500 with ids

* better fix

* New translations en-US.yaml (French) (#9907)

* New translations en-US.yaml (French) (#9912)

* New translations en-US.yaml (French) (#9916)

* New translations en-US.yaml (Russian) (#9918)

* New translations en-US.yaml (Swedish) (#9920)

* Email updates (#9921)

* add from name for emails

* updatd email template style

* reset password email copy

* updated logo to newest version

* update invite email copy

* decouple field template logic

* push up styling

* Start on new v-template-input

* Add notifications API endpoints

Squashed commit of the following:

commit 9d86721ef795d03bc55693c0f99bde8e269d60e9
Merge: b4458c19f 34131d06e
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Mon Nov 22 09:27:43 2021 -0500

    Merge branch 'mentions' into mentions-api

commit b4458c19f7c54f18fa415fc04c63642c2f5a17b0
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 18:34:04 2021 -0500

    Remove unused import

commit e6a9d36bbfdf95cb18d29336da61ecb14b677934
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 18:28:31 2021 -0500

    Extract user mentions from comments

commit b3e571a2daa287e1740a050096913662a57e9861
Merge: c93b833d2 af2a6dd7f
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 17:39:52 2021 -0500

    Merge branch 'mentions' into mentions-api

commit c93b833d2b848e306c434b370d4e4e11967e85d0
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 17:35:45 2021 -0500

    Send emails w/ parsed MD

commit 64bbd6596f20a07028d2387d60e33dfe4f91c032
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 16:18:16 2021 -0500

    Add notifications endpoint + permissions

commit fba55c02dc9c303a38b1b958350684cccd3dd82c
Author: rijkvanzanten <rijkvanzanten@me.com>
Date:   Thu Nov 18 15:33:28 2021 -0500

    Add system data for notifications

* push

* Make v-template-input work

* Add the two-way binding

* submit button posting, not clearing text area

* comment text area clearing on submit

* Replace insertion correctly

* Added scope support to LDAP group and user search (#9529)

* Added scope support LDAP group and user search

* Fixed linter screwing up my markdown

* Update docs/configuration/config-options.md

* Always return correct DN for user with sub scope

* Fix indeterminate meta and schema property in advanded field creation (#9924)

* Fix impossibility to save M2M (alterations not triggered) (#9992)

* Fix alterations refactor

* fix roles aggregate query (#9994)

* Update iis.md (#9998)

added the IIS URL Rewrite module as a requirement

* New translations en-US.yaml (English, United Kingdom) (#10001)

* Fix LDAP race condition (#9993)

* Fix input ui

* Revert changes to v-field-template

* Update mentions permissions

* Fix linter warnings

* Optimize sending flow

* Revert "Rename activity->notifications module (#9446)"

This reverts commit 428e5d4ea9.

* Add notifications drawer

* Update migrations

* Improve constraints

* Add email notifications toggle on users

* Add docs, fix graphql support

* Move caret-pos to devdeps

* Remove unused new triggerKeyPressed system

* Remove unused use-caret composable

Co-authored-by: Nitwel <nitwel@arcor.de>
Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
Co-authored-by: Ben Haynes <ben@rngr.org>
Co-authored-by: Aiden Foxx <aiden.foxx@sbab.se>
Co-authored-by: Oreille <33065839+Oreilles@users.noreply.github.com>
Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>
Co-authored-by: Paul Boudewijn <paul@helderinternet.nl>
This commit is contained in:
Jay Cammarano
2021-11-24 16:11:26 -05:00
committed by GitHub
parent f19a549a1b
commit 25375cc481
49 changed files with 3162 additions and 2531 deletions

View File

@@ -47,6 +47,7 @@ import VSlider from './v-slider/';
import VSwitch from './v-switch/';
import VTable from './v-table/';
import VTabs, { VTab, VTabItem, VTabsItems } from './v-tabs/';
import VTemplateInput from './v-template-input.vue';
import VTextOverflow from './v-text-overflow.vue';
import VTextarea from './v-textarea';
import VUpload from './v-upload';
@@ -103,6 +104,7 @@ export function registerComponents(app: App): void {
app.component('VTable', VTable);
app.component('VTabsItems', VTabsItems);
app.component('VTabs', VTabs);
app.component('VTemplateInput', VTemplateInput);
app.component('VTextarea', VTextarea);
app.component('VTextOverflow', VTextOverflow);
app.component('VUpload', VUpload);

View File

@@ -206,6 +206,7 @@ body {
--content-padding: 16px;
--content-padding-bottom: 32px;
position: relative;
flex-grow: 1;
overflow: auto;

View File

@@ -97,7 +97,7 @@ export default defineComponent({
trigger: {
type: String,
default: null,
validator: (val: string) => ['hover', 'click'].includes(val),
validator: (val: string) => ['hover', 'click', 'keyDown'].includes(val),
},
delay: {
type: Number,

View File

@@ -0,0 +1,177 @@
<template>
<div
ref="input"
class="v-template-input"
:class="{ multiline }"
contenteditable="true"
tabindex="1"
@input="processText"
/>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watch, onMounted } from 'vue';
import { position } from 'caret-pos';
export default defineComponent({
props: {
modelValue: {
type: String,
default: null,
},
captureGroup: {
type: String,
required: true,
},
multiline: {
type: Boolean,
default: false,
},
triggerCharacter: {
type: String,
required: true,
},
items: {
type: Object as PropType<Record<string, string>>,
required: true,
},
},
emits: ['update:modelValue', 'trigger', 'deactivate'],
setup(props, { emit }) {
const input = ref<HTMLDivElement>();
let hasTriggered = false;
watch(
() => props.modelValue,
(newText) => {
if (!input.value) return;
if (newText !== input.value.innerText) {
parseHTML(newText);
}
}
);
onMounted(() => {
if (props.modelValue && props.modelValue !== input.value!.innerText) {
parseHTML(props.modelValue);
}
});
return { processText, input };
function processText(event: KeyboardEvent) {
const input = event.target as HTMLDivElement;
const caretPos = position(input).pos;
const text = input.innerText ?? '';
let endPos = text.indexOf(' ', caretPos);
if (endPos == -1) endPos = text.length;
const result = /\S+$/.exec(text.slice(0, endPos));
let word = result ? result[0] : null;
if (word) word = word.replace(/[\s'";:,./?\\-]$/, '');
if (word?.startsWith(props.triggerCharacter)) {
emit('trigger', { searchQuery: word.substring(props.triggerCharacter.length), caretPosition: caretPos });
hasTriggered = true;
} else {
if (hasTriggered) {
emit('deactivate');
hasTriggered = false;
}
}
parseHTML();
emit('update:modelValue', input.innerText);
}
function parseHTML(innerText?: string) {
if (!input.value) return;
let newHTML = innerText ?? input.value.innerHTML ?? '';
const caretPos = window.getSelection()?.rangeCount ? position(input.value).pos : 0;
const matches = newHTML.match(new RegExp(`${props.captureGroup}(?!</mark>)`, 'gi'));
if (matches) {
for (const match of matches ?? []) {
newHTML = newHTML.replace(
new RegExp(`(${match})(?!</mark>)`),
`&nbsp;<mark class="preview" data-preview="${
props.items[match.substring(props.triggerCharacter.length)]
}" contenteditable="false">${match}</mark>&nbsp;`
);
}
}
if (input.value.innerHTML !== newHTML) {
input.value.innerHTML = newHTML;
const delta = newHTML.length - input.value.innerHTML.length;
const newPosition = caretPos + delta;
if (newPosition >= newHTML.length || newPosition < 0) {
position(input.value, newHTML.length - 1);
} else {
position(input.value, caretPos + delta);
}
}
}
},
});
</script>
<style scoped lang="scss">
.v-template-input {
position: relative;
height: var(--input-height);
padding: var(--input-padding);
padding-bottom: 32px;
overflow: hidden;
color: var(--foreground-normal);
font-family: var(--family-sans-serif);
white-space: nowrap;
background-color: var(--background-page);
border: var(--border-width) solid var(--border-normal);
border-radius: var(--border-radius);
transition: border-color var(--fast) var(--transition);
&.multiline {
height: var(--input-height-tall);
overflow-y: auto;
white-space: pre-line;
}
&:hover {
border-color: var(--border-normal-alt);
}
&:focus-within {
border-color: var(--primary);
}
:deep(.preview) {
display: inline-block;
margin: 2px;
padding: 2px 4px;
color: var(--primary);
font-size: 0;
line-height: 1;
vertical-align: -2px;
background: var(--primary-alt);
border-radius: var(--border-radius);
user-select: text;
&::before {
display: block;
font-size: 1rem;
content: attr(data-preview);
}
}
}
</style>