mirror of
https://github.com/Skylar-Tech/node-red-contrib-matrix-chat.git
synced 2025-04-20 13:03:12 -06:00
369 lines
19 KiB
HTML
369 lines
19 KiB
HTML
<script type="text/html" data-template-name="matrix-room-state-events">
|
|
<div class="form-row">
|
|
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
<input type="text" id="node-input-name" placeholder="Name">
|
|
</div>
|
|
<div class="form-row">
|
|
<label for="node-input-server"><i class="fa fa-user"></i> Matrix Server Config</label>
|
|
<input type="text" id="node-input-server">
|
|
</div>
|
|
<div class="form-row">
|
|
<label for="node-input-room"><i class="fa fa-comments"></i> Room</label>
|
|
<input type="text" id="node-input-room">
|
|
</div>
|
|
<div class="form-row" style="margin-bottom:0;">
|
|
<label><i class="fa fa-list"></i> <span data-i18n="change.label.rules"></span></label>
|
|
</div>
|
|
<div class="form-row node-input-rule-container-row">
|
|
<ol id="node-input-rule-container"></ol>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/html" data-help-name="matrix-room-state-events">
|
|
<h3>Details</h3>
|
|
<p>
|
|
Set and Get a list of room state events for a given room. Allows you to set/get room name, topic, avatar, etc.
|
|
</p>
|
|
|
|
<h3>Inputs</h3>
|
|
<dl class="message-properties">
|
|
<dt class="optional">msg.topic
|
|
<span class="property-type">string | null</span>
|
|
</dt>
|
|
<dd> The room to set/get settings for.</dd>
|
|
|
|
<dt class="optional">dynamic
|
|
<span class="property-type">string|object</span>
|
|
</dt>
|
|
<dd> You configure what room state events in the node configuration. <code style="white-space: normal;">m.room.name</code>, <code style="white-space: normal;">m.room.avatar</code>, and <code style="white-space: normal;">m.room.guest_access</code> allow you to pass a string to set their value but all other room state events will require the full content object (find this by referencing the <a href="https://spec.matrix.org/latest/client-server-api" target="_blank">Matrix Client-Server docs</a>)</dd>
|
|
|
|
<dt class="optional">msg.state_key
|
|
<span class="property-type">string</span>
|
|
</dt>
|
|
<dd> Required for some events such as <code style="white-space: normal;">m.space.parent</code> and <code style="white-space: normal;">m.room.child</code> to set the referenced child/parent room</dd>
|
|
</dl>
|
|
|
|
<h3>Outputs</h3>
|
|
<ol class="node-ports">
|
|
<li>Success
|
|
<dl class="message-properties">
|
|
<dt>msg <span class="property-type">object</span></dt>
|
|
<dd>Original message object with modifications based on config.</dd>
|
|
</dl>
|
|
<dl class="message-properties">
|
|
<dt>msg.setter_errors <span class="property-type">undefined|object</span></dt>
|
|
<dd>Returned if setting a room state event failed. The key of the object is the room state event type and the value is the error that occurred.</dd>
|
|
</dl>
|
|
<dl class="message-properties">
|
|
<dt>msg.getter_errors <span class="property-type">undefined|object</span></dt>
|
|
<dd>Returned if getting a room state event failed. The key of the object is the room state event type and the value is the error that occurred. Note that you will get an error if you try getting a room state event that doesn't exist (such as fetching avatar on a room that doesn't have one).</dd>
|
|
</dl>
|
|
<dt class="optional">dynamic
|
|
<span class="property-type">string|object</span>
|
|
</dt>
|
|
<dd> You configure what room state events to output in the node configuration. <code style="white-space: normal;">m.room.name</code>, <code style="white-space: normal;">m.room.avatar</code>, and <code style="white-space: normal;">m.room.guest_access</code> will come back as strings otherwise you will get the full content object of the event (find this by referencing the <a href="https://spec.matrix.org/latest/client-server-api" target="_blank">Matrix Client-Server docs</a>). Additionally there is a setting when configuring a getter called "Fetch from local storage" that if enabled will search the local storage for the room and try to fetch the state event that way and fallback to hitting the server if that isn't possible.</dd>
|
|
</li>
|
|
</ol>
|
|
</script>
|
|
|
|
<script type="text/javascript">
|
|
(function(){
|
|
var roomEventTypeOptions = [
|
|
{ value: "m.room.name", label: "m.room.name" },
|
|
{ value: "m.room.topic", label: "m.room.topic" },
|
|
{ value: "m.room.avatar", label: "m.room.avatar" },
|
|
{ value: "m.room.power_levels", label: "m.room.power_levels" },
|
|
{ value: "m.room.guest_access", label: "m.room.guest_access" },
|
|
{ value: "m.room.join_rules", label: "m.room.join_rules" },
|
|
{ value: "m.room.canonical_alias", label: "m.room.canonical_alias" },
|
|
{ value: "m.room.history_visibility", label: "m.room.history_visibility" },
|
|
{ value: "m.room.server_acl", label: "m.room.server_acl" },
|
|
{ value: "m.room.pinned_events", label: "m.room.pinned_events"},
|
|
{ value: "m.space.child", label: "m.space.child" },
|
|
{ value: "m.space.parent", label: "m.space.parent" },
|
|
];
|
|
var defaultRules = [{
|
|
t: "set",
|
|
p: roomEventTypeOptions[0].value,
|
|
to: "payload",
|
|
tot: "msg"
|
|
}];
|
|
|
|
function isInvalidProperty(v,vt) {
|
|
if (/msg|flow|global/.test(vt)) {
|
|
if (!RED.utils.validatePropertyExpression(v)) {
|
|
return "Invalid property: " + v;
|
|
}
|
|
} else if (vt === "jsonata") {
|
|
try{ jsonata(v); } catch(e) {
|
|
return "Invalid expression: " + e.message;
|
|
}
|
|
} else if (vt === "json") {
|
|
try{ JSON.parse(v); } catch(e) {
|
|
return "Invalid JSON data: " + e.message;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RED.nodes.registerType('matrix-room-state-events',{
|
|
category: 'matrix',
|
|
color: '#00b7ca',
|
|
icon: "matrix.png",
|
|
outputLabels: ["success", "error"],
|
|
inputs:1,
|
|
outputs:2,
|
|
defaults: {
|
|
name: { value: null },
|
|
server: { type: "matrix-server-config" },
|
|
roomType: { value: "msg" },
|
|
roomValue: { value: "topic" },
|
|
rules: {
|
|
value: defaultRules,
|
|
validate: function(rules, opt) {
|
|
let msg;
|
|
const errors = []
|
|
if (!rules || rules.length === 0) { return true }
|
|
for (let i=0;i<rules.length;i++) {
|
|
const opt = { label: "Rule"+' '+(i+1) }
|
|
const r = rules[i];
|
|
if (r.t === 'set' || r.t === 'get') {
|
|
if ((msg = isInvalidProperty(r.p,r.pt)) !== false) {
|
|
return msg;
|
|
}
|
|
if ((msg = isInvalidProperty(r.to,r.tot)) !== false) {
|
|
return msg;
|
|
}
|
|
}
|
|
}
|
|
return errors.length ? errors : true;
|
|
}
|
|
},
|
|
},
|
|
oneditprepare: function() {
|
|
$("#node-input-room").typedInput({
|
|
type: this.roomType,
|
|
types:['msg','flow','global','str'],
|
|
})
|
|
.typedInput('value', this.roomValue)
|
|
.typedInput('type', this.roomType);
|
|
|
|
var set = "Set";
|
|
var to = "to the value";
|
|
var toValueLabel = "to the property";
|
|
var search = this._("change.action.search");
|
|
var replace = this._("change.action.replace");
|
|
var regex = this._("change.label.regex");
|
|
|
|
function createPropertyValue(row2_1, row2_2, type, defaultType) {
|
|
var propValInput = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
|
|
.appendTo(row2_1)
|
|
.typedInput({
|
|
default: defaultType || (type === 'set' ? 'str' : 'msg'),
|
|
types: (type === 'set' ? ['msg','flow','global','str','json','jsonata'] : ['msg', 'flow', 'global'])
|
|
});
|
|
|
|
var lsLabel = $('<label style="padding-left: 130px;"></label>').appendTo(row2_2);
|
|
var localStorageEl = $('<input type="checkbox" class="node-input-rule-property-localStorage" style="width: auto; margin: 0 6px 0 0">').appendTo(lsLabel);
|
|
$('<span>').text("Fetch from local storage").appendTo(lsLabel);
|
|
|
|
propValInput.on("change", function(evt,type,val) {
|
|
row2_2.toggle(type === "msg" || type === "flow" || type === "global" || type === "env");
|
|
})
|
|
return [propValInput, localStorageEl];
|
|
}
|
|
|
|
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
|
|
addItem: function(container,i,opt) {
|
|
var rule = opt;
|
|
if (!rule.hasOwnProperty('t')) {
|
|
rule = {t:"set",p:roomEventTypeOptions[0].value,to:"payload",tot:"msg"};
|
|
}
|
|
if (rule.t === "set" && !rule.tot) {
|
|
if (rule.to.indexOf("msg.") === 0 && !rule.tot) {
|
|
rule.to = rule.to.substring(4);
|
|
rule.tot = "msg";
|
|
} else {
|
|
rule.tot = "str";
|
|
}
|
|
}
|
|
container.css({
|
|
overflow: 'hidden',
|
|
whiteSpace: 'nowrap'
|
|
});
|
|
let fragment = document.createDocumentFragment();
|
|
var row1 = $('<div/>',{style:"display:flex; align-items: center"}).appendTo(fragment);
|
|
var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
|
|
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
|
|
var row4 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(fragment);
|
|
|
|
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
|
|
var selectOptions = [
|
|
{v:"set",l:"Set"},
|
|
{v:"get",l:"Get"}
|
|
];
|
|
for (var x=0; x<selectOptions.length; x++) {
|
|
selectField.append($("<option></option>").val(selectOptions[x].v).text(selectOptions[x].l));
|
|
}
|
|
|
|
var propertyName = $('<input/>',{class:"node-input-rule-property-name",type:"text"})
|
|
.appendTo(row1)
|
|
.autoComplete({
|
|
minLength:0,
|
|
search: function(val) {
|
|
if(!val) {
|
|
return roomEventTypeOptions.sort(function(A,B){return A.i-B.i});
|
|
}
|
|
var matches = [];
|
|
roomEventTypeOptions.map((x) => x.value).forEach(v => {
|
|
var i = v.toLowerCase().indexOf(val.toLowerCase());
|
|
if (i > -1) {
|
|
matches.push({
|
|
value: v,
|
|
label: v,
|
|
i: i
|
|
})
|
|
}
|
|
});
|
|
matches.sort(function(A,B){return A.i-B.i})
|
|
return matches
|
|
}
|
|
})
|
|
.on('focus', function(evt){
|
|
// following is a fix so autocomplete will show list on focus
|
|
if(!evt.isTrigger) {
|
|
evt.stopPropagation();
|
|
$(this).trigger("keyup.red-ui-autoComplete");
|
|
}
|
|
}).on("keyup", function(evt) {
|
|
// following allows autocomplete to display even when backspace/delete is used
|
|
if (evt.keyCode === 8 || evt.keyCode === 46) {
|
|
evt.stopPropagation();
|
|
$(this).trigger("keyup.red-ui-autoComplete");
|
|
}
|
|
});
|
|
|
|
var row2_1 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row2);
|
|
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
|
.text(toValueLabel)
|
|
.appendTo(row2_1);
|
|
|
|
var row2_2 = $('<div/>', {style:"margin-top: 4px;"}).appendTo(row2);
|
|
|
|
var row3_1 = $('<div/>', {style:"display:flex;align-items: baseline"}).appendTo(row3);
|
|
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
|
.text(search)
|
|
.appendTo(row3_1);
|
|
|
|
var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;align-items: baseline"}).appendTo(row3);
|
|
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
|
.text(replace)
|
|
.appendTo(row3_2);
|
|
|
|
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
|
|
.text(to)
|
|
.appendTo(row4);
|
|
|
|
let propertyValue = null;
|
|
let localStorageEl = null;
|
|
let fromValue = null;
|
|
let toValue = null;
|
|
|
|
selectField.on("change", function() {
|
|
var type = $(this).val();
|
|
if (propertyValue) {
|
|
propertyValue.typedInput('hide');
|
|
}
|
|
if (fromValue) {
|
|
fromValue.typedInput('hide');
|
|
}
|
|
if (toValue) {
|
|
toValue.typedInput('hide');
|
|
}
|
|
|
|
if (!propertyValue) {
|
|
var parts = createPropertyValue(row2_1, row2_2, type);
|
|
propertyValue = parts[0];
|
|
localStorageEl = parts[1];
|
|
} else {
|
|
propertyValue.typedInput('types', (type === 'set' ? ['msg','flow','global','str','json','jsonata'] : ['msg', 'flow', 'global']));
|
|
}
|
|
|
|
propertyValue.typedInput('show');
|
|
row2.show();
|
|
if(type === 'get') {
|
|
localStorageEl.parent().show();
|
|
} else {
|
|
localStorageEl.parent().hide();
|
|
}
|
|
row3.hide();
|
|
row4.hide();
|
|
});
|
|
|
|
selectField.val(rule.t);
|
|
propertyName.val(rule.p);
|
|
if (rule.t === "set" || rule.t === "get") {
|
|
var parts = createPropertyValue(row2_1, row2_2, rule.t, rule.tot);
|
|
propertyValue = parts[0];
|
|
localStorageEl = parts[1];
|
|
propertyValue.typedInput('value',rule.to);
|
|
localStorageEl.prop("checked", !!rule.ls);
|
|
if(rule.t === 'get') {
|
|
localStorageEl.parent().show();
|
|
} else {
|
|
localStorageEl.parent().hide();
|
|
}
|
|
}
|
|
selectField.change();
|
|
container[0].appendChild(fragment);
|
|
},
|
|
removable: true,
|
|
sortable: true
|
|
});
|
|
|
|
for (var i=0; i<this.rules.length; i++) {
|
|
var rule = this.rules[i];
|
|
$("#node-input-rule-container").editableList('addItem',rule);
|
|
}
|
|
},
|
|
oneditsave: function() {
|
|
this.roomType = $("#node-input-room").typedInput('type');
|
|
this.roomValue = $("#node-input-room").typedInput('value');
|
|
|
|
var rules = $("#node-input-rule-container").editableList('items');
|
|
var node = this;
|
|
node.rules= [];
|
|
rules.each(function(i) {
|
|
var rule = $(this);
|
|
var type = rule.find(".node-input-rule-type").val();
|
|
var r = {
|
|
t:type,
|
|
p:rule.find(".node-input-rule-property-name").val(),
|
|
to:rule.find(".node-input-rule-property-value").typedInput('value'),
|
|
tot:rule.find(".node-input-rule-property-value").typedInput('type')
|
|
};
|
|
|
|
if (r.t === "get" && rule.find(".node-input-rule-property-localStorage").prop("checked")) {
|
|
r.ls = true;
|
|
}
|
|
node.rules.push(r);
|
|
});
|
|
},
|
|
oneditresize: function(size) {
|
|
var rows = $("#dialog-form>div:not(.node-input-rule-container-row)");
|
|
var height = size.height;
|
|
for (var i=0; i<rows.length; i++) {
|
|
height -= $(rows[i]).outerHeight(true);
|
|
}
|
|
var editorRow = $("#dialog-form>div.node-input-rule-container-row");
|
|
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
|
height += 16;
|
|
$("#node-input-rule-container").editableList('height',height);
|
|
},
|
|
label: function() {
|
|
return this.name || "Room State Events";
|
|
},
|
|
paletteLabel: 'Room State Events'
|
|
});
|
|
})();
|
|
</script> |