mirror of
https://github.com/flarum/framework.git
synced 2025-03-24 23:55:16 +08:00
Update for composer branch
This commit is contained in:
parent
36607a92cc
commit
ccda85cc29
2
extensions/mentions/.gitignore
vendored
2
extensions/mentions/.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
composer.phar
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
bower_components
|
||||
node_modules
|
@ -9,6 +9,17 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
use Flarum\Mentions\Listener;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
|
||||
return 'Flarum\Mentions\Extension';
|
||||
return function (Dispatcher $events, Factory $views) {
|
||||
$events->subscribe(Listener\AddClientAssets::class);
|
||||
$events->subscribe(Listener\AddPostMentionedByRelationship::class);
|
||||
$events->subscribe(Listener\FormatPostMentions::class);
|
||||
$events->subscribe(Listener\FormatUserMentions::class);
|
||||
$events->subscribe(Listener\UpdatePostMentionsMetadata::class);
|
||||
$events->subscribe(Listener\UpdateUserMentionsMetadata::class);
|
||||
|
||||
$views->addNamespace('flarum-mentions', __DIR__.'/views');
|
||||
};
|
||||
|
@ -1,10 +1,34 @@
|
||||
{
|
||||
"name": "flarum/mentions",
|
||||
"description": "Mention and reply to specific posts and users.",
|
||||
"type": "flarum-extension",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Toby Zerner",
|
||||
"email": "toby.zerner@gmail.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/flarum/core/issues",
|
||||
"source": "https://github.com/flarum/mentions"
|
||||
},
|
||||
"require": {
|
||||
"flarum/core": "^0.1.0-beta.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Flarum\\Mentions\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"style": "phpcs --standard=PSR2 -np src"
|
||||
"extra": {
|
||||
"flarum-extension": {
|
||||
"title": "Mentions",
|
||||
"icon": {
|
||||
"name": "at",
|
||||
"backgroundColor": "#539EC1",
|
||||
"color": "#fff"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "mentions",
|
||||
"title": "Mentions",
|
||||
"description": "Mention and reply to specific posts and users.",
|
||||
"keywords": ["discussions"],
|
||||
"version": "0.1.0-beta.2",
|
||||
"author": {
|
||||
"name": "Toby Zerner",
|
||||
"email": "toby@flarum.org",
|
||||
"homepage": "http://tobyzerner.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"flarum": ">=0.1.0-beta.2"
|
||||
},
|
||||
"support": {
|
||||
"source": "https://github.com/flarum/mentions",
|
||||
"issues": "https://github.com/flarum/core/issues"
|
||||
},
|
||||
"icon": {
|
||||
"name": "at",
|
||||
"backgroundColor": "#539EC1",
|
||||
"color": "#fff"
|
||||
}
|
||||
}
|
4
extensions/mentions/js/.gitignore
vendored
4
extensions/mentions/js/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
bower_components
|
||||
node_modules
|
||||
mithril.js
|
||||
dist
|
@ -2,7 +2,7 @@ var gulp = require('flarum-gulp');
|
||||
|
||||
gulp({
|
||||
modules: {
|
||||
'mentions': 'src/**/*.js'
|
||||
'flarum/mentions': 'src/**/*.js'
|
||||
},
|
||||
files: [
|
||||
'bower_components/textarea-caret-position/index.js'
|
||||
|
955
extensions/mentions/js/forum/dist/extension.js
vendored
Normal file
955
extensions/mentions/js/forum/dist/extension.js
vendored
Normal file
@ -0,0 +1,955 @@
|
||||
/* jshint browser: true */
|
||||
|
||||
(function () {
|
||||
|
||||
// The properties that we copy into a mirrored div.
|
||||
// Note that some browsers, such as Firefox,
|
||||
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
|
||||
// so we have to do every single property specifically.
|
||||
var properties = [
|
||||
'direction', // RTL support
|
||||
'boxSizing',
|
||||
'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
|
||||
'height',
|
||||
'overflowX',
|
||||
'overflowY', // copy the scrollbar for IE
|
||||
|
||||
'borderTopWidth',
|
||||
'borderRightWidth',
|
||||
'borderBottomWidth',
|
||||
'borderLeftWidth',
|
||||
'borderStyle',
|
||||
|
||||
'paddingTop',
|
||||
'paddingRight',
|
||||
'paddingBottom',
|
||||
'paddingLeft',
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/font
|
||||
'fontStyle',
|
||||
'fontVariant',
|
||||
'fontWeight',
|
||||
'fontStretch',
|
||||
'fontSize',
|
||||
'fontSizeAdjust',
|
||||
'lineHeight',
|
||||
'fontFamily',
|
||||
|
||||
'textAlign',
|
||||
'textTransform',
|
||||
'textIndent',
|
||||
'textDecoration', // might not make a difference, but better be safe
|
||||
|
||||
'letterSpacing',
|
||||
'wordSpacing',
|
||||
|
||||
'tabSize',
|
||||
'MozTabSize'
|
||||
|
||||
];
|
||||
|
||||
var isFirefox = window.mozInnerScreenX != null;
|
||||
|
||||
function getCaretCoordinates(element, position) {
|
||||
// mirrored div
|
||||
var div = document.createElement('div');
|
||||
div.id = 'input-textarea-caret-position-mirror-div';
|
||||
document.body.appendChild(div);
|
||||
|
||||
var style = div.style;
|
||||
var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
|
||||
|
||||
// default textarea styles
|
||||
style.whiteSpace = 'pre-wrap';
|
||||
if (element.nodeName !== 'INPUT')
|
||||
style.wordWrap = 'break-word'; // only for textarea-s
|
||||
|
||||
// position off-screen
|
||||
style.position = 'absolute'; // required to return coordinates properly
|
||||
style.visibility = 'hidden'; // not 'display: none' because we want rendering
|
||||
|
||||
// transfer the element's properties to the div
|
||||
properties.forEach(function (prop) {
|
||||
style[prop] = computed[prop];
|
||||
});
|
||||
|
||||
if (isFirefox) {
|
||||
// Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
|
||||
if (element.scrollHeight > parseInt(computed.height))
|
||||
style.overflowY = 'scroll';
|
||||
} else {
|
||||
style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
|
||||
}
|
||||
|
||||
div.textContent = element.value.substring(0, position);
|
||||
// the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
|
||||
if (element.nodeName === 'INPUT')
|
||||
div.textContent = div.textContent.replace(/\s/g, "\u00a0");
|
||||
|
||||
var span = document.createElement('span');
|
||||
// Wrapping must be replicated *exactly*, including when a long word gets
|
||||
// onto the next line, with whitespace at the end of the line before (#7).
|
||||
// The *only* reliable way to do that is to copy the *entire* rest of the
|
||||
// textarea's content into the <span> created at the caret position.
|
||||
// for inputs, just '.' would be enough, but why bother?
|
||||
span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all
|
||||
div.appendChild(span);
|
||||
|
||||
var coordinates = {
|
||||
top: span.offsetTop + parseInt(computed['borderTopWidth']),
|
||||
left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
|
||||
};
|
||||
|
||||
document.body.removeChild(div);
|
||||
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
if (typeof module != "undefined" && typeof module.exports != "undefined") {
|
||||
module.exports = getCaretCoordinates;
|
||||
} else {
|
||||
window.getCaretCoordinates = getCaretCoordinates;
|
||||
}
|
||||
|
||||
}());
|
||||
;System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'flarum/components/ComposerBody', 'flarum/helpers/avatar', 'flarum/helpers/username', 'flarum/helpers/highlight', 'flarum/utils/string', 'flarum/mentions/components/AutocompleteDropdown'], function (_export) {
|
||||
/*global getCaretCoordinates*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var extend, ComposerBody, avatar, usernameHelper, highlight, truncate, AutocompleteDropdown;
|
||||
|
||||
_export('default', addComposerAutocomplete);
|
||||
|
||||
function addComposerAutocomplete() {
|
||||
extend(ComposerBody.prototype, 'config', function (original, isInitialized) {
|
||||
if (isInitialized) return;
|
||||
|
||||
var composer = this;
|
||||
var $container = $('<div class="ComposerBody-mentionsDropdownContainer"></div>');
|
||||
var dropdown = new AutocompleteDropdown({ items: [] });
|
||||
var $textarea = this.$('textarea');
|
||||
var searched = [];
|
||||
var mentionStart = undefined;
|
||||
var typed = undefined;
|
||||
var searchTimeout = undefined;
|
||||
|
||||
var applySuggestion = function applySuggestion(replacement) {
|
||||
var insert = replacement + ' ';
|
||||
|
||||
var content = composer.content();
|
||||
composer.editor.setValue(content.substring(0, mentionStart - 1) + insert + content.substr($textarea[0].selectionStart));
|
||||
|
||||
var index = mentionStart - 1 + insert.length;
|
||||
composer.editor.setSelectionRange(index, index);
|
||||
|
||||
dropdown.hide();
|
||||
};
|
||||
|
||||
$textarea.after($container).on('keydown', dropdown.navigate.bind(dropdown)).on('click keyup', function (e) {
|
||||
var _this = this;
|
||||
|
||||
// Up, down, enter, tab, escape, left, right.
|
||||
if ([9, 13, 27, 40, 38, 37, 39].indexOf(e.which) !== -1) return;
|
||||
|
||||
var cursor = this.selectionStart;
|
||||
|
||||
if (this.selectionEnd - cursor > 0) return;
|
||||
|
||||
// Search backwards from the cursor for an '@' symbol, without any
|
||||
// intervening whitespace. If we find one, we will want to show the
|
||||
// autocomplete dropdown!
|
||||
var value = this.value;
|
||||
mentionStart = 0;
|
||||
for (var i = cursor - 1; i >= 0; i--) {
|
||||
var character = value.substr(i, 1);
|
||||
if (/\s/.test(character)) break;
|
||||
if (character === '@') {
|
||||
mentionStart = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dropdown.hide();
|
||||
dropdown.active = false;
|
||||
|
||||
if (mentionStart) {
|
||||
(function () {
|
||||
typed = value.substring(mentionStart, cursor).toLowerCase();
|
||||
|
||||
var makeSuggestion = function makeSuggestion(user, replacement, content) {
|
||||
var className = arguments.length <= 3 || arguments[3] === undefined ? '' : arguments[3];
|
||||
|
||||
var username = usernameHelper(user);
|
||||
if (typed) {
|
||||
username.children[0] = highlight(username.children[0], typed);
|
||||
}
|
||||
|
||||
return m(
|
||||
'button',
|
||||
{ className: 'PostPreview ' + className,
|
||||
onclick: function () {
|
||||
return applySuggestion(replacement);
|
||||
},
|
||||
onmouseenter: function () {
|
||||
dropdown.setIndex($(this).parent().index());
|
||||
} },
|
||||
m(
|
||||
'span',
|
||||
{ className: 'PostPreview-content' },
|
||||
avatar(user),
|
||||
username,
|
||||
' ',
|
||||
' ',
|
||||
content
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
var buildSuggestions = function buildSuggestions() {
|
||||
var suggestions = [];
|
||||
|
||||
// If the user is replying to a discussion, or if they are editing a
|
||||
// post, then we can suggest other posts in the discussion to mention.
|
||||
// We will add the 5 most recent comments in the discussion which
|
||||
// match any username characters that have been typed.
|
||||
var composerPost = composer.props.post;
|
||||
var discussion = composerPost && composerPost.discussion() || composer.props.discussion;
|
||||
if (discussion) {
|
||||
discussion.posts().filter(function (post) {
|
||||
return post && post.contentType() === 'comment' && (!composerPost || post.number() < composerPost.number());
|
||||
}).sort(function (a, b) {
|
||||
return b.time() - a.time();
|
||||
}).filter(function (post) {
|
||||
var user = post.user();
|
||||
return user && user.username().toLowerCase().substr(0, typed.length) === typed;
|
||||
}).splice(0, 5).forEach(function (post) {
|
||||
var user = post.user();
|
||||
suggestions.push(makeSuggestion(user, '@' + user.username() + '#' + post.id(), [app.trans('flarum-mentions.forum.reply_to_post', { number: post.number() }), ' — ', truncate(post.contentPlain(), 200)], 'MentionsDropdown-post'));
|
||||
});
|
||||
}
|
||||
|
||||
// If the user has started to type a username, then suggest users
|
||||
// matching that username.
|
||||
if (typed) {
|
||||
app.store.all('users').forEach(function (user) {
|
||||
if (user.username().toLowerCase().substr(0, typed.length) !== typed) return;
|
||||
|
||||
suggestions.push(makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user'));
|
||||
});
|
||||
}
|
||||
|
||||
if (suggestions.length) {
|
||||
dropdown.props.items = suggestions;
|
||||
m.render($container[0], dropdown.render());
|
||||
|
||||
dropdown.show();
|
||||
var coordinates = getCaretCoordinates(_this, mentionStart);
|
||||
var width = dropdown.$().outerWidth();
|
||||
var height = dropdown.$().outerHeight();
|
||||
var _parent = dropdown.$().offsetParent();
|
||||
var left = coordinates.left;
|
||||
var _top = coordinates.top + 15;
|
||||
if (_top + height > _parent.height()) {
|
||||
_top = coordinates.top - height - 15;
|
||||
}
|
||||
if (left + width > _parent.width()) {
|
||||
left = _parent.width() - width;
|
||||
}
|
||||
dropdown.show(left, _top);
|
||||
}
|
||||
};
|
||||
|
||||
buildSuggestions();
|
||||
|
||||
dropdown.setIndex(0);
|
||||
dropdown.$().scrollTop(0);
|
||||
dropdown.active = true;
|
||||
|
||||
clearTimeout(searchTimeout);
|
||||
if (typed) {
|
||||
searchTimeout = setTimeout(function () {
|
||||
var typedLower = typed.toLowerCase();
|
||||
if (searched.indexOf(typedLower) === -1) {
|
||||
app.store.find('users', { q: typed, page: { limit: 5 } }).then(function () {
|
||||
if (dropdown.active) buildSuggestions();
|
||||
});
|
||||
searched.push(typedLower);
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
})();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_flarumExtend) {
|
||||
extend = _flarumExtend.extend;
|
||||
}, function (_flarumComponentsComposerBody) {
|
||||
ComposerBody = _flarumComponentsComposerBody['default'];
|
||||
}, function (_flarumHelpersAvatar) {
|
||||
avatar = _flarumHelpersAvatar['default'];
|
||||
}, function (_flarumHelpersUsername) {
|
||||
usernameHelper = _flarumHelpersUsername['default'];
|
||||
}, function (_flarumHelpersHighlight) {
|
||||
highlight = _flarumHelpersHighlight['default'];
|
||||
}, function (_flarumUtilsString) {
|
||||
truncate = _flarumUtilsString.truncate;
|
||||
}, function (_flarumMentionsComponentsAutocompleteDropdown) {
|
||||
AutocompleteDropdown = _flarumMentionsComponentsAutocompleteDropdown['default'];
|
||||
}],
|
||||
execute: function () {}
|
||||
};
|
||||
});;System.register('flarum/mentions/addMentionedByList', ['flarum/extend', 'flarum/Model', 'flarum/models/Post', 'flarum/components/CommentPost', 'flarum/components/PostPreview', 'flarum/helpers/punctuateSeries', 'flarum/helpers/username', 'flarum/helpers/icon'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var extend, Model, Post, CommentPost, PostPreview, punctuateSeries, username, icon;
|
||||
|
||||
_export('default', addMentionedByList);
|
||||
|
||||
function addMentionedByList() {
|
||||
Post.prototype.mentionedBy = Model.hasMany('mentionedBy');
|
||||
|
||||
extend(CommentPost.prototype, 'footerItems', function (items) {
|
||||
var _this = this;
|
||||
|
||||
var post = this.props.post;
|
||||
var replies = post.mentionedBy();
|
||||
|
||||
if (replies && replies.length) {
|
||||
var _ret = (function () {
|
||||
// If there is only one reply, and it's adjacent to this post, we don't
|
||||
// really need to show the list.
|
||||
if (replies.length === 1 && replies[0].number() === post.number() + 1) {
|
||||
return {
|
||||
v: undefined
|
||||
};
|
||||
}
|
||||
|
||||
var hidePreview = function hidePreview() {
|
||||
_this.$('.Post-mentionedBy-preview').removeClass('in').one('transitionend', function () {
|
||||
$(this).hide();
|
||||
});
|
||||
};
|
||||
|
||||
var config = function config(element, isInitialized) {
|
||||
if (isInitialized) return;
|
||||
|
||||
var $this = $(element);
|
||||
var timeout = undefined;
|
||||
|
||||
var $preview = $('<ul class="Dropdown-menu Post-mentionedBy-preview fade"/>');
|
||||
$this.append($preview);
|
||||
|
||||
$this.children().hover(function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function () {
|
||||
if (!$preview.hasClass('in') && $preview.is(':visible')) return;
|
||||
|
||||
// When the user hovers their mouse over the list of people who have
|
||||
// replied to the post, render a list of reply previews into a
|
||||
// popup.
|
||||
m.render($preview[0], replies.map(function (reply) {
|
||||
return m(
|
||||
'li',
|
||||
{ 'data-number': reply.number() },
|
||||
PostPreview.component({
|
||||
post: reply,
|
||||
onclick: hidePreview
|
||||
})
|
||||
);
|
||||
}));
|
||||
$preview.show();
|
||||
setTimeout(function () {
|
||||
return $preview.off('transitionend').addClass('in');
|
||||
});
|
||||
}, 500);
|
||||
}, function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(hidePreview, 250);
|
||||
});
|
||||
|
||||
// Whenever the user hovers their mouse over a particular name in the
|
||||
// list of repliers, highlight the corresponding post in the preview
|
||||
// popup.
|
||||
$this.find('.Post-mentionedBy-summary a').hover(function () {
|
||||
$preview.find('[data-number="' + $(this).data('number') + '"]').addClass('active');
|
||||
}, function () {
|
||||
$preview.find('[data-number]').removeClass('active');
|
||||
});
|
||||
};
|
||||
|
||||
// Create a list of unique users who have replied. So even if a user has
|
||||
// replied twice, they will only be in this array once.
|
||||
var used = [];
|
||||
var repliers = replies.filter(function (reply) {
|
||||
var user = reply.user();
|
||||
var id = user && user.id();
|
||||
if (used.indexOf(id) === -1) {
|
||||
used.push(id);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var names = repliers.sort(function (a) {
|
||||
return a === app.session.user ? -1 : 1;
|
||||
}).map(function (reply) {
|
||||
var user = reply.user();
|
||||
|
||||
return m(
|
||||
'a',
|
||||
{ href: app.route.post(reply),
|
||||
config: m.route,
|
||||
onclick: hidePreview,
|
||||
'data-number': reply.number() },
|
||||
app.session.user === user ? app.trans('flarum-mentions.forum.you') : username(user)
|
||||
);
|
||||
});
|
||||
|
||||
items.add('replies', m(
|
||||
'div',
|
||||
{ className: 'Post-mentionedBy', config: config },
|
||||
m(
|
||||
'span',
|
||||
{ className: 'Post-mentionedBy-summary' },
|
||||
icon('reply'),
|
||||
app.trans('flarum-mentions.forum.post_mentioned_by', {
|
||||
count: names.length,
|
||||
users: punctuateSeries(names)
|
||||
})
|
||||
)
|
||||
));
|
||||
})();
|
||||
|
||||
if (typeof _ret === 'object') return _ret.v;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_flarumExtend) {
|
||||
extend = _flarumExtend.extend;
|
||||
}, function (_flarumModel) {
|
||||
Model = _flarumModel['default'];
|
||||
}, function (_flarumModelsPost) {
|
||||
Post = _flarumModelsPost['default'];
|
||||
}, function (_flarumComponentsCommentPost) {
|
||||
CommentPost = _flarumComponentsCommentPost['default'];
|
||||
}, function (_flarumComponentsPostPreview) {
|
||||
PostPreview = _flarumComponentsPostPreview['default'];
|
||||
}, function (_flarumHelpersPunctuateSeries) {
|
||||
punctuateSeries = _flarumHelpersPunctuateSeries['default'];
|
||||
}, function (_flarumHelpersUsername) {
|
||||
username = _flarumHelpersUsername['default'];
|
||||
}, function (_flarumHelpersIcon) {
|
||||
icon = _flarumHelpersIcon['default'];
|
||||
}],
|
||||
execute: function () {}
|
||||
};
|
||||
});;System.register('flarum/mentions/addPostMentionPreviews', ['flarum/extend', 'flarum/components/CommentPost', 'flarum/components/PostPreview', 'flarum/components/LoadingIndicator'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var extend, CommentPost, PostPreview, LoadingIndicator;
|
||||
|
||||
_export('default', addPostMentionPreviews);
|
||||
|
||||
function addPostMentionPreviews() {
|
||||
extend(CommentPost.prototype, 'config', function () {
|
||||
var contentHtml = this.props.post.contentHtml();
|
||||
|
||||
if (contentHtml === this.oldPostContentHtml || this.isEditing()) return;
|
||||
|
||||
this.oldPostContentHtml = contentHtml;
|
||||
|
||||
var parentPost = this.props.post;
|
||||
var $parentPost = this.$();
|
||||
|
||||
this.$('.UserMention, .PostMention').each(function () {
|
||||
m.route.call(this, this, false, {}, { attrs: { href: this.getAttribute('href') } });
|
||||
});
|
||||
|
||||
this.$('.PostMention').each(function () {
|
||||
var $this = $(this);
|
||||
var id = $this.data('id');
|
||||
var timeout = undefined;
|
||||
|
||||
// Wrap the mention link in a wrapper element so that we can insert a
|
||||
// preview popup as its sibling and relatively position it.
|
||||
var $preview = $('<ul class="Dropdown-menu PostMention-preview fade"/>');
|
||||
$parentPost.append($preview);
|
||||
|
||||
var getPostElement = function getPostElement() {
|
||||
return $('.PostStream-item[data-id="' + id + '"]');
|
||||
};
|
||||
|
||||
var showPreview = function showPreview() {
|
||||
// When the user hovers their mouse over the mention, look for the
|
||||
// post that it's referring to in the stream, and determine if it's
|
||||
// in the viewport. If it is, we will "pulsate" it.
|
||||
var $post = getPostElement();
|
||||
var visible = false;
|
||||
if ($post.length) {
|
||||
var _top = $post.offset().top;
|
||||
var scrollTop = window.pageYOffset;
|
||||
if (_top > scrollTop && _top + $post.height() < scrollTop + $(window).height()) {
|
||||
$post.addClass('pulsate');
|
||||
visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we will show a popup preview of the post. If the post
|
||||
// hasn't yet been loaded, we will need to do that.
|
||||
if (!visible) {
|
||||
(function () {
|
||||
// Position the preview so that it appears above the mention.
|
||||
// (The offsetParent should be .Post-body.)
|
||||
var positionPreview = function positionPreview() {
|
||||
$preview.show().css('top', $this.offset().top - $parentPost.offset().top - $preview.outerHeight(true)).css('left', $this.offsetParent().offset().left - $parentPost.offset().left).css('max-width', $this.offsetParent().width());
|
||||
};
|
||||
|
||||
var showPost = function showPost(post) {
|
||||
var discussion = post.discussion();
|
||||
|
||||
m.render($preview[0], [discussion !== parentPost.discussion() ? m(
|
||||
'li',
|
||||
null,
|
||||
m(
|
||||
'span',
|
||||
{ className: 'PostMention-preview-discussion' },
|
||||
discussion.title()
|
||||
)
|
||||
) : '', m(
|
||||
'li',
|
||||
null,
|
||||
PostPreview.component({ post: post })
|
||||
)]);
|
||||
positionPreview();
|
||||
};
|
||||
|
||||
var post = app.store.getById('posts', id);
|
||||
if (post && post.discussion()) {
|
||||
showPost(post);
|
||||
} else {
|
||||
m.render($preview[0], LoadingIndicator.component());
|
||||
app.store.find('posts', id).then(showPost);
|
||||
positionPreview();
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
return $preview.off('transitionend').addClass('in');
|
||||
});
|
||||
})();
|
||||
}
|
||||
};
|
||||
|
||||
var hidePreview = function hidePreview() {
|
||||
getPostElement().removeClass('pulsate');
|
||||
if ($preview.hasClass('in')) {
|
||||
$preview.removeClass('in').one('transitionend', function () {
|
||||
return $preview.hide();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$this.on('touchstart', function (e) {
|
||||
return e.preventDefault();
|
||||
});
|
||||
|
||||
$this.add($preview).hover(function () {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(showPreview, 250);
|
||||
}, function () {
|
||||
clearTimeout(timeout);
|
||||
getPostElement().removeClass('pulsate');
|
||||
timeout = setTimeout(hidePreview, 250);
|
||||
}).on('touchend', function (e) {
|
||||
showPreview();
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(document).on('touchend', hidePreview);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_flarumExtend) {
|
||||
extend = _flarumExtend.extend;
|
||||
}, function (_flarumComponentsCommentPost) {
|
||||
CommentPost = _flarumComponentsCommentPost['default'];
|
||||
}, function (_flarumComponentsPostPreview) {
|
||||
PostPreview = _flarumComponentsPostPreview['default'];
|
||||
}, function (_flarumComponentsLoadingIndicator) {
|
||||
LoadingIndicator = _flarumComponentsLoadingIndicator['default'];
|
||||
}],
|
||||
execute: function () {}
|
||||
};
|
||||
});;System.register('flarum/mentions/addPostReplyAction', ['flarum/extend', 'flarum/components/Button', 'flarum/components/CommentPost', 'flarum/utils/DiscussionControls'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var extend, Button, CommentPost, DiscussionControls;
|
||||
return {
|
||||
setters: [function (_flarumExtend) {
|
||||
extend = _flarumExtend.extend;
|
||||
}, function (_flarumComponentsButton) {
|
||||
Button = _flarumComponentsButton['default'];
|
||||
}, function (_flarumComponentsCommentPost) {
|
||||
CommentPost = _flarumComponentsCommentPost['default'];
|
||||
}, function (_flarumUtilsDiscussionControls) {
|
||||
DiscussionControls = _flarumUtilsDiscussionControls['default'];
|
||||
}],
|
||||
execute: function () {
|
||||
_export('default', function () {
|
||||
extend(CommentPost.prototype, 'actionItems', function (items) {
|
||||
var post = this.props.post;
|
||||
|
||||
if (post.isHidden() || app.session.user && !post.discussion().canReply()) return;
|
||||
|
||||
function insertMention(component, quote) {
|
||||
var mention = '@' + post.user().username() + '#' + post.id() + ' ';
|
||||
|
||||
// If the composer is empty, then assume we're starting a new reply.
|
||||
// In which case we don't want the user to have to confirm if they
|
||||
// close the composer straight away.
|
||||
if (!component.content()) {
|
||||
component.props.originalContent = mention;
|
||||
}
|
||||
|
||||
component.editor.insertAtCursor((component.editor.getSelectionRange()[0] > 0 ? '\n\n' : '') + (quote ? '> ' + mention + quote.trim().replace(/\n/g, '\n> ') + '\n\n' : mention));
|
||||
}
|
||||
|
||||
items.add('reply', Button.component({
|
||||
className: 'Button Button--link',
|
||||
children: app.trans('flarum-mentions.forum.reply_link'),
|
||||
onclick: function onclick() {
|
||||
var quote = window.getSelection().toString();
|
||||
|
||||
var component = app.composer.component;
|
||||
if (component && component.props.post && component.props.post.discussion() === post.discussion()) {
|
||||
insertMention(component, quote);
|
||||
} else {
|
||||
DiscussionControls.replyAction.call(post.discussion()).then(function (newComponent) {
|
||||
return insertMention(newComponent, quote);
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});;System.register('flarum/mentions/main', ['flarum/extend', 'flarum/app', 'flarum/components/NotificationGrid', 'flarum/utils/string', 'flarum/mentions/addPostMentionPreviews', 'flarum/mentions/addMentionedByList', 'flarum/mentions/addPostReplyAction', 'flarum/mentions/addComposerAutocomplete', 'flarum/mentions/components/PostMentionedNotification', 'flarum/mentions/components/UserMentionedNotification'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var extend, app, NotificationGrid, getPlainContent, addPostMentionPreviews, addMentionedByList, addPostReplyAction, addComposerAutocomplete, PostMentionedNotification, UserMentionedNotification;
|
||||
return {
|
||||
setters: [function (_flarumExtend) {
|
||||
extend = _flarumExtend.extend;
|
||||
}, function (_flarumApp) {
|
||||
app = _flarumApp['default'];
|
||||
}, function (_flarumComponentsNotificationGrid) {
|
||||
NotificationGrid = _flarumComponentsNotificationGrid['default'];
|
||||
}, function (_flarumUtilsString) {
|
||||
getPlainContent = _flarumUtilsString.getPlainContent;
|
||||
}, function (_flarumMentionsAddPostMentionPreviews) {
|
||||
addPostMentionPreviews = _flarumMentionsAddPostMentionPreviews['default'];
|
||||
}, function (_flarumMentionsAddMentionedByList) {
|
||||
addMentionedByList = _flarumMentionsAddMentionedByList['default'];
|
||||
}, function (_flarumMentionsAddPostReplyAction) {
|
||||
addPostReplyAction = _flarumMentionsAddPostReplyAction['default'];
|
||||
}, function (_flarumMentionsAddComposerAutocomplete) {
|
||||
addComposerAutocomplete = _flarumMentionsAddComposerAutocomplete['default'];
|
||||
}, function (_flarumMentionsComponentsPostMentionedNotification) {
|
||||
PostMentionedNotification = _flarumMentionsComponentsPostMentionedNotification['default'];
|
||||
}, function (_flarumMentionsComponentsUserMentionedNotification) {
|
||||
UserMentionedNotification = _flarumMentionsComponentsUserMentionedNotification['default'];
|
||||
}],
|
||||
execute: function () {
|
||||
|
||||
app.initializers.add('flarum-mentions', function () {
|
||||
// For every mention of a post inside a post's content, set up a hover handler
|
||||
// that shows a preview of the mentioned post.
|
||||
addPostMentionPreviews();
|
||||
|
||||
// In the footer of each post, show information about who has replied (i.e.
|
||||
// who the post has been mentioned by).
|
||||
addMentionedByList();
|
||||
|
||||
// Add a 'reply' control to the footer of each post. When clicked, it will
|
||||
// open up the composer and add a post mention to its contents.
|
||||
addPostReplyAction();
|
||||
|
||||
// After typing '@' in the composer, show a dropdown suggesting a bunch of
|
||||
// posts or users that the user could mention.
|
||||
addComposerAutocomplete();
|
||||
|
||||
app.notificationComponents.postMentioned = PostMentionedNotification;
|
||||
app.notificationComponents.userMentioned = UserMentionedNotification;
|
||||
|
||||
// Add notification preferences.
|
||||
extend(NotificationGrid.prototype, 'notificationTypes', function (items) {
|
||||
items.add('postMentioned', {
|
||||
name: 'postMentioned',
|
||||
icon: 'reply',
|
||||
label: app.trans('flarum-mentions.forum.notify_post_mentioned')
|
||||
});
|
||||
|
||||
items.add('userMentioned', {
|
||||
name: 'userMentioned',
|
||||
icon: 'at',
|
||||
label: app.trans('flarum-mentions.forum.notify_user_mentioned')
|
||||
});
|
||||
});
|
||||
|
||||
getPlainContent.removeSelectors.push('a.PostMention');
|
||||
});
|
||||
}
|
||||
};
|
||||
});;System.register('flarum/mentions/components/AutocompleteDropdown', ['flarum/Component'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var Component, AutocompleteDropdown;
|
||||
return {
|
||||
setters: [function (_flarumComponent) {
|
||||
Component = _flarumComponent['default'];
|
||||
}],
|
||||
execute: function () {
|
||||
AutocompleteDropdown = (function (_Component) {
|
||||
babelHelpers.inherits(AutocompleteDropdown, _Component);
|
||||
|
||||
function AutocompleteDropdown() {
|
||||
babelHelpers.classCallCheck(this, AutocompleteDropdown);
|
||||
|
||||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
babelHelpers.get(Object.getPrototypeOf(AutocompleteDropdown.prototype), 'constructor', this).apply(this, args);
|
||||
|
||||
this.active = false;
|
||||
this.index = 0;
|
||||
this.keyWasJustPressed = false;
|
||||
}
|
||||
|
||||
babelHelpers.createClass(AutocompleteDropdown, [{
|
||||
key: 'view',
|
||||
value: function view() {
|
||||
return m(
|
||||
'ul',
|
||||
{ className: 'Dropdown-menu MentionsDropdown' },
|
||||
this.props.items.map(function (item) {
|
||||
return m(
|
||||
'li',
|
||||
null,
|
||||
item
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}, {
|
||||
key: 'show',
|
||||
value: function show(left, top) {
|
||||
this.$().show().css({
|
||||
left: left + 'px',
|
||||
top: top + 'px'
|
||||
});
|
||||
this.active = true;
|
||||
}
|
||||
}, {
|
||||
key: 'hide',
|
||||
value: function hide() {
|
||||
this.$().hide();
|
||||
this.active = false;
|
||||
}
|
||||
}, {
|
||||
key: 'navigate',
|
||||
value: function navigate(e) {
|
||||
var _this = this;
|
||||
|
||||
if (!this.active) return;
|
||||
|
||||
switch (e.which) {
|
||||
case 40:case 38:
|
||||
// Down/Up
|
||||
this.keyWasJustPressed = true;
|
||||
this.setIndex(this.index + (e.which === 40 ? 1 : -1), true);
|
||||
clearTimeout(this.keyWasJustPressedTimeout);
|
||||
this.keyWasJustPressedTimeout = setTimeout(function () {
|
||||
return _this.keyWasJustPressed = false;
|
||||
}, 500);
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 13:case 9:
|
||||
// Enter/Tab
|
||||
this.$('li').eq(this.index).find('button').click();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 27:
|
||||
// Escape
|
||||
this.hide();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
default:
|
||||
// no default
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'setIndex',
|
||||
value: function setIndex(index, scrollToItem) {
|
||||
if (this.keyWasJustPressed && !scrollToItem) return;
|
||||
|
||||
var $dropdown = this.$();
|
||||
var $items = $dropdown.find('li');
|
||||
var rangedIndex = index;
|
||||
|
||||
if (rangedIndex < 0) {
|
||||
rangedIndex = $items.length - 1;
|
||||
} else if (rangedIndex >= $items.length) {
|
||||
rangedIndex = 0;
|
||||
}
|
||||
|
||||
this.index = rangedIndex;
|
||||
|
||||
var $item = $items.removeClass('active').eq(rangedIndex).addClass('active');
|
||||
|
||||
if (scrollToItem) {
|
||||
var dropdownScroll = $dropdown.scrollTop();
|
||||
var dropdownTop = $dropdown.offset().top;
|
||||
var dropdownBottom = dropdownTop + $dropdown.outerHeight();
|
||||
var itemTop = $item.offset().top;
|
||||
var itemBottom = itemTop + $item.outerHeight();
|
||||
|
||||
var scrollTop = undefined;
|
||||
if (itemTop < dropdownTop) {
|
||||
scrollTop = dropdownScroll - dropdownTop + itemTop - parseInt($dropdown.css('padding-top'), 10);
|
||||
} else if (itemBottom > dropdownBottom) {
|
||||
scrollTop = dropdownScroll - dropdownBottom + itemBottom + parseInt($dropdown.css('padding-bottom'), 10);
|
||||
}
|
||||
|
||||
if (typeof scrollTop !== 'undefined') {
|
||||
$dropdown.stop(true).animate({ scrollTop: scrollTop }, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
return AutocompleteDropdown;
|
||||
})(Component);
|
||||
|
||||
_export('default', AutocompleteDropdown);
|
||||
}
|
||||
};
|
||||
});;System.register('flarum/mentions/components/PostMentionedNotification', ['flarum/components/Notification', 'flarum/helpers/username', 'flarum/helpers/punctuate'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var Notification, username, punctuate, PostMentionedNotification;
|
||||
return {
|
||||
setters: [function (_flarumComponentsNotification) {
|
||||
Notification = _flarumComponentsNotification['default'];
|
||||
}, function (_flarumHelpersUsername) {
|
||||
username = _flarumHelpersUsername['default'];
|
||||
}, function (_flarumHelpersPunctuate) {
|
||||
punctuate = _flarumHelpersPunctuate['default'];
|
||||
}],
|
||||
execute: function () {
|
||||
PostMentionedNotification = (function (_Notification) {
|
||||
babelHelpers.inherits(PostMentionedNotification, _Notification);
|
||||
|
||||
function PostMentionedNotification() {
|
||||
babelHelpers.classCallCheck(this, PostMentionedNotification);
|
||||
babelHelpers.get(Object.getPrototypeOf(PostMentionedNotification.prototype), 'constructor', this).apply(this, arguments);
|
||||
}
|
||||
|
||||
babelHelpers.createClass(PostMentionedNotification, [{
|
||||
key: 'icon',
|
||||
value: function icon() {
|
||||
return 'reply';
|
||||
}
|
||||
}, {
|
||||
key: 'href',
|
||||
value: function href() {
|
||||
var notification = this.props.notification;
|
||||
var post = notification.subject();
|
||||
var auc = notification.additionalUnreadCount();
|
||||
var content = notification.content();
|
||||
|
||||
return app.route.discussion(post.discussion(), auc ? post.number() : content && content.replyNumber);
|
||||
}
|
||||
}, {
|
||||
key: 'content',
|
||||
value: function content() {
|
||||
var notification = this.props.notification;
|
||||
var auc = notification.additionalUnreadCount();
|
||||
var user = notification.sender();
|
||||
|
||||
return app.trans('flarum-mentions.forum.post_mentioned_notification', {
|
||||
user: user,
|
||||
username: auc ? punctuate([username(user), app.trans('flarum-mentions.forum.others', { count: auc })]) : undefined
|
||||
});
|
||||
}
|
||||
}, {
|
||||
key: 'excerpt',
|
||||
value: function excerpt() {
|
||||
return this.props.notification.subject().contentPlain();
|
||||
}
|
||||
}]);
|
||||
return PostMentionedNotification;
|
||||
})(Notification);
|
||||
|
||||
_export('default', PostMentionedNotification);
|
||||
}
|
||||
};
|
||||
});;System.register('flarum/mentions/components/UserMentionedNotification', ['flarum/components/Notification'], function (_export) {
|
||||
'use strict';
|
||||
|
||||
var Notification, UserMentionedNotification;
|
||||
return {
|
||||
setters: [function (_flarumComponentsNotification) {
|
||||
Notification = _flarumComponentsNotification['default'];
|
||||
}],
|
||||
execute: function () {
|
||||
UserMentionedNotification = (function (_Notification) {
|
||||
babelHelpers.inherits(UserMentionedNotification, _Notification);
|
||||
|
||||
function UserMentionedNotification() {
|
||||
babelHelpers.classCallCheck(this, UserMentionedNotification);
|
||||
babelHelpers.get(Object.getPrototypeOf(UserMentionedNotification.prototype), 'constructor', this).apply(this, arguments);
|
||||
}
|
||||
|
||||
babelHelpers.createClass(UserMentionedNotification, [{
|
||||
key: 'icon',
|
||||
value: function icon() {
|
||||
return 'at';
|
||||
}
|
||||
}, {
|
||||
key: 'href',
|
||||
value: function href() {
|
||||
var post = this.props.notification.subject();
|
||||
|
||||
return app.route.discussion(post.discussion(), post.number());
|
||||
}
|
||||
}, {
|
||||
key: 'content',
|
||||
value: function content() {
|
||||
var user = this.props.notification.sender();
|
||||
|
||||
return app.trans('flarum-mentions.forum.user_mentioned_notification', { user: user });
|
||||
}
|
||||
}, {
|
||||
key: 'excerpt',
|
||||
value: function excerpt() {
|
||||
return this.props.notification.subject().contentPlain();
|
||||
}
|
||||
}]);
|
||||
return UserMentionedNotification;
|
||||
})(Notification);
|
||||
|
||||
_export('default', UserMentionedNotification);
|
||||
}
|
||||
};
|
||||
});
|
@ -7,7 +7,7 @@ import usernameHelper from 'flarum/helpers/username';
|
||||
import highlight from 'flarum/helpers/highlight';
|
||||
import { truncate } from 'flarum/utils/string';
|
||||
|
||||
import AutocompleteDropdown from 'mentions/components/AutocompleteDropdown';
|
||||
import AutocompleteDropdown from 'flarum/mentions/components/AutocompleteDropdown';
|
||||
|
||||
export default function addComposerAutocomplete() {
|
||||
extend(ComposerBody.prototype, 'config', function(original, isInitialized) {
|
||||
@ -79,7 +79,7 @@ export default function addComposerAutocomplete() {
|
||||
}}>
|
||||
<span className="PostPreview-content">
|
||||
{avatar(user)}
|
||||
{username}{' '}
|
||||
{username} {' '}
|
||||
{content}
|
||||
</span>
|
||||
</button>
|
||||
@ -108,7 +108,7 @@ export default function addComposerAutocomplete() {
|
||||
const user = post.user();
|
||||
suggestions.push(
|
||||
makeSuggestion(user, '@' + user.username() + '#' + post.id(), [
|
||||
app.trans('mentions.reply_to_post', {number: post.number()}), ' — ',
|
||||
app.trans('flarum-mentions.forum.reply_to_post', {number: post.number()}), ' — ',
|
||||
truncate(post.contentPlain(), 200)
|
||||
], 'MentionsDropdown-post')
|
||||
);
|
||||
|
@ -3,7 +3,7 @@ import Model from 'flarum/Model';
|
||||
import Post from 'flarum/models/Post';
|
||||
import CommentPost from 'flarum/components/CommentPost';
|
||||
import PostPreview from 'flarum/components/PostPreview';
|
||||
import punctuate from 'flarum/helpers/punctuate';
|
||||
import punctuateSeries from 'flarum/helpers/punctuateSeries';
|
||||
import username from 'flarum/helpers/username';
|
||||
import icon from 'flarum/helpers/icon';
|
||||
|
||||
@ -91,7 +91,7 @@ export default function addMentionedByList() {
|
||||
config={m.route}
|
||||
onclick={hidePreview}
|
||||
data-number={reply.number()}>
|
||||
{app.session.user === user ? app.trans('mentions.you') : username(user)}
|
||||
{app.session.user === user ? app.trans('flarum-mentions.forum.you') : username(user)}
|
||||
</a>
|
||||
);
|
||||
});
|
||||
@ -100,9 +100,9 @@ export default function addMentionedByList() {
|
||||
<div className="Post-mentionedBy" config={config}>
|
||||
<span className="Post-mentionedBy-summary">
|
||||
{icon('reply')}
|
||||
{app.trans('mentions.post_mentioned_by', {
|
||||
{app.trans('flarum-mentions.forum.post_mentioned_by', {
|
||||
count: names.length,
|
||||
users: punctuate(names)
|
||||
users: punctuateSeries(names)
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -30,7 +30,7 @@ export default function() {
|
||||
items.add('reply',
|
||||
Button.component({
|
||||
className: 'Button Button--link',
|
||||
children: app.trans('mentions.reply_link'),
|
||||
children: app.trans('flarum-mentions.forum.reply_link'),
|
||||
onclick: () => {
|
||||
const quote = window.getSelection().toString();
|
||||
|
||||
|
@ -21,11 +21,11 @@ export default class PostMentionedNotification extends Notification {
|
||||
const auc = notification.additionalUnreadCount();
|
||||
const user = notification.sender();
|
||||
|
||||
return app.trans('mentions.post_mentioned_notification', {
|
||||
return app.trans('flarum-mentions.forum.post_mentioned_notification', {
|
||||
user,
|
||||
username: auc ? punctuate([
|
||||
username(user),
|
||||
app.trans('mentions.others', {count: auc})
|
||||
app.trans('flarum-mentions.forum.others', {count: auc})
|
||||
]) : undefined
|
||||
});
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export default class UserMentionedNotification extends Notification {
|
||||
content() {
|
||||
const user = this.props.notification.sender();
|
||||
|
||||
return app.trans('mentions.user_mentioned_notification', {user});
|
||||
return app.trans('flarum-mentions.forum.user_mentioned_notification', {user});
|
||||
}
|
||||
|
||||
excerpt() {
|
||||
|
@ -3,14 +3,14 @@ import app from 'flarum/app';
|
||||
import NotificationGrid from 'flarum/components/NotificationGrid';
|
||||
import { getPlainContent } from 'flarum/utils/string';
|
||||
|
||||
import addPostMentionPreviews from 'mentions/addPostMentionPreviews';
|
||||
import addMentionedByList from 'mentions/addMentionedByList';
|
||||
import addPostReplyAction from 'mentions/addPostReplyAction';
|
||||
import addComposerAutocomplete from 'mentions/addComposerAutocomplete';
|
||||
import PostMentionedNotification from 'mentions/components/PostMentionedNotification';
|
||||
import UserMentionedNotification from 'mentions/components/UserMentionedNotification';
|
||||
import addPostMentionPreviews from 'flarum/mentions/addPostMentionPreviews';
|
||||
import addMentionedByList from 'flarum/mentions/addMentionedByList';
|
||||
import addPostReplyAction from 'flarum/mentions/addPostReplyAction';
|
||||
import addComposerAutocomplete from 'flarum/mentions/addComposerAutocomplete';
|
||||
import PostMentionedNotification from 'flarum/mentions/components/PostMentionedNotification';
|
||||
import UserMentionedNotification from 'flarum/mentions/components/UserMentionedNotification';
|
||||
|
||||
app.initializers.add('mentions', function() {
|
||||
app.initializers.add('flarum-mentions', function() {
|
||||
// For every mention of a post inside a post's content, set up a hover handler
|
||||
// that shows a preview of the mentioned post.
|
||||
addPostMentionPreviews();
|
||||
@ -35,13 +35,13 @@ app.initializers.add('mentions', function() {
|
||||
items.add('postMentioned', {
|
||||
name: 'postMentioned',
|
||||
icon: 'reply',
|
||||
label: app.trans('mentions.notify_post_mentioned')
|
||||
label: app.trans('flarum-mentions.forum.notify_post_mentioned')
|
||||
});
|
||||
|
||||
items.add('userMentioned', {
|
||||
name: 'userMentioned',
|
||||
icon: 'at',
|
||||
label: app.trans('mentions.notify_user_mentioned')
|
||||
label: app.trans('flarum-mentions.forum.notify_user_mentioned')
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
mentions:
|
||||
reply_to_post: "Reply to #{number}"
|
||||
post_mentioned_notification: "{username} replied to your post"
|
||||
others: "{count} others"
|
||||
user_mentioned_notification: "{username} mentioned you"
|
||||
post_mentioned_by: "{users} replied to this."
|
||||
you: You
|
||||
reply_link: Reply
|
||||
notify_post_mentioned: Someone replies to my post
|
||||
notify_user_mentioned: Someone mentions me in a post
|
@ -8,18 +8,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Migrations\Mentions;
|
||||
namespace Flarum\Mentions\Migration;
|
||||
|
||||
use Flarum\Database\AbstractMigration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Flarum\Migrations\Migration;
|
||||
|
||||
class CreateMentionsPostsTable extends Migration
|
||||
class CreateMentionsPostsTable extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$this->schema->create('mentions_posts', function (Blueprint $table) {
|
||||
@ -29,11 +24,6 @@ class CreateMentionsPostsTable extends Migration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$this->schema->drop('mentions_posts');
|
||||
|
@ -8,18 +8,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Migrations\Mentions;
|
||||
namespace Flarum\Mentions\Migration;
|
||||
|
||||
use Flarum\Database\AbstractMigration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Flarum\Migrations\Migration;
|
||||
|
||||
class CreateMentionsUsersTable extends Migration
|
||||
class CreateMentionsUsersTable extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$this->schema->create('mentions_users', function (Blueprint $table) {
|
||||
@ -29,11 +24,6 @@ class CreateMentionsUsersTable extends Migration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$this->schema->drop('mentions_users');
|
||||
|
29
extensions/mentions/scripts/compile.sh
Executable file
29
extensions/mentions/scripts/compile.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script compiles the extension so that it can be used in a Flarum
|
||||
# installation. It should be run from the root directory of the extension.
|
||||
|
||||
base=$PWD
|
||||
|
||||
composer install --prefer-dist --optimize-autoloader --ignore-platform-reqs --no-dev
|
||||
|
||||
cd "${base}/js"
|
||||
|
||||
if [ -f bower.json ]; then
|
||||
bower install
|
||||
fi
|
||||
|
||||
for app in forum admin; do
|
||||
cd "${base}/js"
|
||||
|
||||
if [ -d $app ]; then
|
||||
cd $app
|
||||
|
||||
if [ -f bower.json ]; then
|
||||
bower install
|
||||
fi
|
||||
|
||||
npm install
|
||||
gulp --production
|
||||
fi
|
||||
done
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions;
|
||||
|
||||
use Flarum\Support\Extension as BaseExtension;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
|
||||
class Extension extends BaseExtension
|
||||
{
|
||||
public function listen(Dispatcher $events)
|
||||
{
|
||||
$events->subscribe('Flarum\Mentions\Listeners\AddClientAssets');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\AddModelRelationships');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\AddApiRelationships');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\AddUserMentionsFormatter');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\AddPostMentionsFormatter');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\UpdateUserMentionsMetadata');
|
||||
$events->subscribe('Flarum\Mentions\Listeners\UpdatePostMentionsMetadata');
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../views', 'mentions');
|
||||
}
|
||||
}
|
40
extensions/mentions/src/Listener/AddClientAssets.php
Executable file
40
extensions/mentions/src/Listener/AddClientAssets.php
Executable file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Event\ConfigureClientView;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class AddClientAssets
|
||||
{
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(ConfigureClientView::class, [$this, 'addAssets']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConfigureClientView $event
|
||||
*/
|
||||
public function addAssets(ConfigureClientView $event)
|
||||
{
|
||||
if ($event->isForum()) {
|
||||
$event->addAssets([
|
||||
__DIR__.'/../../js/forum/dist/extension.js',
|
||||
__DIR__.'/../../less/forum/extension.less'
|
||||
]);
|
||||
$event->addBootstrapper('flarum/mentions/main');
|
||||
$event->addTranslations('flarum-mentions.forum');
|
||||
}
|
||||
}
|
||||
}
|
104
extensions/mentions/src/Listener/AddPostMentionedByRelationship.php
Executable file
104
extensions/mentions/src/Listener/AddPostMentionedByRelationship.php
Executable file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Api\Controller\CreatePostController;
|
||||
use Flarum\Api\Controller\ListPostsController;
|
||||
use Flarum\Api\Controller\ShowDiscussionController;
|
||||
use Flarum\Api\Controller\ShowPostController;
|
||||
use Flarum\Api\Serializer\PostBasicSerializer;
|
||||
use Flarum\Core\Post;
|
||||
use Flarum\Core\User;
|
||||
use Flarum\Event\ConfigureApiController;
|
||||
use Flarum\Event\GetApiRelationship;
|
||||
use Flarum\Event\GetModelRelationship;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class AddPostMentionedByRelationship
|
||||
{
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(GetModelRelationship::class, [$this, 'getModelRelationship']);
|
||||
$events->listen(GetApiRelationship::class, [$this, 'getApiRelationship']);
|
||||
$events->listen(ConfigureApiController::class, [$this, 'includeRelationships']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GetModelRelationship $event
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|null
|
||||
*/
|
||||
public function getModelRelationship(GetModelRelationship $event)
|
||||
{
|
||||
if ($event->isRelationship(Post::class, 'mentionedBy')) {
|
||||
return $event->model->belongsToMany(Post::class, 'mentions_posts', 'mentions_id', 'post_id', 'mentionedBy');
|
||||
}
|
||||
|
||||
if ($event->isRelationship(Post::class, 'mentionsPosts')) {
|
||||
return $event->model->belongsToMany(Post::class, 'mentions_posts', 'post_id', 'mentions_id', 'mentionsPosts');
|
||||
}
|
||||
|
||||
if ($event->isRelationship(Post::class, 'mentionsUsers')) {
|
||||
return $event->model->belongsToMany(User::class, 'mentions_users', 'post_id', 'mentions_id', 'mentionsUsers');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param GetApiRelationship $event
|
||||
* @return \Flarum\Api\Relationship\HasManyBuilder|null
|
||||
*/
|
||||
public function getApiRelationship(GetApiRelationship $event)
|
||||
{
|
||||
if ($event->isRelationship(PostBasicSerializer::class, 'mentionedBy')) {
|
||||
return $event->serializer->hasMany(PostBasicSerializer::class, 'mentionedBy');
|
||||
}
|
||||
|
||||
if ($event->isRelationship(PostBasicSerializer::class, 'mentionsPosts')) {
|
||||
return $event->serializer->hasMany(PostBasicSerializer::class, 'mentionsPosts');
|
||||
}
|
||||
|
||||
if ($event->isRelationship(PostBasicSerializer::class, 'mentionsUsers')) {
|
||||
return $event->serializer->hasMany(PostBasicSerializer::class, 'mentionsUsers');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConfigureApiController $event
|
||||
*/
|
||||
public function includeRelationships(ConfigureApiController $event)
|
||||
{
|
||||
if ($event->isController(ShowDiscussionController::class)) {
|
||||
$event->addInclude([
|
||||
'posts.mentionedBy',
|
||||
'posts.mentionedBy.user',
|
||||
'posts.mentionedBy.discussion'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($event->isController(ShowPostController::class)
|
||||
|| $event->isController(ListPostsController::class)) {
|
||||
$event->addInclude([
|
||||
'mentionedBy',
|
||||
'mentionedBy.user',
|
||||
'mentionedBy.discussion'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($event->isController(CreatePostController::class)) {
|
||||
$event->addInclude([
|
||||
'mentionsPosts',
|
||||
'mentionsPosts.mentionedBy'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,19 +8,26 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Events\FormatterConfigurator;
|
||||
use Flarum\Core\Posts\CommentPost;
|
||||
use Flarum\Core\Post\CommentPost;
|
||||
use Flarum\Event\ConfigureFormatter;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class AddPostMentionsFormatter
|
||||
class FormatPostMentions
|
||||
{
|
||||
public function subscribe($events)
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(FormatterConfigurator::class, [$this, 'configure']);
|
||||
$events->listen(ConfigureFormatter::class, [$this, 'configure']);
|
||||
}
|
||||
|
||||
public function configure(FormatterConfigurator $event)
|
||||
/**
|
||||
* @param ConfigureFormatter $event
|
||||
*/
|
||||
public function configure(ConfigureFormatter $event)
|
||||
{
|
||||
$configurator = $event->configurator;
|
||||
|
||||
@ -44,6 +51,10 @@ class AddPostMentionsFormatter
|
||||
$configurator->Preg->match('/\B@(?<username>[a-z0-9_-]+)#(?<id>\d+)/i', $tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tag
|
||||
* @return bool
|
||||
*/
|
||||
public static function addId($tag)
|
||||
{
|
||||
$post = CommentPost::find($tag->getAttribute('id'));
|
@ -8,30 +8,43 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Events\FormatterConfigurator;
|
||||
use Flarum\Events\FormatterRenderer;
|
||||
use Flarum\Events\FormatterParser;
|
||||
use Flarum\Core\Users\UserRepository;
|
||||
use Flarum\Core\Repository\UserRepository;
|
||||
use Flarum\Event\ConfigureFormatter;
|
||||
use Flarum\Event\ConfigureFormatterParser;
|
||||
use Flarum\Event\ConfigureFormatterRenderer;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class AddUserMentionsFormatter
|
||||
class FormatUserMentions
|
||||
{
|
||||
/**
|
||||
* @var UserRepository
|
||||
*/
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* @param UserRepository $users
|
||||
*/
|
||||
public function __construct(UserRepository $users)
|
||||
{
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function subscribe($events)
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(FormatterConfigurator::class, [$this, 'configure']);
|
||||
$events->listen(FormatterParser::class, [$this, 'parse']);
|
||||
$events->listen(FormatterRenderer::class, [$this, 'render']);
|
||||
$events->listen(ConfigureFormatter::class, [$this, 'configure']);
|
||||
$events->listen(ConfigureFormatterParser::class, [$this, 'parse']);
|
||||
$events->listen(ConfigureFormatterRenderer::class, [$this, 'render']);
|
||||
}
|
||||
|
||||
public function configure(FormatterConfigurator $event)
|
||||
/**
|
||||
* @param ConfigureFormatter $event
|
||||
*/
|
||||
public function configure(ConfigureFormatter $event)
|
||||
{
|
||||
$configurator = $event->configurator;
|
||||
|
||||
@ -50,17 +63,28 @@ class AddUserMentionsFormatter
|
||||
$configurator->Preg->match('/\B@(?<username>[a-z0-9_-]+)(?!#)/i', $tagName);
|
||||
}
|
||||
|
||||
public function parse(FormatterParser $event)
|
||||
/**
|
||||
* @param ConfigureFormatterParser $event
|
||||
*/
|
||||
public function parse(ConfigureFormatterParser $event)
|
||||
{
|
||||
$event->parser->registeredVars['userRepository'] = $this->users;
|
||||
}
|
||||
|
||||
public function render(FormatterRenderer $event)
|
||||
/**
|
||||
* @param ConfigureFormatterRenderer $event
|
||||
*/
|
||||
public function render(ConfigureFormatterRenderer $event)
|
||||
{
|
||||
// TODO: use URL generator
|
||||
$event->renderer->setParameter('PROFILE_URL', '/u/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $tag
|
||||
* @param UserRepository $users
|
||||
* @return bool
|
||||
*/
|
||||
public static function addId($tag, UserRepository $users)
|
||||
{
|
||||
if ($id = $users->getIdForUsername($tag->getAttribute('username'))) {
|
@ -8,33 +8,42 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Mentions\Notifications\PostMentionedBlueprint;
|
||||
use Flarum\Core\Notifications\NotificationSyncer;
|
||||
use Flarum\Events\RegisterNotificationTypes;
|
||||
use Flarum\Events\PostWasPosted;
|
||||
use Flarum\Events\PostWasRevised;
|
||||
use Flarum\Events\PostWasHidden;
|
||||
use Flarum\Events\PostWasRestored;
|
||||
use Flarum\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Api\Serializer\PostBasicSerializer;
|
||||
use Flarum\Core\Notification\NotificationSyncer;
|
||||
use Flarum\Core\Post;
|
||||
use Flarum\Event\ConfigureNotificationTypes;
|
||||
use Flarum\Event\PostWasDeleted;
|
||||
use Flarum\Event\PostWasHidden;
|
||||
use Flarum\Event\PostWasPosted;
|
||||
use Flarum\Event\PostWasRestored;
|
||||
use Flarum\Event\PostWasRevised;
|
||||
use Flarum\Mentions\Notification\PostMentionedBlueprint;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use s9e\TextFormatter\Utils;
|
||||
|
||||
class UpdatePostMentionsMetadata
|
||||
{
|
||||
/**
|
||||
* @var NotificationSyncer
|
||||
*/
|
||||
protected $notifications;
|
||||
|
||||
/**
|
||||
* @param NotificationSyncer $notifications
|
||||
*/
|
||||
public function __construct(NotificationSyncer $notifications)
|
||||
{
|
||||
$this->notifications = $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(RegisterNotificationTypes::class, [$this, 'registerNotificationType']);
|
||||
|
||||
$events->listen(ConfigureNotificationTypes::class, [$this, 'addNotificationType']);
|
||||
$events->listen(PostWasPosted::class, [$this, 'whenPostWasPosted']);
|
||||
$events->listen(PostWasRevised::class, [$this, 'whenPostWasRevised']);
|
||||
$events->listen(PostWasHidden::class, [$this, 'whenPostWasHidden']);
|
||||
@ -42,40 +51,57 @@ class UpdatePostMentionsMetadata
|
||||
$events->listen(PostWasDeleted::class, [$this, 'whenPostWasDeleted']);
|
||||
}
|
||||
|
||||
public function registerNotificationType(RegisterNotificationTypes $event)
|
||||
/**
|
||||
* @param ConfigureNotificationTypes $event
|
||||
*/
|
||||
public function addNotificationType(ConfigureNotificationTypes $event)
|
||||
{
|
||||
$event->register(
|
||||
PostMentionedBlueprint::class,
|
||||
'Flarum\Api\Serializers\PostBasicSerializer',
|
||||
['alert']
|
||||
);
|
||||
$event->add(PostMentionedBlueprint::class, PostBasicSerializer::class, ['alert']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasPosted $event
|
||||
*/
|
||||
public function whenPostWasPosted(PostWasPosted $event)
|
||||
{
|
||||
$this->replyBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasRevised $event
|
||||
*/
|
||||
public function whenPostWasRevised(PostWasRevised $event)
|
||||
{
|
||||
$this->replyBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasHidden $event
|
||||
*/
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->replyBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasRestored $event
|
||||
*/
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$this->replyBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasDeleted $event
|
||||
*/
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->replyBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $reply
|
||||
*/
|
||||
protected function replyBecameVisible(Post $reply)
|
||||
{
|
||||
$mentioned = Utils::getAttributeValues($reply->parsedContent, 'POSTMENTION', 'id');
|
||||
@ -83,11 +109,18 @@ class UpdatePostMentionsMetadata
|
||||
$this->sync($reply, $mentioned);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $reply
|
||||
*/
|
||||
protected function replyBecameInvisible(Post $reply)
|
||||
{
|
||||
$this->sync($reply, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $reply
|
||||
* @param array $mentioned
|
||||
*/
|
||||
protected function sync(Post $reply, array $mentioned)
|
||||
{
|
||||
$reply->mentionsPosts()->sync($mentioned);
|
@ -8,34 +8,42 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
namespace Flarum\Mentions\Listener;
|
||||
|
||||
use Flarum\Mentions\Notifications\UserMentionedBlueprint;
|
||||
use Flarum\Core\Notifications\NotificationSyncer;
|
||||
use Flarum\Events\RegisterNotificationTypes;
|
||||
use Flarum\Events\PostWasPosted;
|
||||
use Flarum\Events\PostWasRevised;
|
||||
use Flarum\Events\PostWasHidden;
|
||||
use Flarum\Events\PostWasRestored;
|
||||
use Flarum\Events\PostWasDeleted;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Notification\NotificationSyncer;
|
||||
use Flarum\Core\Post;
|
||||
use Flarum\Core\User;
|
||||
use Flarum\Event\ConfigureNotificationTypes;
|
||||
use Flarum\Event\PostWasDeleted;
|
||||
use Flarum\Event\PostWasHidden;
|
||||
use Flarum\Event\PostWasPosted;
|
||||
use Flarum\Event\PostWasRestored;
|
||||
use Flarum\Event\PostWasRevised;
|
||||
use Flarum\Mentions\Notification\UserMentionedBlueprint;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use s9e\TextFormatter\Utils;
|
||||
|
||||
class UpdateUserMentionsMetadata
|
||||
{
|
||||
/**
|
||||
* @var NotificationSyncer
|
||||
*/
|
||||
protected $notifications;
|
||||
|
||||
/**
|
||||
* @param NotificationSyncer $notifications
|
||||
*/
|
||||
public function __construct(NotificationSyncer $notifications)
|
||||
{
|
||||
$this->notifications = $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Dispatcher $events
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(RegisterNotificationTypes::class, [$this, 'registerNotificationType']);
|
||||
|
||||
$events->listen(ConfigureNotificationTypes::class, [$this, 'addNotificationType']);
|
||||
$events->listen(PostWasPosted::class, [$this, 'whenPostWasPosted']);
|
||||
$events->listen(PostWasRevised::class, [$this, 'whenPostWasRevised']);
|
||||
$events->listen(PostWasHidden::class, [$this, 'whenPostWasHidden']);
|
||||
@ -43,40 +51,61 @@ class UpdateUserMentionsMetadata
|
||||
$events->listen(PostWasDeleted::class, [$this, 'whenPostWasDeleted']);
|
||||
}
|
||||
|
||||
public function registerNotificationType(RegisterNotificationTypes $event)
|
||||
/**
|
||||
* @param ConfigureNotificationTypes $event
|
||||
*/
|
||||
public function addNotificationType(ConfigureNotificationTypes $event)
|
||||
{
|
||||
$event->register(
|
||||
$event->add(
|
||||
UserMentionedBlueprint::class,
|
||||
'Flarum\Api\Serializers\PostBasicSerializer',
|
||||
['alert']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasPosted $event
|
||||
*/
|
||||
public function whenPostWasPosted(PostWasPosted $event)
|
||||
{
|
||||
$this->postBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasRevised $event
|
||||
*/
|
||||
public function whenPostWasRevised(PostWasRevised $event)
|
||||
{
|
||||
$this->postBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasHidden $event
|
||||
*/
|
||||
public function whenPostWasHidden(PostWasHidden $event)
|
||||
{
|
||||
$this->postBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasRestored $event
|
||||
*/
|
||||
public function whenPostWasRestored(PostWasRestored $event)
|
||||
{
|
||||
$this->postBecameVisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostWasDeleted $event
|
||||
*/
|
||||
public function whenPostWasDeleted(PostWasDeleted $event)
|
||||
{
|
||||
$this->postBecameInvisible($event->post);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
*/
|
||||
protected function postBecameVisible(Post $post)
|
||||
{
|
||||
$mentioned = Utils::getAttributeValues($post->parsedContent, 'USERMENTION', 'id');
|
||||
@ -84,11 +113,18 @@ class UpdateUserMentionsMetadata
|
||||
$this->sync($post, $mentioned);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
*/
|
||||
protected function postBecameInvisible(Post $post)
|
||||
{
|
||||
$this->sync($post, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
* @param array $mentioned
|
||||
*/
|
||||
protected function sync(Post $post, array $mentioned)
|
||||
{
|
||||
$post->mentionsUsers()->sync($mentioned);
|
@ -1,65 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
|
||||
use Flarum\Events\ApiRelationship;
|
||||
use Flarum\Events\BuildApiAction;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Flarum\Api\Serializers\PostBasicSerializer;
|
||||
use Flarum\Api\Actions\Discussions;
|
||||
use Flarum\Api\Actions\Posts;
|
||||
|
||||
class AddApiRelationships
|
||||
{
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(ApiRelationship::class, [$this, 'addRelationships']);
|
||||
$events->listen(BuildApiAction::class, [$this, 'includeRelationships']);
|
||||
}
|
||||
|
||||
public function addRelationships(ApiRelationship $event)
|
||||
{
|
||||
if ($event->serializer instanceof PostBasicSerializer) {
|
||||
if ($event->relationship === 'mentionedBy') {
|
||||
return $event->serializer->hasMany('Flarum\Api\Serializers\PostBasicSerializer', 'mentionedBy');
|
||||
}
|
||||
|
||||
if ($event->relationship === 'mentionsPosts') {
|
||||
return $event->serializer->hasMany('Flarum\Api\Serializers\PostBasicSerializer', 'mentionsPosts');
|
||||
}
|
||||
|
||||
if ($event->relationship === 'mentionsUsers') {
|
||||
return $event->serializer->hasMany('Flarum\Api\Serializers\PostBasicSerializer', 'mentionsUsers');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function includeRelationships(BuildApiAction $event)
|
||||
{
|
||||
if ($event->action instanceof Discussions\ShowAction) {
|
||||
$event->addInclude('posts.mentionedBy');
|
||||
$event->addInclude('posts.mentionedBy.user');
|
||||
$event->addInclude('posts.mentionedBy.discussion');
|
||||
}
|
||||
|
||||
if ($event->action instanceof Posts\ShowAction ||
|
||||
$event->action instanceof Posts\IndexAction) {
|
||||
$event->addInclude('mentionedBy');
|
||||
$event->addInclude('mentionedBy.user');
|
||||
$event->addInclude('mentionedBy.discussion');
|
||||
}
|
||||
|
||||
if ($event->action instanceof Posts\CreateAction) {
|
||||
$event->addInclude('mentionsPosts');
|
||||
$event->addInclude('mentionsPosts.mentionedBy');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
|
||||
use Flarum\Events\RegisterLocales;
|
||||
use Flarum\Events\BuildClientView;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
|
||||
class AddClientAssets
|
||||
{
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$events->listen(RegisterLocales::class, [$this, 'addLocale']);
|
||||
$events->listen(BuildClientView::class, [$this, 'addAssets']);
|
||||
}
|
||||
|
||||
public function addLocale(RegisterLocales $event)
|
||||
{
|
||||
$event->addTranslations('en', __DIR__.'/../../locale/en.yml');
|
||||
}
|
||||
|
||||
public function addAssets(BuildClientView $event)
|
||||
{
|
||||
$event->forumAssets([
|
||||
__DIR__.'/../../js/forum/dist/extension.js',
|
||||
__DIR__.'/../../less/forum/extension.less'
|
||||
]);
|
||||
|
||||
$event->forumBootstrapper('mentions/main');
|
||||
|
||||
$event->forumTranslations([
|
||||
'mentions.reply_to_post',
|
||||
'mentions.post_mentioned_notification',
|
||||
'mentions.others',
|
||||
'mentions.user_mentioned_notification',
|
||||
'mentions.post_mentioned_by',
|
||||
'mentions.you',
|
||||
'mentions.reply_link',
|
||||
'mentions.notify_post_mentioned',
|
||||
'mentions.notify_user_mentioned'
|
||||
]);
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* (c) Toby Zerner <toby.zerner@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Listeners;
|
||||
|
||||
use Flarum\Events\ModelRelationship;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Users\User;
|
||||
|
||||
class AddModelRelationships
|
||||
{
|
||||
public function subscribe($events)
|
||||
{
|
||||
$events->listen(ModelRelationship::class, [$this, 'addRelationships']);
|
||||
}
|
||||
|
||||
public function addRelationships(ModelRelationship $event)
|
||||
{
|
||||
if ($event->model instanceof Post) {
|
||||
if ($event->relationship === 'mentionedBy') {
|
||||
return $event->model->belongsToMany(Post::class, 'mentions_posts', 'mentions_id', 'post_id', 'mentionedBy');
|
||||
}
|
||||
|
||||
if ($event->relationship === 'mentionsPosts') {
|
||||
return $event->model->belongsToMany(Post::class, 'mentions_posts', 'post_id', 'mentions_id', 'mentionsPosts');
|
||||
}
|
||||
|
||||
if ($event->relationship === 'mentionsUsers') {
|
||||
return $event->model->belongsToMany(User::class, 'mentions_users', 'post_id', 'mentions_id', 'mentionsUsers');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,54 +8,85 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Notifications;
|
||||
namespace Flarum\Mentions\Notification;
|
||||
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Notifications\Blueprint;
|
||||
use Flarum\Core\Notifications\MailableBlueprint;
|
||||
use Flarum\Core\Post;
|
||||
use Flarum\Core\Notification\BlueprintInterface;
|
||||
use Flarum\Core\Notification\MailableInterface;
|
||||
|
||||
class PostMentionedBlueprint implements Blueprint, MailableBlueprint
|
||||
class PostMentionedBlueprint implements BlueprintInterface, MailableInterface
|
||||
{
|
||||
/**
|
||||
* @var Post
|
||||
*/
|
||||
public $post;
|
||||
|
||||
/**
|
||||
* @var Post
|
||||
*/
|
||||
public $reply;
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
* @param Post $reply
|
||||
*/
|
||||
public function __construct(Post $post, Post $reply)
|
||||
{
|
||||
$this->post = $post;
|
||||
$this->reply = $reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSender()
|
||||
{
|
||||
return $this->reply->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return ['replyNumber' => (int) $this->reply->number];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEmailView()
|
||||
{
|
||||
return ['text' => 'mentions::emails.postMentioned'];
|
||||
return ['text' => 'flarum-mentions::emails.postMentioned'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEmailSubject()
|
||||
{
|
||||
return "{$this->reply->user->username} replied to your post in {$this->post->discussion->title}";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getType()
|
||||
{
|
||||
return 'postMentioned';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return Post::class;
|
@ -8,52 +8,78 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Mentions\Notifications;
|
||||
namespace Flarum\Mentions\Notification;
|
||||
|
||||
use Flarum\Core\Users\User;
|
||||
use Flarum\Core\Posts\Post;
|
||||
use Flarum\Core\Notifications\Blueprint;
|
||||
use Flarum\Core\Notifications\MailableBlueprint;
|
||||
use Flarum\Core\Post;
|
||||
use Flarum\Core\Notification\BlueprintInterface;
|
||||
use Flarum\Core\Notification\MailableInterface;
|
||||
|
||||
class UserMentionedBlueprint implements Blueprint, MailableBlueprint
|
||||
class UserMentionedBlueprint implements BlueprintInterface, MailableInterface
|
||||
{
|
||||
/**
|
||||
* @var Post
|
||||
*/
|
||||
public $post;
|
||||
|
||||
/**
|
||||
* @param Post $post
|
||||
*/
|
||||
public function __construct(Post $post)
|
||||
{
|
||||
$this->post = $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubject()
|
||||
{
|
||||
return $this->post;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSender()
|
||||
{
|
||||
return $this->post->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEmailView()
|
||||
{
|
||||
return ['text' => 'mentions::emails.userMentioned'];
|
||||
return ['text' => 'flarum-mentions::emails.userMentioned'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEmailSubject()
|
||||
{
|
||||
return "{$this->post->user->username} mentioned you in {$this->post->discussion->title}";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getType()
|
||||
{
|
||||
return 'userMentioned';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubjectModel()
|
||||
{
|
||||
return Post::class;
|
Loading…
x
Reference in New Issue
Block a user