Replys to ActivityPub should start with @someone
This commit is contained in:
parent
4d97922933
commit
c25c600417
216
core/fse.js
216
core/fse.js
|
@ -21,6 +21,7 @@ const {
|
||||||
messageInfoFromAddressedToInfo,
|
messageInfoFromAddressedToInfo,
|
||||||
setExternalAddressedToInfo,
|
setExternalAddressedToInfo,
|
||||||
copyExternalAddressedToInfo,
|
copyExternalAddressedToInfo,
|
||||||
|
getReplyToMessagePrefix,
|
||||||
} = require('./mail_util.js');
|
} = require('./mail_util.js');
|
||||||
const Events = require('./events.js');
|
const Events = require('./events.js');
|
||||||
const UserProps = require('./user_property.js');
|
const UserProps = require('./user_property.js');
|
||||||
|
@ -28,10 +29,10 @@ const SysProps = require('./system_property.js');
|
||||||
const FileArea = require('./file_base_area.js');
|
const FileArea = require('./file_base_area.js');
|
||||||
const FileEntry = require('./file_entry.js');
|
const FileEntry = require('./file_entry.js');
|
||||||
const DownloadQueue = require('./download_queue.js');
|
const DownloadQueue = require('./download_queue.js');
|
||||||
|
const EngiAssert = require('./enigma_assert.js');
|
||||||
|
|
||||||
// deps
|
// deps
|
||||||
const async = require('async');
|
const async = require('async');
|
||||||
const assert = require('assert');
|
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const fse = require('fs-extra');
|
const fse = require('fs-extra');
|
||||||
|
@ -123,7 +124,7 @@ exports.FullScreenEditorModule =
|
||||||
this.editorMode = config.editorMode;
|
this.editorMode = config.editorMode;
|
||||||
|
|
||||||
if (config.messageAreaTag) {
|
if (config.messageAreaTag) {
|
||||||
// :TODO: swtich to this.config.messageAreaTag so we can follow Object.assign pattern for config/extraArgs
|
// :TODO: switch to this.config.messageAreaTag so we can follow Object.assign pattern for config/extraArgs
|
||||||
this.messageAreaTag = config.messageAreaTag;
|
this.messageAreaTag = config.messageAreaTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +252,7 @@ exports.FullScreenEditorModule =
|
||||||
if (self.newQuoteBlock) {
|
if (self.newQuoteBlock) {
|
||||||
self.newQuoteBlock = false;
|
self.newQuoteBlock = false;
|
||||||
|
|
||||||
// :TODO: If replying to ANSI, add a blank sepration line here
|
// :TODO: If replying to ANSI, add a blank separation line here
|
||||||
|
|
||||||
quoteMsgView.addText(self.getQuoteByHeader());
|
quoteMsgView.addText(self.getQuoteByHeader());
|
||||||
}
|
}
|
||||||
|
@ -480,106 +481,108 @@ exports.FullScreenEditorModule =
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
|
||||||
this.updateLastReadId(() => {
|
this.updateLastReadId(() => {
|
||||||
if (this.isReady) {
|
if (!this.isReady) {
|
||||||
this.initHeaderViewMode();
|
return;
|
||||||
this.initFooterViewMode();
|
}
|
||||||
|
|
||||||
const bodyMessageView = this.viewControllers.body.getView(
|
this.initHeaderViewMode();
|
||||||
MciViewIds.body.message
|
this.initFooterViewMode();
|
||||||
);
|
|
||||||
let msg = this.message.message;
|
|
||||||
|
|
||||||
if (bodyMessageView && _.has(this, 'message.message')) {
|
const bodyMessageView = this.viewControllers.body.getView(
|
||||||
|
MciViewIds.body.message
|
||||||
|
);
|
||||||
|
let msg = this.message.message;
|
||||||
|
|
||||||
|
if (bodyMessageView && _.has(this, 'message.message')) {
|
||||||
|
//
|
||||||
|
// We handle ANSI messages differently than standard messages -- this is required as
|
||||||
|
// we don't want to do things like word wrap ANSI, but instead, trust that it's formatted
|
||||||
|
// how the author wanted it
|
||||||
|
//
|
||||||
|
if (isAnsi(msg)) {
|
||||||
//
|
//
|
||||||
// We handle ANSI messages differently than standard messages -- this is required as
|
// Find tearline - we want to color it differently.
|
||||||
// we don't want to do things like word wrap ANSI, but instead, trust that it's formatted
|
|
||||||
// how the author wanted it
|
|
||||||
//
|
//
|
||||||
if (isAnsi(msg)) {
|
const tearLinePos = Message.getTearLinePosition(msg);
|
||||||
//
|
|
||||||
// Find tearline - we want to color it differently.
|
|
||||||
//
|
|
||||||
const tearLinePos = Message.getTearLinePosition(msg);
|
|
||||||
|
|
||||||
if (tearLinePos > -1) {
|
if (tearLinePos > -1) {
|
||||||
msg = insert(
|
msg = insert(
|
||||||
msg,
|
msg,
|
||||||
tearLinePos,
|
tearLinePos,
|
||||||
bodyMessageView.getTextSgrPrefix()
|
bodyMessageView.getTextSgrPrefix()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMessageView.setAnsi(
|
||||||
|
msg.replace(/\r?\n/g, '\r\n'), // messages are stored with CRLF -> LF
|
||||||
|
{
|
||||||
|
prepped: false,
|
||||||
|
forceLineTerm: true,
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
msg = stripAnsiControlCodes(msg); // start clean
|
||||||
|
|
||||||
bodyMessageView.setAnsi(
|
const styleToArray = (style, len) => {
|
||||||
msg.replace(/\r?\n/g, '\r\n'), // messages are stored with CRLF -> LF
|
if (!Array.isArray(style)) {
|
||||||
{
|
style = [style];
|
||||||
prepped: false,
|
}
|
||||||
forceLineTerm: true,
|
while (style.length < len) {
|
||||||
|
style.push(style[0]);
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// In *View* mode, if enabled, do a little prep work so we can stylize:
|
||||||
|
// - Quote indicators
|
||||||
|
// - Tear lines
|
||||||
|
// - Origins
|
||||||
|
//
|
||||||
|
if (this.menuConfig.config.quoteStyleLevel1) {
|
||||||
|
// can be a single style to cover 'XX> TEXT' or an array to cover 'XX', '>', and TEXT
|
||||||
|
// Non-standard (as for BBSes) single > TEXT, omitting space before XX, etc. are allowed
|
||||||
|
const styleL1 = styleToArray(
|
||||||
|
this.menuConfig.config.quoteStyleLevel1,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
const QuoteRegex =
|
||||||
|
/^([ ]?)([!-~]{0,2})>([ ]*)([^\r\n]*\r?\n)/gm;
|
||||||
|
msg = msg.replace(
|
||||||
|
QuoteRegex,
|
||||||
|
(m, spc1, initials, spc2, text) => {
|
||||||
|
return `${spc1}${styleL1[0]}${initials}${styleL1[1]}>${spc2}${styleL1[2]}${text}${bodyMessageView.styleSGR1}`;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
msg = stripAnsiControlCodes(msg); // start clean
|
|
||||||
|
|
||||||
const styleToArray = (style, len) => {
|
|
||||||
if (!Array.isArray(style)) {
|
|
||||||
style = [style];
|
|
||||||
}
|
|
||||||
while (style.length < len) {
|
|
||||||
style.push(style[0]);
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// In *View* mode, if enabled, do a little prep work so we can stylize:
|
|
||||||
// - Quote indicators
|
|
||||||
// - Tear lines
|
|
||||||
// - Origins
|
|
||||||
//
|
|
||||||
if (this.menuConfig.config.quoteStyleLevel1) {
|
|
||||||
// can be a single style to cover 'XX> TEXT' or an array to cover 'XX', '>', and TEXT
|
|
||||||
// Non-standard (as for BBSes) single > TEXT, omitting space before XX, etc. are allowed
|
|
||||||
const styleL1 = styleToArray(
|
|
||||||
this.menuConfig.config.quoteStyleLevel1,
|
|
||||||
3
|
|
||||||
);
|
|
||||||
|
|
||||||
const QuoteRegex =
|
|
||||||
/^([ ]?)([!-~]{0,2})>([ ]*)([^\r\n]*\r?\n)/gm;
|
|
||||||
msg = msg.replace(
|
|
||||||
QuoteRegex,
|
|
||||||
(m, spc1, initials, spc2, text) => {
|
|
||||||
return `${spc1}${styleL1[0]}${initials}${styleL1[1]}>${spc2}${styleL1[2]}${text}${bodyMessageView.styleSGR1}`;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.menuConfig.config.tearLineStyle) {
|
|
||||||
// '---' and TEXT
|
|
||||||
const style = styleToArray(
|
|
||||||
this.menuConfig.config.tearLineStyle,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
const TearLineRegex = /^--- (.+)$(?![\s\S]*^--- .+$)/m;
|
|
||||||
msg = msg.replace(TearLineRegex, (m, text) => {
|
|
||||||
return `${style[0]}--- ${style[1]}${text}${bodyMessageView.styleSGR1}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.menuConfig.config.originStyle) {
|
|
||||||
const style = styleToArray(
|
|
||||||
this.menuConfig.config.originStyle,
|
|
||||||
3
|
|
||||||
);
|
|
||||||
|
|
||||||
const OriginRegex = /^([ ]{1,2})\* Origin: (.+)$/m;
|
|
||||||
msg = msg.replace(OriginRegex, (m, spc, text) => {
|
|
||||||
return `${spc}${style[0]}* ${style[1]}Origin: ${style[2]}${text}${bodyMessageView.styleSGR1}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyMessageView.setText(controlCodesToAnsi(msg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.menuConfig.config.tearLineStyle) {
|
||||||
|
// '---' and TEXT
|
||||||
|
const style = styleToArray(
|
||||||
|
this.menuConfig.config.tearLineStyle,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
const TearLineRegex = /^--- (.+)$(?![\s\S]*^--- .+$)/m;
|
||||||
|
msg = msg.replace(TearLineRegex, (m, text) => {
|
||||||
|
return `${style[0]}--- ${style[1]}${text}${bodyMessageView.styleSGR1}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.menuConfig.config.originStyle) {
|
||||||
|
const style = styleToArray(
|
||||||
|
this.menuConfig.config.originStyle,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
const OriginRegex = /^([ ]{1,2})\* Origin: (.+)$/m;
|
||||||
|
msg = msg.replace(OriginRegex, (m, spc, text) => {
|
||||||
|
return `${spc}${style[0]}* ${style[1]}Origin: ${style[2]}${text}${bodyMessageView.styleSGR1}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMessageView.setText(controlCodesToAnsi(msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -849,7 +852,7 @@ exports.FullScreenEditorModule =
|
||||||
const self = this;
|
const self = this;
|
||||||
var art = self.menuConfig.config.art;
|
var art = self.menuConfig.config.art;
|
||||||
|
|
||||||
assert(_.isObject(art));
|
EngiAssert(_.isObject(art));
|
||||||
|
|
||||||
async.waterfall(
|
async.waterfall(
|
||||||
[
|
[
|
||||||
|
@ -1161,7 +1164,7 @@ exports.FullScreenEditorModule =
|
||||||
}
|
}
|
||||||
|
|
||||||
initHeaderReplyEditMode() {
|
initHeaderReplyEditMode() {
|
||||||
assert(_.isObject(this.replyToMessage));
|
EngiAssert(_.isObject(this.replyToMessage));
|
||||||
|
|
||||||
this.setHeaderText(MciViewIds.header.to, this.replyToMessage.fromUserName);
|
this.setHeaderText(MciViewIds.header.to, this.replyToMessage.fromUserName);
|
||||||
|
|
||||||
|
@ -1177,6 +1180,20 @@ exports.FullScreenEditorModule =
|
||||||
this.setHeaderText(MciViewIds.header.subject, newSubj);
|
this.setHeaderText(MciViewIds.header.subject, newSubj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initBodyReplyEditMode() {
|
||||||
|
EngiAssert(_.isObject(this.replyToMessage));
|
||||||
|
|
||||||
|
const bodyMessageView = this.viewControllers.body.getView(
|
||||||
|
MciViewIds.body.message
|
||||||
|
);
|
||||||
|
|
||||||
|
const messagePrefix = getReplyToMessagePrefix(
|
||||||
|
this.replyToMessage.fromUserName
|
||||||
|
);
|
||||||
|
|
||||||
|
bodyMessageView.setText(messagePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
initFooterViewMode() {
|
initFooterViewMode() {
|
||||||
this.setViewText(
|
this.setViewText(
|
||||||
'footerView',
|
'footerView',
|
||||||
|
@ -1450,11 +1467,18 @@ exports.FullScreenEditorModule =
|
||||||
switchToBody() {
|
switchToBody() {
|
||||||
const to = this.getView('header', MciViewIds.header.to).getData();
|
const to = this.getView('header', MciViewIds.header.to).getData();
|
||||||
const msgInfo = messageInfoFromAddressedToInfo(getAddressedToInfo(to));
|
const msgInfo = messageInfoFromAddressedToInfo(getAddressedToInfo(to));
|
||||||
|
const bodyView = this.getView('body', MciViewIds.body.message);
|
||||||
|
|
||||||
if (msgInfo.maxMessageLength > 0) {
|
if (msgInfo.maxMessageLength > 0) {
|
||||||
const bodyView = this.getView('body', MciViewIds.body.message);
|
|
||||||
bodyView.maxLength = msgInfo.maxMessageLength;
|
bodyView.maxLength = msgInfo.maxMessageLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// first pass through, init body (we may need header values set)
|
||||||
|
const bodyText = bodyView.getData();
|
||||||
|
if (!bodyText && this.isReply()) {
|
||||||
|
this.initBodyReplyEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
this.viewControllers.header.setFocus(false);
|
this.viewControllers.header.setFocus(false);
|
||||||
this.viewControllers.body.switchFocus(1);
|
this.viewControllers.body.switchFocus(1);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ exports.setExternalAddressedToInfo = setExternalAddressedToInfo;
|
||||||
exports.copyExternalAddressedToInfo = copyExternalAddressedToInfo;
|
exports.copyExternalAddressedToInfo = copyExternalAddressedToInfo;
|
||||||
exports.messageInfoFromAddressedToInfo = messageInfoFromAddressedToInfo;
|
exports.messageInfoFromAddressedToInfo = messageInfoFromAddressedToInfo;
|
||||||
exports.getQuotePrefixFromName = getQuotePrefixFromName;
|
exports.getQuotePrefixFromName = getQuotePrefixFromName;
|
||||||
|
exports.getReplyToMessagePrefix = getReplyToMessagePrefix;
|
||||||
|
|
||||||
const EMAIL_REGEX =
|
const EMAIL_REGEX =
|
||||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}]?)|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}]?)|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
@ -32,6 +33,11 @@ const EMAIL_REGEX =
|
||||||
foo@host.com { name : 'foo', flavor : 'email', remote : 'foo@host.com' }
|
foo@host.com { name : 'foo', flavor : 'email', remote : 'foo@host.com' }
|
||||||
Bar <baz@foobar.net> { name : 'Bar', flavor : 'email', remote : 'baz@foobar.com' }
|
Bar <baz@foobar.net> { name : 'Bar', flavor : 'email', remote : 'baz@foobar.com' }
|
||||||
@JoeUser@some.host.com { name : 'JoeUser', flavor : 'activitypub', remote 'JoeUser@some.host.com' }
|
@JoeUser@some.host.com { name : 'JoeUser', flavor : 'activitypub', remote 'JoeUser@some.host.com' }
|
||||||
|
|
||||||
|
Fields:
|
||||||
|
- name : user/display name
|
||||||
|
- flavor : remote flavor - FTN/etc.
|
||||||
|
- remote : Address in remote format, if applicable
|
||||||
*/
|
*/
|
||||||
function getAddressedToInfo(input) {
|
function getAddressedToInfo(input) {
|
||||||
input = input.trim();
|
input = input.trim();
|
||||||
|
@ -41,17 +47,26 @@ function getAddressedToInfo(input) {
|
||||||
if (firstAtPos < 0) {
|
if (firstAtPos < 0) {
|
||||||
let addr = Address.fromString(input);
|
let addr = Address.fromString(input);
|
||||||
if (Address.isValidAddress(addr)) {
|
if (Address.isValidAddress(addr)) {
|
||||||
return { flavor: MessageConst.AddressFlavor.FTN, remote: input };
|
return {
|
||||||
|
flavor: MessageConst.AddressFlavor.FTN,
|
||||||
|
remote: input,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const lessThanPos = input.indexOf('<');
|
const lessThanPos = input.indexOf('<');
|
||||||
if (lessThanPos < 0) {
|
if (lessThanPos < 0) {
|
||||||
return { name: input, flavor: MessageConst.AddressFlavor.Local };
|
return {
|
||||||
|
name: input,
|
||||||
|
flavor: MessageConst.AddressFlavor.Local,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const greaterThanPos = input.indexOf('>');
|
const greaterThanPos = input.indexOf('>');
|
||||||
if (greaterThanPos < lessThanPos) {
|
if (greaterThanPos < lessThanPos) {
|
||||||
return { name: input, flavor: MessageConst.AddressFlavor.Local };
|
return {
|
||||||
|
name: input,
|
||||||
|
flavor: MessageConst.AddressFlavor.Local,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = Address.fromString(input.slice(lessThanPos + 1, greaterThanPos));
|
addr = Address.fromString(input.slice(lessThanPos + 1, greaterThanPos));
|
||||||
|
@ -93,7 +108,10 @@ function getAddressedToInfo(input) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name: input, flavor: MessageConst.AddressFlavor.Local };
|
return {
|
||||||
|
name: input,
|
||||||
|
flavor: MessageConst.AddressFlavor.Local,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = input.match(EMAIL_REGEX);
|
let m = input.match(EMAIL_REGEX);
|
||||||
|
@ -107,7 +125,10 @@ function getAddressedToInfo(input) {
|
||||||
|
|
||||||
let addr = Address.fromString(input); // 5D?
|
let addr = Address.fromString(input); // 5D?
|
||||||
if (Address.isValidAddress(addr)) {
|
if (Address.isValidAddress(addr)) {
|
||||||
return { flavor: MessageConst.AddressFlavor.FTN, remote: addr.toString() };
|
return {
|
||||||
|
flavor: MessageConst.AddressFlavor.FTN,
|
||||||
|
remote: addr.toString(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
addr = Address.fromString(input.slice(firstAtPos + 1).trim());
|
addr = Address.fromString(input.slice(firstAtPos + 1).trim());
|
||||||
|
@ -172,6 +193,17 @@ function messageInfoFromAddressedToInfo(addressInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQuotePrefixFromName(name) {
|
function getQuotePrefixFromName(name) {
|
||||||
const addrInfo = getAddressedToInfo(name);
|
const addressInfo = getAddressedToInfo(name);
|
||||||
return getQuotePrefix(addrInfo.name || name);
|
return getQuotePrefix(addressInfo.name || name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReplyToMessagePrefix(name) {
|
||||||
|
const addressInfo = getAddressedToInfo(name);
|
||||||
|
|
||||||
|
// currently only ActivityPub
|
||||||
|
if (addressInfo.flavor === MessageConst.AddressFlavor.ActivityPub) {
|
||||||
|
return `@${addressInfo.name} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue