Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
6e76a80
Modify /poll so that it blocks until an event is received
ringtailsoftware Dec 22, 2022
84697b2
Merge remote-tracking branch 'remotes/labs/eventnotify' into eventnotify
ringtailsoftware Dec 22, 2022
9c3bf60
Add missing UserEvent.js file
ringtailsoftware Dec 22, 2022
2d98d44
Merge branch 'eventnotify' of labs:shuttlecraft into eventnotify
ringtailsoftware Dec 22, 2022
f61d750
Add file attachment support.
ringtailsoftware Dec 22, 2022
836a5cb
Fix posting with no attachment
ringtailsoftware Dec 22, 2022
fdfcb20
app.post() cannot be async, as onsubmit calls it synchronously. Inste…
ringtailsoftware Dec 22, 2022
154234d
Fix typo
ringtailsoftware Dec 22, 2022
2b590ee
Increase express max payload
ringtailsoftware Dec 22, 2022
0214d63
Move file structure logic out of storage.js
ringtailsoftware Dec 22, 2022
0c2d5a8
For media, store 2 files. <hash> for the binary data, <hash>.json for…
ringtailsoftware Dec 22, 2022
1508961
Add alt-text (description) for media uploads
ringtailsoftware Dec 22, 2022
70c2917
Add blurhash and image dimensions support
ringtailsoftware Dec 22, 2022
22759f6
Use mimetype to choose meaningful filename for data file
ringtailsoftware Dec 22, 2022
3e43755
On first poll, immediately return with results
ringtailsoftware Dec 22, 2022
e891012
When /poll request is closed, abort polling.
ringtailsoftware Dec 22, 2022
1b3b34b
In the ActivityPub Note, set the URL of the media file to <hash>.ext …
ringtailsoftware Dec 22, 2022
3e48825
Process hashtags in content body.
ringtailsoftware Dec 23, 2022
1cd0f58
Add route and placeholder template for /tags/:tag so clicking a tag d…
ringtailsoftware Dec 23, 2022
943c2c9
Render Question type, just a disabled form for now, but less confusin…
ringtailsoftware Dec 23, 2022
d0cfbee
Add voting in polls, with multiple Note sends for anyOf Questions.
ringtailsoftware Dec 23, 2022
a5c2e9e
Working construction and sending of polls.
ringtailsoftware Dec 23, 2022
9204baf
Add poll validation (can't have choices with no text)
ringtailsoftware Dec 23, 2022
37a9134
Fix case where poll is created then toggled off before posting
ringtailsoftware Dec 23, 2022
89cf80f
Patch incoming messages with a datestamp if missing, else storage wil…
ringtailsoftware Dec 23, 2022
ea1aa91
Record incoming votes
ringtailsoftware Dec 23, 2022
d057ec7
Fix refresh of poll via Mastodon
ringtailsoftware Dec 23, 2022
c33f59a
Show more friendly end time for polls
ringtailsoftware Dec 23, 2022
9c2cadf
Fix replying to (voting in) polls, where polldata elements no longer …
ringtailsoftware Dec 23, 2022
b1c16ee
Added settings page
ringtailsoftware Dec 24, 2022
8afa147
Add edit button and composer changes to allow editing of sent posts.
ringtailsoftware Dec 24, 2022
3d5b985
Merge remote-tracking branch 'remotes/origin/eventnotify' into dev
ringtailsoftware Dec 26, 2022
0e82329
Merge remote-tracking branch 'remotes/origin/post_hashtags' into dev
ringtailsoftware Dec 26, 2022
461e622
Merge remote-tracking branch 'remotes/origin/attachments' into dev
ringtailsoftware Dec 26, 2022
611c96c
Fix case where there are no hashtags
ringtailsoftware Dec 26, 2022
3d22f5b
Merge branch 'post_hashtags' into dev
ringtailsoftware Dec 26, 2022
3649d61
Merge remote-tracking branch 'remotes/origin/settings' into dev
ringtailsoftware Dec 26, 2022
cf0f524
Merge remote-tracking branch 'remotes/origin/question' into dev
ringtailsoftware Dec 26, 2022
2a9b517
Fix for vote notifications not showing properly
ringtailsoftware Dec 26, 2022
1c10146
Merge remote-tracking branch 'remotes/origin/question' into dev
ringtailsoftware Dec 26, 2022
ce372b2
Merge remote-tracking branch 'remotes/origin/edit' into dev
ringtailsoftware Dec 26, 2022
38577a4
Remove change of preferredUsername, this breaks search from Mastodon …
ringtailsoftware Dec 26, 2022
70bb506
Merge branch 'settings' into dev
ringtailsoftware Dec 26, 2022
4df90f2
Raise notification for DM.
ringtailsoftware Dec 26, 2022
32b25c5
Merge branch 'eventnotify' into dev
ringtailsoftware Dec 26, 2022
6a2c6dc
Render (disabled) poll in public note view
ringtailsoftware Dec 26, 2022
8fd5cba
Merge branch 'question' into dev
ringtailsoftware Dec 26, 2022
71ba5e9
Add direct link from DM notification to message viewer
ringtailsoftware Dec 26, 2022
930daf4
Merge branch 'eventnotify' into dev
ringtailsoftware Dec 26, 2022
7d16068
Use addNotification for incoming unread main timeline messages to upd…
ringtailsoftware Dec 26, 2022
f9bd654
Merge branch 'eventnotify' into dev
ringtailsoftware Dec 26, 2022
be94b6d
If GET /poll fails (e.g. timeout from nginx), repoll. Previously it w…
ringtailsoftware Dec 26, 2022
7eb0446
Merge branch 'eventnotify' into dev
ringtailsoftware Dec 26, 2022
a5012bb
Add logic to handle Delete of posts
ringtailsoftware Dec 26, 2022
892e932
Merge remote-tracking branch 'origin/deletepost' into dev
ringtailsoftware Dec 26, 2022
ab7434b
Merge remote-tracking branch 'origin/deletepost' into dev
ringtailsoftware Dec 26, 2022
b201d3b
Merge branch 'dev' of https://github.com/ringtailsoftware/shuttlecraf…
ringtailsoftware Dec 26, 2022
54c440a
Fix broken <title> on settings page
ringtailsoftware Dec 26, 2022
be134a6
Merge branch 'settings' into dev
ringtailsoftware Dec 26, 2022
c039785
Merge branch 'dev' of https://github.com/ringtailsoftware/shuttlecraf…
ringtailsoftware Dec 26, 2022
381c18e
Add avatar to list of DMs
ringtailsoftware Dec 26, 2022
e0cb638
Show mini avatars on DM sender list
ringtailsoftware Dec 26, 2022
746b4d8
Fix link in list of avatars on message page
ringtailsoftware Dec 27, 2022
7f3a75d
Merge branch 'dmavatarmini' into dev
ringtailsoftware Dec 27, 2022
0bcdaef
Add a beep sound
ringtailsoftware Dec 27, 2022
cb248c9
Merge remote-tracking branch 'origin/beep' into dev
ringtailsoftware Dec 27, 2022
a32ca55
Add a beep sound
ringtailsoftware Dec 27, 2022
012dace
Merge remote-tracking branch 'origin/beepsound'
ringtailsoftware Dec 27, 2022
faff916
Merge remote-tracking branch 'origin/beepsound' into dev
ringtailsoftware Dec 27, 2022
25ab388
Merge branch 'dev'
ringtailsoftware Dec 27, 2022
95cce40
Provide follower/following counts for notifications page
ringtailsoftware Dec 27, 2022
be3b060
Merge remote-tracking branch 'origin/fix71' into dev
ringtailsoftware Dec 27, 2022
93df05c
Merge branch 'dev'
ringtailsoftware Dec 27, 2022
3a1dd5c
Fix double posting bug caused in merge of attachments and edit
ringtailsoftware Dec 27, 2022
4c53d3e
Add --border-radius to root css config, used to round tops of boxes
ringtailsoftware Dec 27, 2022
b90e3bf
Merge remote-tracking branch 'origin/roundedcorners' into dev
ringtailsoftware Dec 27, 2022
3053408
Merge branch 'dev'
ringtailsoftware Dec 27, 2022
0d7eb70
No not raise DMs or unread messages as notifications, instead trigger…
ringtailsoftware Dec 27, 2022
9b251d2
Merge branch 'eventnotify' into dev
ringtailsoftware Dec 27, 2022
640f790
Add a button to settings page to export .data directory.
ringtailsoftware Dec 29, 2022
10dad29
Merge branch 'dev'
ringtailsoftware Dec 29, 2022
606c984
Add button to settings page to export CSV list of followed accounts
ringtailsoftware Dec 29, 2022
292a7ed
Merge branch 'dev'
ringtailsoftware Dec 29, 2022
9cb6d04
Add button to settings to import CSV list of accounts to follow
ringtailsoftware Dec 29, 2022
1868a69
Merge branch 'dev'
ringtailsoftware Dec 29, 2022
71f4f5b
Add hashtags to INDEX, on request for /tags/:tag, generate a listing …
ringtailsoftware Dec 29, 2022
5c3a3af
Fix crash when get non-existent note occurs.
ringtailsoftware Dec 31, 2022
e3fe301
Don't crash when unknown m/:id is requested
ringtailsoftware Dec 31, 2022
99948e4
Don't crash when asked to delete non existent id
ringtailsoftware Jan 1, 2023
1a0a4d9
Fix issue where failure to fetch boosted post (e.g. deleted) causes c…
ringtailsoftware Jan 1, 2023
933da4b
Use .wav file for beep instead of data URL
ringtailsoftware Jan 3, 2023
cf492c9
Merge branch 'beepsound' into dev
ringtailsoftware Jan 3, 2023
23cddd7
Fix merge
ringtailsoftware Jan 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions design/dashboard.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
🚀 boosted by <a href="{{booster.url}}">{{booster.name}}</a>
</div>
{{/if}}
{{> note note=note}}
{{> note note=note me=../me}}
</div>
{{/with}}
{{/each}}
Expand All @@ -37,4 +37,4 @@
{{/if}}{{/each}}

app.pollForPosts();
</script>
</script>
10 changes: 6 additions & 4 deletions design/dms.handlebars
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<div class="feeds {{#if recipient}}inbox_visible{{/if}}">
<header>Messages</header>
{{#if inboxes}}
{{#each inboxes}}
{{#if inboxesWithAccounts}}
{{#each inboxesWithAccounts}}
<div class="feed {{#if this.unread}}unread{{/if}} {{#isEq this.id ../recipient.id}}current{{/isEq}}">
<a href="/private/dms/{{getUsername this.id}}">{{getUsername this.id}}</a>
<a href="/private/dms/{{getUsername this.id}}">
{{> avatar actor=this.actor mini=true nolink=true}}
{{getUsername this.id}}</a>
</div>
{{/each}}
{{else}}
Expand Down Expand Up @@ -38,4 +40,4 @@
{{/if}}
<script>
app.pollForPosts();
</script>
</script>
1 change: 1 addition & 0 deletions design/layouts/private.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<li><a href="{{rootUrl}}/private/notifications">🔔 <span class="label">Notifications</span></a><span class="badge" id="newNotificationsBadge" hidden></span></li>
<li><a href="{{rootUrl}}/private/dms">💬 <span class="label">Messages</span></a><span class="badge" id="newDMsBadge" hidden></span></li>
<li><a href="{{rootUrl}}/private/post">➕ <span class="label">Compose</span></a></li>
<li><a href="{{rootUrl}}/private/settings">⚙️ <span class="label">Settings</span></a></li>

</ul>
</nav>
Expand Down
5 changes: 4 additions & 1 deletion design/notifications.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
{{{...note.content}}}
</div>
{{/isEq}}
{{#isEq notification.type "Vote"}}
<div class="notification">🗳 <a href="/private/profile/{{getUsername ../actor.id}}">{{../actor.name}}</a> voted on <a href="{{../note.id}}">your poll</a> {{timesince ../time}}</div>
{{/isEq}}
{{#isEq notification.type "Follow"}}
<div class="notification">🤷🏽‍♂️ <a href="/private/profile/{{getUsername ../actor.id}}">{{../actor.name}}</a> followed you {{timesince ../time}}</div>
{{> byline actor=../actor}}
Expand All @@ -42,4 +45,4 @@
{{/if}}{{/each}}

app.pollForPosts();
</script>
</script>
18 changes: 16 additions & 2 deletions design/partials/avatar.handlebars
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
{{#if nolink}}
{{else}}
<a href="{{#if public}}{{or actor.url actor.id}}{{else}}/private/profile/{{getUsername actor.id}}{{/if}}">
{{/if}}
{{#if actor.icon.url}}
<img src="{{actor.icon.url}}" class="avatar">
{{#if mini}}
<img src="{{actor.icon.url}}" class="mini-avatar">
{{else}}
<img src="{{actor.icon.url}}" class="avatar">
{{/if}}
{{else}}
<img src="/images/avatar-unknown.png" class="avatar">
{{#if mini}}
<img src="/images/avatar-unknown.png" class="mini-avatar">
{{else}}
<img src="/images/avatar-unknown.png" class="avatar">
{{/if}}
{{/if}}
{{#if nolink}}
{{else}}
</a>
{{/if}}
183 changes: 171 additions & 12 deletions design/partials/composer.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,184 @@
{{{originalPost.content}}}
</div>
{{else}}
<Header>Compose</Header>
{{#if prev}}
<Header>Edit</Header>
{{else}}
{{/if}}
{{/if}}
<form onsubmit="return app.post()" id="composer">
<textarea id="post" required placeholder="The coolest thing I can think of right now..." onfocus="this.value = this.value;">{{#if actor}}@{{getUsername actor.id}} {{/if}}</textarea>
<input type="text" id="cw" placeholder="content warning" />

<form onsubmit="return validatePoll() && app.post()" id="composer">
{{#if names}} <!-- When voting, show the user's choice before posting -->
Vote for:
<ul>
{{#each names}}
<li>{{this}}</li>
{{/each}}
</ul>
<textarea id="post" hidden></textarea> <!-- no post content on poll vote -->
<input type="text" id="cw" hidden /> <!-- no cw on poll vote -->
{{else}}
<textarea id="post" required placeholder="The coolest thing I can think of right now..." onfocus="this.value = this.value;">{{#if actor}}@{{getUsername actor.id}} {{/if}}{{#if prev}}{{stripHTML prev.content}}{{/if}}</textarea>
<input type="text" id="cw" placeholder="content warning"
{{#if prev}}value="{{{prev.summary}}}"{{/if}}
/>
<input id="inReplyTo" placeholder="in reply to" hidden
{{#if prev}}
value="{{{prev.inReplyTo}}}"
{{else}}
value="{{inReplyTo}}"
{{/if}}
/>
<input id="to" placeholder="to" hidden value="{{to}}"
{{#if prev}}
value="{{{prev.to}}}"
{{else}}
value="{{to}}"
{{/if}}
/>
<input id="attachment" type="file" />
<input id="description" type="text" placeholder="alt text" />
<div id="polldesigner" style="display:none"></div> <!-- The interactive poll designer -->
<input id="polldata" type="text" hidden> <!-- JSON data for the designed poll -->
<button id="togglepoll" type="button">📊</button>
{{#if prev}}
<input id="editOf" value="{{{prev.id}}}" hidden/>
{{/if}}
{{/if}}

<input id="inReplyTo" placeholder="in reply to" value="{{inReplyTo}}" hidden />
<input id="to" placeholder="to" value="{{to}}" hidden />

{{#each names}} <!-- replying to a post containing a poll, embed user's choice in field so it can be posted -->
<input class="pollchoice" value="{{{this}}}" hidden />
{{/each}}

<button id="submit" type="submit">Post</button>
</form>
</div>
<script>
let polldata;

let times = {
'5 minutes': 5 * 60,
'30 minutes': 30 * 60,
'1 hour': 60 * 60,
'6 hours': 6 * 60 * 60,
'1 day': 24 * 60 * 60,
'3 days': 3 * 24 * 60 * 60,
'7 days': 7 * 24 * 60 * 60
};

function pollReset() {
polldata = {
type: 'oneOf',
choices: [null, null],
time: 86400,
};
}

function escapeHTML(str) {
return str
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

function addChoice() {
polldata.choices.push(null);
updatePollHTML(false);
}

function deleteChoice(n) {
polldata.choices.splice(n, 1);
updatePollHTML(false);
}

function changeChoiceText(n, value) {
polldata.choices[n] = value;
updatePollHTML(true);
}

function togglePollType() {
if (polldata.type === 'oneOf') {
polldata.type = 'anyOf';
} else {
polldata.type = 'oneOf';
}
updatePollHTML(false);
return false;
}

function updateTime(value) {
polldata.time = parseInt(value);
updatePollHTML(true);
}

function validatePoll() {
if (document.getElementById('polldesigner')) {
if (document.getElementById('polldesigner').style.display === 'none') {
return true;
}
return !polldata.choices.includes(null); // undefined elements in poll
} else {
return true;
}
}

function updatePollHTML(dataOnly) {
let div = document.getElementById('polldesigner');
if (!dataOnly) {
let html = '';
html += '<form>';
for (let i=0;i<polldata.choices.length;i++) {
let radioOrCheckbox = '<a href="#" onclick="togglePollType()">' + (polldata.type == 'oneOf' ? '🔘' : '🔲') + '</a>';
let nameBox = '<input type="text" value="' + escapeHTML(polldata.choices[i] ? polldata.choices[i] : '') + '" size="20" onChange="changeChoiceText(' + i + ', this.value)" placeholder="Choice ' + (i+1) + '">';
let deleteButton = '<a href="#" onclick="deleteChoice(' + i + ')">' + (i > 1 ? '❎' : '') + '</a>';
html += radioOrCheckbox + nameBox + deleteButton + '<br>';
}

html += '<input type="button" value="➕ Add a choice" onclick="addChoice()">';
html += '<select id="time" name="time" onChange="updateTime(this.value)">';
Object.keys(times).forEach((k) => {
html += ' <option value="' + times[k] + '" ' + (polldata.time == times[k] ? 'selected' : '') + '>' + k + '</option>';
});
html += '</select>';
html += '</form>';
// update display
div.innerHTML = html;
}
// update hidden field with data so that it gets posted
if (validatePoll()) {
document.getElementById('polldata').value = JSON.stringify(polldata);
}
}

document.addEventListener("DOMContentLoaded", function(event) {
pollReset();

// scroll to bottom
var div = document.getElementById("main");
div.scrollTop = div.scrollHeight;
// scroll to bottom
var div = document.getElementById("main");
div.scrollTop = div.scrollHeight;

// focus the text input
const input = document.getElementById('post');
input.focus();
input.selectionStart = input.selectionEnd = input.value.length;
// focus the text input
const input = document.getElementById('post');
input.focus();
input.selectionStart = input.selectionEnd = input.value.length;

</script>
let togglepollButton = document.getElementById('togglepoll');
togglepollButton.onclick = function() {
let div = document.getElementById('polldesigner');
if (div.style.display !== 'none') {
div.style.display = 'none';
pollReset();
updatePollHTML(true);
} else {
pollReset();
updatePollHTML(false);
div.style.display = 'block';
}
};
});
</script>
29 changes: 29 additions & 0 deletions design/partials/note.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,28 @@
<p><a href="{{note.inReplyTo}}" class="showThread">Show Thread</a></p>
{{/if}}

{{#if (isEq note.type 'Question')}}
<form id="{{note.id}}">
<fieldset>
{{#each note.oneOf}}
<input type="radio" id="poll_option{{@index}}" name="pollchoice" value="{{name}}">
<label for="{{@index}}">{{name}}</label> ({{replies.totalItems}})<br>
{{/each}}
{{#each note.anyOf}}
<input type="checkbox" id="poll_option{{@index}}" name="pollchoice" value="{{name}}">
<label for="{{@index}}">{{name}}</label> ({{replies.totalItems}})<br>
{{/each}}
</fieldset>
</form>
{{#if (expired note.endTime)}}
Ended {{timesince note.endTime}}
{{else}}
Ends {{timesince note.endTime}}
{{/if}}
<br>
Votes cast: {{note.votersCount}}<br>
{{/if}}

{{#each note.attachment}}
<div class="attachment">
{{#isImage mediaType}}
Expand All @@ -25,6 +47,7 @@
</div>
{{/each}}
<footer>
{{#if note}}
<div class="tools">
<button onclick='return app.replyTo("{{note.id}}","{{{getUsername actor.id}}}")'>↩️</button>
<button class="booster {{#if note.isBoosted}}active{{/if}}" onclick='return app.toggleBoost(this, "{{note.id}}")'>
Expand All @@ -35,7 +58,13 @@
<span class="active">⭐️</span>
<span class="inactive">☆</span>
</button>
{{#if (isEq note.attributedTo me.id)}}
<button onclick='return app.editPost("{{note.id}}")'>
<span>✏️</span>
</button>
{{/if}}
</div>
{{/if}}
<a href="{{note.url}}" class="permalink">{{timesince note.published}}</a>
</footer>
</div>
21 changes: 21 additions & 0 deletions design/public/note.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@
{{/isVideo}}
</div>
{{/each}}
{{#if (isEq note.type 'Question')}}
<form id="{{note.id}}">
<fieldset>
{{#each note.oneOf}}
<input type="radio" id="poll_option{{@index}}" name="pollchoice" value="{{name}}" disabled>
<label for="{{@index}}">{{name}}</label> ({{replies.totalItems}})<br>
{{/each}}
{{#each note.anyOf}}
<input type="checkbox" id="poll_option{{@index}}" name="pollchoice" value="{{name}}" disabled>
<label for="{{@index}}">{{name}}</label> ({{replies.totalItems}})<br>
{{/each}}
</fieldset>
</form>
{{#if (expired note.endTime)}}
Ended {{timesince note.endTime}}
{{else}}
Ends {{timesince note.endTime}}
{{/if}}
<br>
Votes cast: {{note.votersCount}}<br>
{{/if}}
<footer>
<div class="tools">
{{#if stats}}
Expand Down
35 changes: 35 additions & 0 deletions design/public/tag.handlebars
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<div class="stream">
<div class="follow_box">
<p>
Follow this site on Mastodon or other fediverse clients:
<input type="text" value="{{user}}@{{domain}}" size="50"/>
</p>
</div>
<header class="back">
<a href="#" onclick="history.back(); return false;"><img src="{{actor.icon.url}}" class="avatar"></a>
<a href="#" onclick="history.back(); return false;">Back to {{actor.preferredUsername}}</a>
</header>

<header>
Posts containing #{{tag}}
</header>

{{#each notes}}
<div class="activity">
{{#with this}}
{{#if summary}}
<div class="content_warning">
content warning: {{summary}}
</div>
{{/if}}
<div class="content">
{{{content}}}

<a href="{{url}}" class="permalink">{{timesince published}}</a>
</div>
{{/with}}
</div>
{{/each}}


</div>
Loading