Multi-item posts; cut-and-paste; drag-and-drop
This commit is contained in:
parent
b946bbec3c
commit
4454fe4c03
|
@ -127,6 +127,7 @@ img.avatar {
|
||||||
border-radius: 1.5rem;
|
border-radius: 1.5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: 0 0px;
|
margin: 0 0px;
|
||||||
|
min-height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post p {
|
.post p {
|
||||||
|
@ -165,7 +166,44 @@ img.avatar {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-body > img {
|
.post-date {
|
||||||
|
float: right;
|
||||||
|
height: 0.25em;
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-author {
|
||||||
|
/* font-weight: bold; */
|
||||||
|
font-size: 0.75rem;
|
||||||
|
position: relative;
|
||||||
|
top: -0.75em;
|
||||||
|
height: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item {
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item-draft {
|
||||||
|
/* background: #e8e8ff; */
|
||||||
|
background: white;
|
||||||
|
border: solid #d3d3d3 1px;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item-draft .close-draft {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item-image {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-item-draft .post-item-image {
|
||||||
|
max-width: 80%;
|
||||||
|
max-height: 30vh;
|
||||||
}
|
}
|
|
@ -31,10 +31,10 @@
|
||||||
</div>
|
</div>
|
||||||
{{/showConversationList}}
|
{{/showConversationList}}
|
||||||
{{#showConversationMain}}
|
{{#showConversationMain}}
|
||||||
<div class="col-md-8 h-100 column-container">
|
<div id="conversation-main" class="col-md-8 h-100 column-container">
|
||||||
{{#selected}}
|
{{#selected}}
|
||||||
|
|
||||||
<div class="column-fill post-backdrop">
|
<div class="column-fill post-backdrop {{^miniMode}}not-{{/miniMode}}mini-mode">
|
||||||
{{#miniMode}}
|
{{#miniMode}}
|
||||||
<div class="conversation-control-panel bg-primary text-white px-1 mb-1">
|
<div class="conversation-control-panel bg-primary text-white px-1 mb-1">
|
||||||
<div class="float-right dropdown">
|
<div class="float-right dropdown">
|
||||||
|
@ -127,9 +127,13 @@
|
||||||
{{/showConversationPosts}}
|
{{/showConversationPosts}}
|
||||||
</div>
|
</div>
|
||||||
{{#showConversationPosts}}
|
{{#showConversationPosts}}
|
||||||
|
<div id="pending-draft-items">
|
||||||
|
</div>
|
||||||
<form id="message-input-form" class="form-inline pt-1" style="display: flex;">
|
<form id="message-input-form" class="form-inline pt-1" style="display: flex;">
|
||||||
<input type="text" autocomplete="off" id="message-input" class="form-control" style="flex: 1">
|
<input type="text" autocomplete="off" id="message-input" class="form-control" style="flex: 1">
|
||||||
<button id="send-message-button" class="form-control btn btn-primary" style="max-width: 3em"><i class="icon ion-paper-airplane"></i></button>
|
<input type="file" style="display: none;" hidden id="attach-item-file">
|
||||||
|
<button type="button" id="attach-item-button" class="form-control btn btn-secondary" style="max-width: 3em; font-size: 120%;"><i class="icon ion-paperclip"></i></button>
|
||||||
|
<button type="submit" id="send-message-button" class="form-control btn btn-primary btn-default" style="max-width: 3em"><i class="icon ion-paper-airplane"></i></button>
|
||||||
</form>
|
</form>
|
||||||
{{/showConversationPosts}}
|
{{/showConversationPosts}}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<div id="post-{{postId}}" class="post {{postClass}}">
|
|
||||||
<div class="post-body {{contentType}}">
|
|
||||||
<p>{{content}}</p>
|
|
||||||
<p class="align-right"><small>{{author}}<br>{{date}}</small></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div id="post-{{postId}}" class="post {{#fromMe}}from-me{{/fromMe}}{{^fromMe}}to-me{{/fromMe}}">
|
||||||
|
<div class="post-body {{contentClass}} clearfix">
|
||||||
|
{{^fromMe}}<p class="post-author text-muted">{{author}}</p>{{/fromMe}}
|
||||||
|
<div class="post-item-container"></div>
|
||||||
|
<div class="post-date text-muted">{{time}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1 @@
|
||||||
|
<img class="post-item-image" src="{{itemURL}}">
|
|
@ -0,0 +1 @@
|
||||||
|
<p>{{item.data}}</p>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<div id="{{itemId}}" class="post-item {{#postInfo.isDraft}}post-item-draft{{/postInfo.isDraft}} {{contentClass}} clearfix">
|
||||||
|
{{#postInfo.isDraft}}<button class="btn close-draft"><i class="icon ion-close"></i></button>{{/postInfo.isDraft}}
|
||||||
|
<div class="post-item-body-container"></div>
|
||||||
|
</div>
|
|
@ -14,7 +14,7 @@
|
||||||
assertion type conversation(id, title, creator, blurb);
|
assertion type conversation(id, title, creator, blurb);
|
||||||
assertion type invitation(conversationId, inviter, invitee);
|
assertion type invitation(conversationId, inviter, invitee);
|
||||||
assertion type inConversation(conversationId, member) = "in-conversation";
|
assertion type inConversation(conversationId, member) = "in-conversation";
|
||||||
assertion type post(id, timestamp, conversationId, author, contentType, content);
|
assertion type post(id, timestamp, conversationId, author, items);
|
||||||
|
|
||||||
message type createResource(description) = "create-resource";
|
message type createResource(description) = "create-resource";
|
||||||
message type updateResource(description) = "update-resource";
|
message type updateResource(description) = "update-resource";
|
||||||
|
@ -40,6 +40,9 @@
|
||||||
assertion type selectedCid(cid); // currently-selected conversation ID, or null
|
assertion type selectedCid(cid); // currently-selected conversation ID, or null
|
||||||
message type windowWidthChanged(newWidth);
|
message type windowWidthChanged(newWidth);
|
||||||
|
|
||||||
|
assertion type draftItem(timestamp, dataURL);
|
||||||
|
message type draftSent();
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
var brokerConnected = Syndicate.Broker.brokerConnected;
|
var brokerConnected = Syndicate.Broker.brokerConnected;
|
||||||
|
@ -75,6 +78,10 @@
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
document.addEventListener('dragover', function (e) {
|
||||||
|
e.preventDefault(); // make it so drag-and-drop doesn't load the dropped object into the browser
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
if (document.body.id === 'webchat-main') {
|
if (document.body.id === 'webchat-main') {
|
||||||
$('head meta').each(function (_i, tag) {
|
$('head meta').each(function (_i, tag) {
|
||||||
|
@ -432,6 +439,10 @@
|
||||||
field this.latestPostTimestamp = 0;
|
field this.latestPostTimestamp = 0;
|
||||||
field this.latestPostId = null;
|
field this.latestPostId = null;
|
||||||
|
|
||||||
|
field this.draftItems = Immutable.Map();
|
||||||
|
on asserted draftItem($ts, $d) { this.draftItems = this.draftItems.set(ts, d); }
|
||||||
|
on retracted draftItem($ts, _) { this.draftItems = this.draftItems.remove(ts); }
|
||||||
|
|
||||||
assert mainpage_c.html('div#main-div', Mustache.render(mainEntry, {
|
assert mainpage_c.html('div#main-div', Mustache.render(mainEntry, {
|
||||||
miniMode: this.miniMode,
|
miniMode: this.miniMode,
|
||||||
showConversationList: !this.miniMode,
|
showConversationList: !this.miniMode,
|
||||||
|
@ -458,17 +469,81 @@
|
||||||
setTimeout(function () { e.target.scrollIntoView(false); }, 500);
|
setTimeout(function () { e.target.scrollIntoView(false); }, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var spawnItemFromDataURL = (function (ui) {
|
||||||
|
return function (dataURL) {
|
||||||
|
var timestamp = +(new Date());
|
||||||
|
actor {
|
||||||
|
field this.ui = ui.context('draft-post', timestamp);
|
||||||
|
assert draftItem(timestamp, dataURL);
|
||||||
|
manifestPostItem(this.ui,
|
||||||
|
'#pending-draft-items',
|
||||||
|
{
|
||||||
|
isDraft: true,
|
||||||
|
postId: 'draft',
|
||||||
|
timestamp: timestamp,
|
||||||
|
fromMe: true,
|
||||||
|
author: sessionInfo.email
|
||||||
|
},
|
||||||
|
dataURL);
|
||||||
|
stop on message draftSent();
|
||||||
|
stop on message this.ui.event('.close-draft', 'click', _);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(this.ui);
|
||||||
|
|
||||||
|
var handleDataTransfer = function (dataTransfer) {
|
||||||
|
return dataTransferFiles(dataTransfer, Syndicate.Dataspace.wrap(
|
||||||
|
function (dataURLs) {
|
||||||
|
dataURLs.forEach(spawnItemFromDataURL);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
on message mainpage_c.event('#conversation-main', 'drop', $e) {
|
||||||
|
handleDataTransfer.call(this, e.dataTransfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
on message mainpage_c.event('#message-input', '+paste', $e) {
|
||||||
|
if (handleDataTransfer.call(this, e.clipboardData)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
on message mainpage_c.event('#attach-item-button', 'click', _) {
|
||||||
|
console.log('clickenating');
|
||||||
|
$('#attach-item-file').click();
|
||||||
|
}
|
||||||
|
on message mainpage_c.event('#attach-item-file', 'change', $e) {
|
||||||
|
if (e.target.files) {
|
||||||
|
for (var i = 0; i < e.target.files.length; i++) {
|
||||||
|
var file = e.target.files[i];
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.addEventListener('load', Syndicate.Dataspace.wrap(function (e) {
|
||||||
|
spawnItemFromDataURL(e.target.result);
|
||||||
|
}));
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
on message mainpage_c.event('#send-message-button', 'click', _) {
|
on message mainpage_c.event('#send-message-button', 'click', _) {
|
||||||
|
var timestamp = +(new Date());
|
||||||
|
var items = this.draftItems.entrySeq().toArray();
|
||||||
|
items.sort(function (a, b) { return a[0] - b[0]; });
|
||||||
var message = ($("#message-input").val() || '').trim();
|
var message = ($("#message-input").val() || '').trim();
|
||||||
if (message) {
|
if (message) {
|
||||||
|
var b64 = btoa(unescape(encodeURIComponent(message))); // utf-8, then base64
|
||||||
|
items.push([timestamp,
|
||||||
|
"data:text/plain;charset=utf-8;base64," + encodeURIComponent(b64)]);
|
||||||
|
}
|
||||||
|
if (items.length) {
|
||||||
:: outbound(createResource(post(random_hex_string(16),
|
:: outbound(createResource(post(random_hex_string(16),
|
||||||
+(new Date()),
|
timestamp,
|
||||||
cid,
|
cid,
|
||||||
sessionInfo.email,
|
sessionInfo.email,
|
||||||
"text/plain",
|
items.map(function (di) { return di[1]; }))));
|
||||||
message)));
|
|
||||||
}
|
}
|
||||||
$("#message-input").val('').focus();
|
$("#message-input").val('').focus();
|
||||||
|
:: draftSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
on message mainpage_c.event('.invite-to-conversation', 'click', _) {
|
on message mainpage_c.event('.invite-to-conversation', 'click', _) {
|
||||||
|
@ -540,32 +615,33 @@
|
||||||
this.editingBlurb = false;
|
this.editingBlurb = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
during inbound(post($pid, $timestamp, cid, $author, $contentType, $content)) {
|
during mainpage_c.fragmentVersion($mainpageVersion) {
|
||||||
if (timestamp > this.latestPostTimestamp) {
|
during inbound(post($pid, $timestamp, cid, $author, $items)) {
|
||||||
this.latestPostTimestamp = timestamp;
|
var fromMe = (author === sessionInfo.email);
|
||||||
this.latestPostId = pid;
|
var postInfo = {
|
||||||
}
|
isDraft: false,
|
||||||
during mainpage_c.fragmentVersion($mainpageVersion) {
|
postId: pid,
|
||||||
function cleanContentType(t) {
|
timestamp: timestamp,
|
||||||
return t.replace('/', '-');
|
date: new Date(timestamp).toString(),
|
||||||
|
time: new Date(timestamp).toTimeString().substr(0, 8),
|
||||||
|
fromMe: fromMe,
|
||||||
|
author: author
|
||||||
|
};
|
||||||
|
if (timestamp > this.latestPostTimestamp) {
|
||||||
|
this.latestPostTimestamp = timestamp;
|
||||||
|
this.latestPostId = pid;
|
||||||
}
|
}
|
||||||
during inbound(
|
var c = this.ui.context(mainpageVersion, 'post', timestamp, pid);
|
||||||
uiTemplate("post-entry-" + cleanContentType(contentType) + ".html", $entry))
|
during inbound(uiTemplate("post-entry.html", $postEntryTemplate)) {
|
||||||
{
|
assert c.html('.posts', Mustache.render(postEntryTemplate, postInfo));
|
||||||
var c = this.ui.context(mainpageVersion, 'post', timestamp, pid);
|
during c.fragmentVersion(_) {
|
||||||
assert c.html('.posts', Mustache.render(entry, {
|
var itemCounter = 0;
|
||||||
postId: pid,
|
items.forEach((function (itemURL) {
|
||||||
date: new Date(timestamp).toString(),
|
manifestPostItem(c.context('item', itemCounter++),
|
||||||
postClass: (author === sessionInfo.email) ? "from-me" : "to-me",
|
'#post-' + pid + ' .post-item-container',
|
||||||
author: author,
|
postInfo,
|
||||||
contentType: cleanContentType(contentType),
|
itemURL);
|
||||||
content: content
|
}).bind(this));
|
||||||
}));
|
|
||||||
on asserted c.fragmentVersion(_) {
|
|
||||||
if ((this.latestPostTimestamp === timestamp) &&
|
|
||||||
(this.latestPostId === pid)) {
|
|
||||||
$("#post-" + pid)[0].scrollIntoView(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,6 +744,49 @@
|
||||||
// $("#debug-space").text(Syndicate.prettyTrie(mux.routingTable));
|
// $("#debug-space").text(Syndicate.prettyTrie(mux.routingTable));
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nextItemid = 0;
|
||||||
|
function manifestPostItem(uiContext, containerSelector, postInfo, itemURL) {
|
||||||
|
function cleanContentType(t) {
|
||||||
|
t = t.toLowerCase();
|
||||||
|
if (t.startsWith('image/')) {
|
||||||
|
t = 'image';
|
||||||
|
} else {
|
||||||
|
t = t.replace('/', '-');
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = parseDataURL(itemURL);
|
||||||
|
var itemId = 'post-' + postInfo.postId + '-item-' + nextItemid++;
|
||||||
|
var contentClass = cleanContentType(item.type);
|
||||||
|
var itemInfo = {
|
||||||
|
itemId: itemId,
|
||||||
|
postInfo: postInfo,
|
||||||
|
contentClass: contentClass,
|
||||||
|
item: item,
|
||||||
|
itemURL: itemURL
|
||||||
|
};
|
||||||
|
|
||||||
|
during inbound(uiTemplate("post-item.html", $postItemTemplate)) {
|
||||||
|
during inbound(uiTemplate("post-item-" + contentClass + ".html", $entry)) {
|
||||||
|
assert uiContext.html(containerSelector, Mustache.render(postItemTemplate, itemInfo));
|
||||||
|
on asserted uiContext.fragmentVersion(_) {
|
||||||
|
var innerContext = uiContext.context('item-body');
|
||||||
|
assert innerContext.html('#' + itemId + ' .post-item-body-container',
|
||||||
|
Mustache.render(entry, itemInfo));
|
||||||
|
if (!postInfo.isDraft) {
|
||||||
|
on asserted innerContext.fragmentVersion(_) {
|
||||||
|
if ((this.latestPostTimestamp === postInfo.timestamp) &&
|
||||||
|
(this.latestPostId === postInfo.postId)) {
|
||||||
|
setTimeout(function () { $("#post-" + postInfo.postId)[0].scrollIntoView(false); }, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -703,3 +822,133 @@ function random_hex_string(halfLength) {
|
||||||
}
|
}
|
||||||
return encoded.join('');
|
return encoded.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function parseDataURL(u) {
|
||||||
|
var pieces;
|
||||||
|
|
||||||
|
if (!u.startsWith('data:')) return null;
|
||||||
|
u = u.substr(5);
|
||||||
|
|
||||||
|
pieces = u.split(',');
|
||||||
|
if (pieces.length !== 2) return null;
|
||||||
|
|
||||||
|
var mimeType = pieces[0];
|
||||||
|
var data = decodeURIComponent(pieces[1]);
|
||||||
|
var isBase64 = false;
|
||||||
|
|
||||||
|
if (mimeType.endsWith(';base64')) {
|
||||||
|
mimeType = mimeType.substr(0, mimeType.length - 7);
|
||||||
|
isBase64 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBase64) {
|
||||||
|
data = atob(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pieces = mimeType.split(';');
|
||||||
|
var type = pieces[0];
|
||||||
|
|
||||||
|
var parameters = {};
|
||||||
|
for (var i = 1; i < pieces.length; i++) {
|
||||||
|
var m = pieces[i].match(/^([^=]+)=(.*)$/);
|
||||||
|
if (m) {
|
||||||
|
parameters[m[1].toLowerCase()] = m[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.startsWith('text/')) {
|
||||||
|
var charset = (parameters.charset || 'US-ASCII').toLowerCase();
|
||||||
|
switch (charset) {
|
||||||
|
case 'utf-8':
|
||||||
|
data = decodeURIComponent(escape(data));
|
||||||
|
break;
|
||||||
|
case 'us-ascii':
|
||||||
|
case 'ascii':
|
||||||
|
case 'latin1':
|
||||||
|
case 'iso-8859-1':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('Unknown charset while decoding data URL:', charset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: type,
|
||||||
|
parameters: parameters,
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Extract file contents from a DataTransfer object
|
||||||
|
function dataTransferFiles(d, k) {
|
||||||
|
var items = d.items;
|
||||||
|
var types = d.types;
|
||||||
|
var files = d.files;
|
||||||
|
|
||||||
|
var results = [];
|
||||||
|
var expectedCount = files.length;
|
||||||
|
var completedCount = 0;
|
||||||
|
|
||||||
|
function completeOne() {
|
||||||
|
completedCount++;
|
||||||
|
if (completedCount === expectedCount) {
|
||||||
|
k(results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
(function (i) {
|
||||||
|
var item = items[i];
|
||||||
|
var type = types[i];
|
||||||
|
if (type === 'text/uri-list') {
|
||||||
|
expectedCount++;
|
||||||
|
item.getAsString(function (itemstr) {
|
||||||
|
var firstChunk = itemstr.substr(0, 6).toLowerCase();
|
||||||
|
if (firstChunk.startsWith('http:') || firstChunk.startsWith('https:')) {
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: itemstr,
|
||||||
|
beforeSend: function (xhr) {
|
||||||
|
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||||
|
},
|
||||||
|
success: function (_data, _status, xhr) {
|
||||||
|
var contentType = xhr.getResponseHeader('content-type');
|
||||||
|
var rawdata = xhr.responseText;
|
||||||
|
var data = [];
|
||||||
|
for (var j = 0; j < rawdata.length; j++) {
|
||||||
|
data = data + String.fromCharCode(rawdata.charCodeAt(j) & 0xff);
|
||||||
|
}
|
||||||
|
results.push('data:' + contentType + ';base64,' + encodeURIComponent(btoa(data)));
|
||||||
|
completeOne();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
completeOne();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
completeOne();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
(function (i) {
|
||||||
|
var file = files[i];
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.addEventListener('load', function (e) {
|
||||||
|
results.push(e.target.result);
|
||||||
|
completeOne();
|
||||||
|
});
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (expectedCount > 0);
|
||||||
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
(assert (api (session who _) i)))
|
(assert (api (session who _) i)))
|
||||||
(during ($ c (conversation cid _ _ _))
|
(during ($ c (conversation cid _ _ _))
|
||||||
(assert (api (session who _) c)))
|
(assert (api (session who _) c)))
|
||||||
(during ($ p (post _ _ cid _ _ _))
|
(during ($ p (post _ _ cid _ _))
|
||||||
(assert (api (session who _) p))))))
|
(assert (api (session who _) p))))))
|
||||||
|
|
||||||
(supervise
|
(supervise
|
||||||
|
@ -120,16 +120,16 @@
|
||||||
(actor #:name 'post-factory
|
(actor #:name 'post-factory
|
||||||
(stop-when-reloaded)
|
(stop-when-reloaded)
|
||||||
(on (message (create-resource
|
(on (message (create-resource
|
||||||
($ p0 (post $pid $timestamp $cid $author $content-type $content0))))
|
($ p0 (post $pid $timestamp $cid $author $items0))))
|
||||||
(actor #:name p0
|
(actor #:name p0
|
||||||
(field [content content0])
|
(field [items items0])
|
||||||
(define/dataflow p (post pid timestamp cid author content-type (content)))
|
(define/dataflow p (post pid timestamp cid author (items)))
|
||||||
(assert (p))
|
(assert (p))
|
||||||
(stop-when-duplicate (list 'post cid pid))
|
(stop-when-duplicate (list 'post cid pid))
|
||||||
(stop-when (message (delete-resource (post pid _ cid _ _ _))))
|
(stop-when (message (delete-resource (post pid _ cid _ _))))
|
||||||
(stop-when (message (delete-resource (conversation cid _ _ _))))
|
(stop-when (message (delete-resource (conversation cid _ _ _))))
|
||||||
(on (message (update-resource (post pid _ cid _ _ $newcontent)))
|
(on (message (update-resource (post pid _ cid _ $newitems)))
|
||||||
(content newcontent))))))
|
(items newitems))))))
|
||||||
|
|
||||||
(supervise
|
(supervise
|
||||||
(actor #:name 'conversation:questions
|
(actor #:name 'conversation:questions
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
`()))
|
`()))
|
||||||
(div ((class "container main-container"))
|
(div ((class "container main-container"))
|
||||||
(div ((class "header clearfix"))
|
(div ((class "header clearfix"))
|
||||||
(nav ((class "navbar bg-faded"))
|
(nav ((class "navbar"))
|
||||||
;; (span ((id "nav-heading") (class "navbar-brand text-muted")) ,nav-heading)
|
;; (span ((id "nav-heading") (class "navbar-brand text-muted")) ,nav-heading)
|
||||||
(ul ((id "nav-ul") (class "nav navbar-nav nav-pills float-xs-right"))
|
(ul ((id "nav-ul") (class "nav navbar-nav nav-pills float-xs-right"))
|
||||||
;; (li ((class "nav-item")) (a ((class "nav-link active") (href "#")) "Home " (span ((class "sr-only")) "(current)")))
|
;; (li ((class "nav-item")) (a ((class "nav-link active") (href "#")) "Home " (span ((class "sr-only")) "(current)")))
|
||||||
|
|
|
@ -152,8 +152,7 @@
|
||||||
timestamp ;; Seconds
|
timestamp ;; Seconds
|
||||||
conversation-id ;; String
|
conversation-id ;; String
|
||||||
author ;; Principal
|
author ;; Principal
|
||||||
content-type ;; MimeTypeString
|
items ;; Listof DataURLString
|
||||||
content ;; Any
|
|
||||||
) #:prefab) ;; ASSERTION
|
) #:prefab) ;; ASSERTION
|
||||||
|
|
||||||
;;---------------------------------------------------------------------------
|
;;---------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue