Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
8a9502d1f2 | |||
534438df63 | |||
45a4feec96 | |||
aa7a32395e | |||
ab9f57f044 | |||
4040d6aa08 | |||
1c96f5c35e | |||
4d3e42812d | |||
f7b3711d4f | |||
2408e076ff | |||
6f71ffb477 | |||
214433f36a | |||
309b22732e | |||
6fe7687b2a | |||
a8cbf757ff |
@ -16,14 +16,14 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 37
|
VERSION_CODE := 38
|
||||||
VERSION_CODE_IOS := 13
|
VERSION_CODE_IOS := 14
|
||||||
VERSION_NUMBER := 0.0.31
|
VERSION_NUMBER := 0.0.32-wip
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.0
|
||||||
|
|
||||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3490200.zip
|
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500000.zip
|
||||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&0Lxm4IgS3mpvSccP3bg7wNPACtLKMTbie51ea/vJbeg=.sha256"
|
"previous": "&R6lVyXLYem8Qkuhok/USflvzqw/ZgGic1aUsE23yzR0=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -353,27 +353,37 @@ class TfElement extends LitElement {
|
|||||||
let latest_private = this.get_latest_private(following);
|
let latest_private = this.get_latest_private(following);
|
||||||
let channels = await tfrpc.rpc.query(
|
let channels = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
WHERE
|
WHERE
|
||||||
messages.content ->> 'type' = 'post' AND
|
messages.content ->> 'type' = 'post' AND
|
||||||
messages.content ->> 'root' IS NULL AND
|
messages.content ->> 'root' IS NULL AND
|
||||||
messages.author != ?4
|
messages.author != ?4
|
||||||
GROUP by channel
|
GROUP by channel
|
||||||
UNION
|
UNION
|
||||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
JOIN messages_refs ON messages.id = messages_refs.message
|
||||||
WHERE
|
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||||
messages.content ->> 'type' = 'post' AND
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
messages.content ->> 'root' IS NULL AND
|
WHERE
|
||||||
messages.author != ?4
|
messages.content ->> 'type' = 'post' AND
|
||||||
UNION
|
messages.content ->> 'root' IS NULL AND
|
||||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
messages.author != ?4
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
GROUP by channel
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
UNION
|
||||||
WHERE messages.author != ?4
|
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
`,
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'post' AND
|
||||||
|
messages.content ->> 'root' IS NULL AND
|
||||||
|
messages.author != ?4
|
||||||
|
UNION
|
||||||
|
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||||
|
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||||
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE messages.author != ?4
|
||||||
|
`,
|
||||||
[
|
[
|
||||||
JSON.stringify(this.channels),
|
JSON.stringify(this.channels),
|
||||||
JSON.stringify(following),
|
JSON.stringify(following),
|
||||||
@ -381,9 +391,15 @@ class TfElement extends LitElement {
|
|||||||
this.whoami,
|
this.whoami,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
this.channels_latest = Object.fromEntries(
|
let latest = {};
|
||||||
channels.map((x) => [x.channel, x.rowid])
|
for (let row of channels) {
|
||||||
);
|
if (!latest[row.channel]) {
|
||||||
|
latest[row.channel] = row.rowid;
|
||||||
|
} else {
|
||||||
|
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.channels_latest = latest;
|
||||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||||
let self = this;
|
let self = this;
|
||||||
start_time = new Date();
|
start_time = new Date();
|
||||||
|
@ -86,12 +86,16 @@ class TfMessageElement extends LitElement {
|
|||||||
|
|
||||||
render_votes() {
|
render_votes() {
|
||||||
function normalize_expression(expression) {
|
function normalize_expression(expression) {
|
||||||
if (expression === 'Like' || expression === 'like' || !expression) {
|
if (
|
||||||
return '👍';
|
expression === 'Unlike' ||
|
||||||
} else if (expression === 'Unlike' || expression === 'unlike') {
|
expression === 'unlike' ||
|
||||||
|
expression == 'undig'
|
||||||
|
) {
|
||||||
return '👎';
|
return '👎';
|
||||||
} else if (expression === 'heart') {
|
} else if (expression === 'heart') {
|
||||||
return '❤️';
|
return '❤️';
|
||||||
|
} else if ((expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)) {
|
||||||
|
return '👍';
|
||||||
} else {
|
} else {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
@ -297,31 +301,35 @@ class TfMessageElement extends LitElement {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expanded_key() {
|
||||||
|
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
||||||
|
}
|
||||||
|
|
||||||
set_expanded(expanded, tag) {
|
set_expanded(expanded, tag) {
|
||||||
|
let key = this.expanded_key();
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('tf-expand', {
|
new CustomEvent('tf-expand', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
detail: {id: key + (tag || ''), expanded: expanded},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_expanded(tag) {
|
toggle_expanded(tag) {
|
||||||
this.set_expanded(
|
let key = this.expanded_key();
|
||||||
!this.expanded[(this.message.id || '') + (tag || '')],
|
this.set_expanded(!this.expanded[key + (tag || '')], tag);
|
||||||
tag
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is_expanded(tag) {
|
is_expanded(tag) {
|
||||||
return this.expanded[(this.message.id || '') + (tag || '')];
|
let key = this.expanded_key();
|
||||||
|
return this.expanded[key + (tag || '')];
|
||||||
}
|
}
|
||||||
|
|
||||||
render_children() {
|
render_children() {
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message.child_messages?.length) {
|
if (this.message.child_messages?.length) {
|
||||||
if (!this.expanded[this.message.id]) {
|
if (!this.expanded[this.expanded_key()]) {
|
||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
@ -574,6 +582,43 @@ class TfMessageElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content_group_by_author() {
|
||||||
|
let sorted = this.message.messages
|
||||||
|
.map((x) => [
|
||||||
|
x.author,
|
||||||
|
x.content.blocking !== undefined
|
||||||
|
? x.content.blocking
|
||||||
|
? 'is blocking'
|
||||||
|
: 'is no longer blocking'
|
||||||
|
: x.content.following !== undefined
|
||||||
|
? x.content.following
|
||||||
|
? 'is following'
|
||||||
|
: 'is no longer following'
|
||||||
|
: '',
|
||||||
|
x.content.contact,
|
||||||
|
x,
|
||||||
|
])
|
||||||
|
.sort();
|
||||||
|
let result = [];
|
||||||
|
let last;
|
||||||
|
let group;
|
||||||
|
for (let row of sorted) {
|
||||||
|
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||||
|
group.push(row[2]);
|
||||||
|
} else {
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
last = row;
|
||||||
|
group = [row[2]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let content = this.message?.content;
|
let content = this.message?.content;
|
||||||
if (this.message?.decrypted?.type == 'post') {
|
if (this.message?.decrypted?.type == 'post') {
|
||||||
@ -582,29 +627,88 @@ class TfMessageElement extends LitElement {
|
|||||||
let class_background = this.class_background();
|
let class_background = this.class_background();
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message?.type === 'contact_group') {
|
if (this.message?.type === 'contact_group') {
|
||||||
return this.render_frame(
|
if (this.expanded[this.expanded_key()]) {
|
||||||
html` ${this.message.messages.map(
|
return this.render_frame(html`
|
||||||
(x) =>
|
<div class="w3-padding">
|
||||||
html`<tf-message
|
${this.message.messages.map(
|
||||||
.message=${x}
|
(x) =>
|
||||||
whoami=${this.whoami}
|
html`<tf-message
|
||||||
.users=${this.users}
|
.message=${x}
|
||||||
.drafts=${this.drafts}
|
whoami=${this.whoami}
|
||||||
.expanded=${this.expanded}
|
.users=${this.users}
|
||||||
channel=${this.channel}
|
.drafts=${this.drafts}
|
||||||
channel_unread=${this.channel_unread}
|
.expanded=${this.expanded}
|
||||||
></tf-message>`
|
channel=${this.channel}
|
||||||
)}`
|
channel_unread=${this.channel_unread}
|
||||||
);
|
></tf-message>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(false)}
|
||||||
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.content_group_by_author().map(
|
||||||
|
(x) => html`
|
||||||
|
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||||
|
${x.action}
|
||||||
|
${x.users.map(
|
||||||
|
(y) => html`
|
||||||
|
<tf-user id=${y} .users=${this.users}></tf-user>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(true)}
|
||||||
|
>
|
||||||
|
Expand
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
} else if (this.message.placeholder) {
|
} else if (this.message.placeholder) {
|
||||||
return this.render_frame(
|
return this.render_frame(
|
||||||
html`<div class="w3-padding">
|
html`<div>
|
||||||
<p>
|
<div class="w3-bar">
|
||||||
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
<a
|
||||||
>${this.message.id}</a
|
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
|
||||||
|
target="_top"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
>
|
>
|
||||||
(placeholder)
|
This message is not currently available.
|
||||||
</p>
|
</a>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>${this.render_votes()}</div>
|
<div>${this.render_votes()}</div>
|
||||||
${(this.message.child_messages || []).map(
|
${(this.message.child_messages || []).map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
@ -631,7 +735,7 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
if (content.image !== undefined) {
|
if (content.image !== undefined) {
|
||||||
image = html`
|
image = html`
|
||||||
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
<div @click=${this.body_click}><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
if (content.description !== undefined) {
|
if (content.description !== undefined) {
|
||||||
@ -654,25 +758,60 @@ class TfMessageElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
} else if (content.type == 'contact') {
|
} else if (content.type == 'contact') {
|
||||||
return html`
|
return this.render_frame(html`
|
||||||
<div class="w3-padding">
|
<div class="w3-bar">
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
<div class="w3-bar-item">
|
||||||
is
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||||
${content.blocking === true
|
is
|
||||||
? 'blocking'
|
${content.blocking === true
|
||||||
: content.blocking === false
|
? 'blocking'
|
||||||
? 'no longer blocking'
|
: content.blocking === false
|
||||||
: content.following === true
|
? 'no longer blocking'
|
||||||
? 'following'
|
: content.following === true
|
||||||
: content.following === false
|
? 'following'
|
||||||
? 'no longer following'
|
: content.following === false
|
||||||
: '?'}
|
? 'no longer following'
|
||||||
<tf-user
|
: '?'}
|
||||||
id=${this.message.content.contact}
|
<tf-user
|
||||||
.users=${this.users}
|
id=${this.message.content.contact}
|
||||||
></tf-user>
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
|
</div>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
${this.drafts[this.message?.id] === undefined
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
@click=${this.show_reply}
|
||||||
|
>
|
||||||
|
↩️ Reply
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${this.render_votes()} ${this.render_actions()}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`);
|
||||||
} else if (content.type == 'post') {
|
} else if (content.type == 'post') {
|
||||||
let self = this;
|
let self = this;
|
||||||
let body;
|
let body;
|
||||||
|
@ -166,7 +166,10 @@ class TfNewsElement extends LitElement {
|
|||||||
if (message?.content?.type === 'contact') {
|
if (message?.content?.type === 'contact') {
|
||||||
group.push(message);
|
group.push(message);
|
||||||
} else {
|
} else {
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
@ -177,7 +180,10 @@ class TfNewsElement extends LitElement {
|
|||||||
result.push(message);
|
result.push(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
|
@ -166,6 +166,40 @@ class TfProfileElement extends LitElement {
|
|||||||
navigator.clipboard.writeText(this.id);
|
navigator.clipboard.writeText(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_image(link) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.style.left = 0;
|
||||||
|
div.style.top = 0;
|
||||||
|
div.style.width = '100%';
|
||||||
|
div.style.height = '100%';
|
||||||
|
div.style.position = 'fixed';
|
||||||
|
div.style.background = '#000';
|
||||||
|
div.style.zIndex = 100;
|
||||||
|
div.style.display = 'grid';
|
||||||
|
let img = document.createElement('img');
|
||||||
|
img.src = link;
|
||||||
|
img.style.maxWidth = '100%';
|
||||||
|
img.style.maxHeight = '100%';
|
||||||
|
img.style.display = 'block';
|
||||||
|
img.style.margin = 'auto';
|
||||||
|
img.style.objectFit = 'contain';
|
||||||
|
img.style.width = '100%';
|
||||||
|
div.appendChild(img);
|
||||||
|
function image_close(event) {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
window.removeEventListener('keydown', image_close);
|
||||||
|
}
|
||||||
|
div.onclick = image_close;
|
||||||
|
window.addEventListener('keydown', image_close);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_click(event) {
|
||||||
|
if (event.srcElement.tagName == 'IMG') {
|
||||||
|
this.show_image(event.srcElement.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.load();
|
this.load();
|
||||||
let self = this;
|
let self = this;
|
||||||
@ -254,7 +288,7 @@ class TfProfileElement extends LitElement {
|
|||||||
<header class="w3-container">
|
<header class="w3-container">
|
||||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container">
|
<div class="w3-container" @click=${this.body_click}>
|
||||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||||
|
@ -308,6 +308,12 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
<div class="w3-bar-item">
|
<div class="w3-bar-item">
|
||||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||||
<div><small>${x.address}:${x.port}</small></div>
|
<div><small>${x.address}:${x.port}</small></div>
|
||||||
|
<div>
|
||||||
|
<small
|
||||||
|
>Last connection:
|
||||||
|
${new Date(x.last_success * 1000)}</small
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this.render_message(x)}
|
${this.render_message(x)}
|
||||||
|
@ -441,9 +441,14 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return cache(html`
|
return cache(html`
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
${!this.hash.startsWith('#%')
|
||||||
Mark All Read
|
? html`<button
|
||||||
</button>
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.mark_all_read}
|
||||||
|
>
|
||||||
|
Mark All Read
|
||||||
|
</button>`
|
||||||
|
: undefined}
|
||||||
<tf-news
|
<tf-news
|
||||||
id="news"
|
id="news"
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
|
@ -235,12 +235,18 @@ class TfTabNewsElement extends LitElement {
|
|||||||
<h4 style="margin: 0">Connections</h4>
|
<h4 style="margin: 0">Connections</h4>
|
||||||
</a>
|
</a>
|
||||||
${this.connections
|
${this.connections
|
||||||
.filter((x) => x.id && !x.destroy_reason)
|
.filter((x) => x.id)
|
||||||
.map(
|
.map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
<tf-user
|
<tf-user
|
||||||
class="w3-bar-item"
|
class="w3-bar-item"
|
||||||
style="max-width: 100%"
|
style=${x.destroy_reason
|
||||||
|
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||||
|
: x.connected
|
||||||
|
? x.flags?.one_shot
|
||||||
|
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||||
|
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||||
|
: ''}
|
||||||
id=${x.id}
|
id=${x.id}
|
||||||
fallback_name=${x.host}
|
fallback_name=${x.host}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.30";
|
version = "0.0.31";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitea {
|
src = pkgs.fetchFromGitea {
|
||||||
domain = "dev.tildefriends.net";
|
domain = "dev.tildefriends.net";
|
||||||
owner = "cory";
|
owner = "cory";
|
||||||
repo = "tildefriends";
|
repo = "tildefriends";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-t5yvouzSL2j/ge1VHLqzIZ+Avqj4iEDt7L+yrHoTZAQ=";
|
hash = "sha256-c2ZKVNikI5jN5GQuvp7S53qqnRZniSrJMF1FUZdVNPI=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
2
deps/codemirror/cm6.js
vendored
2
deps/codemirror/cm6.js
vendored
File diff suppressed because one or more lines are too long
6
deps/codemirror_src/package-lock.json
generated
vendored
6
deps/codemirror_src/package-lock.json
generated
vendored
@ -144,9 +144,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.36.8",
|
"version": "6.37.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.0.tgz",
|
||||||
"integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==",
|
"integrity": "sha512-ghHIeRGfWB8h9Tc3sMdr7D5zp4sZvlCzG36Xjdh2ymmfAwvSaCJAAsL3HLyLEnHcE953+5Uox1bx5OS+YCW/7Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
|
953
deps/sqlite/shell.c
vendored
953
deps/sqlite/shell.c
vendored
File diff suppressed because it is too large
Load Diff
4208
deps/sqlite/sqlite3.c
vendored
4208
deps/sqlite/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
146
deps/sqlite/sqlite3.h
vendored
146
deps/sqlite/sqlite3.h
vendored
@ -133,7 +133,7 @@ extern "C" {
|
|||||||
**
|
**
|
||||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||||
** SQLite source code has been stored in the
|
** SQLite source code has been stored in the
|
||||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||||
** a string which identifies a particular check-in of SQLite
|
** a string which identifies a particular check-in of SQLite
|
||||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||||
@ -146,9 +146,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.49.2"
|
#define SQLITE_VERSION "3.50.0"
|
||||||
#define SQLITE_VERSION_NUMBER 3049002
|
#define SQLITE_VERSION_NUMBER 3050000
|
||||||
#define SQLITE_SOURCE_ID "2025-05-07 10:39:52 17144570b0d96ae63cd6f3edca39e27ebd74925252bbaf6723bcb2f6b4861fb1"
|
#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
|||||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||||
** integer is overwritten with the previous value of M.
|
** integer is overwritten with the previous value of M.
|
||||||
**
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||||
|
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||||
|
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||||
|
** This is used to implement the functionality associated with
|
||||||
|
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||||
|
**
|
||||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||||
@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
#define SQLITE_FCNTL_NULL_IO 43
|
#define SQLITE_FCNTL_NULL_IO 43
|
||||||
|
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||||
** the default size of lookaside memory on each [database connection].
|
** the default size of [lookaside memory] on each [database connection].
|
||||||
** The first argument is the
|
** The first argument is the
|
||||||
** size of each lookaside buffer slot and the second is the number of
|
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
** slots allocated to each database connection ("cnt").)^
|
||||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||||
** configuration on individual connections.)^ </dd>
|
** be used to change the lookaside configuration on individual connections.)^
|
||||||
|
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||||
|
** default lookaside configuration at compile-time.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||||
@ -2232,31 +2242,50 @@ struct sqlite3_mem_methods {
|
|||||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||||
** configuration of the lookaside memory allocator within a database
|
** configuration of the [lookaside memory allocator] within a database
|
||||||
** connection.
|
** connection.
|
||||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||||
** in the [DBCONFIG arguments|usual format].
|
** in the [DBCONFIG arguments|usual format].
|
||||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||||
** should have a total of five parameters.
|
** should have a total of five parameters.
|
||||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
** <ol>
|
||||||
|
** <li><p>The first argument ("buf") is a
|
||||||
** pointer to a memory buffer to use for lookaside memory.
|
** pointer to a memory buffer to use for lookaside memory.
|
||||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
** The first argument may be NULL in which case SQLite will allocate the
|
||||||
** may be NULL in which case SQLite will allocate the
|
** lookaside buffer itself using [sqlite3_malloc()].
|
||||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
** <li><P>The second argument ("sz") is the
|
||||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||||
** slots. The size of the buffer in the first argument must be greater than
|
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||||
** or equal to the product of the second and third arguments. The buffer
|
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
** it does.
|
||||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||||
|
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||||
|
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||||
|
** than 1,000,000.
|
||||||
|
** </ol>
|
||||||
|
** <p>If the "buf" argument is not NULL, then it must
|
||||||
|
** point to a memory buffer with a size that is greater than
|
||||||
|
** or equal to the product of "sz" and "cnt".
|
||||||
|
** The buffer must be aligned to an 8-byte boundary.
|
||||||
|
** The lookaside memory
|
||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].
|
||||||
|
** If the "buf" argument is NULL and an attempt
|
||||||
|
** to allocate memory based on "sz" and "cnt" fails, then
|
||||||
|
** lookaside is silently disabled.
|
||||||
|
** <p>
|
||||||
|
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||||
|
** default lookaside configuration at initialization. The
|
||||||
|
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||||
|
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||||
|
** "sz" and 40 to 100 for "cnt".
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||||
@ -2993,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
|||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Set the Setlk Timeout
|
||||||
|
** METHOD: sqlite3
|
||||||
|
**
|
||||||
|
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||||
|
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||||
|
** eligible locks taken on wal mode databases by the specified database
|
||||||
|
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||||
|
** not support blocking locks, this function is a no-op.
|
||||||
|
**
|
||||||
|
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||||
|
** -1 to this function requests that the VFS blocks for a long time -
|
||||||
|
** indefinitely if possible. The results of passing any other negative value
|
||||||
|
** are undefined.
|
||||||
|
**
|
||||||
|
** Internally, each SQLite database handle store two timeout values - the
|
||||||
|
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||||
|
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||||
|
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||||
|
** values, this function sets only the setlk-timeout value. Therefore,
|
||||||
|
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||||
|
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||||
|
**
|
||||||
|
** Whenever the number of connections to a wal mode database falls from
|
||||||
|
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||||
|
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||||
|
** new connection that tries to read from the database fails with an
|
||||||
|
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||||
|
** passed to this API, the new connection blocks until the exclusive lock
|
||||||
|
** has been released.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||||
|
*/
|
||||||
|
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Convenience Routines For Running Queries
|
** CAPI3REF: Convenience Routines For Running Queries
|
||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
@ -5108,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
|||||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||||
** sqlite3_step(). Failure to reset the prepared statement using
|
** sqlite3_step(). Failure to reset the prepared statement using
|
||||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||||
** sqlite3_step() began
|
** sqlite3_step() began
|
||||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||||
@ -7004,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
|||||||
**
|
**
|
||||||
** ^The second argument is a pointer to the function to invoke when a
|
** ^The second argument is a pointer to the function to invoke when a
|
||||||
** row is updated, inserted or deleted in a rowid table.
|
** row is updated, inserted or deleted in a rowid table.
|
||||||
|
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||||
|
** with a NULL pointer as the second parameter.
|
||||||
** ^The first argument to the callback is a copy of the third argument
|
** ^The first argument to the callback is a copy of the third argument
|
||||||
** to sqlite3_update_hook().
|
** to sqlite3_update_hook().
|
||||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||||
@ -11486,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
|
|||||||
** is inserted while a session object is enabled, then later deleted while
|
** is inserted while a session object is enabled, then later deleted while
|
||||||
** the same session object is disabled, no INSERT record will appear in the
|
** the same session object is disabled, no INSERT record will appear in the
|
||||||
** changeset, even though the delete took place while the session was disabled.
|
** changeset, even though the delete took place while the session was disabled.
|
||||||
** Or, if one field of a row is updated while a session is disabled, and
|
** Or, if one field of a row is updated while a session is enabled, and
|
||||||
** another field of the same row is updated while the session is enabled, the
|
** then another field of the same row is updated while the session is disabled,
|
||||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
** the resulting changeset will contain an UPDATE change that updates both
|
||||||
|
** fields.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3session_changeset(
|
SQLITE_API int sqlite3session_changeset(
|
||||||
sqlite3_session *pSession, /* Session object */
|
sqlite3_session *pSession, /* Session object */
|
||||||
@ -11560,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
|||||||
** database zFrom the contents of the two compatible tables would be
|
** database zFrom the contents of the two compatible tables would be
|
||||||
** identical.
|
** identical.
|
||||||
**
|
**
|
||||||
** It an error if database zFrom does not exist or does not contain the
|
** Unless the call to this function is a no-op as described above, it is an
|
||||||
** required compatible table.
|
** error if database zFrom does not exist or does not contain the required
|
||||||
|
** compatible table.
|
||||||
**
|
**
|
||||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||||
@ -11696,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
|||||||
** The following flags may passed via the 4th parameter to
|
** The following flags may passed via the 4th parameter to
|
||||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||||
**
|
**
|
||||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||||
** Invert the changeset while iterating through it. This is equivalent to
|
** Invert the changeset while iterating through it. This is equivalent to
|
||||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||||
** It is an error to specify this flag with a patchset.
|
** It is an error to specify this flag with a patchset.
|
||||||
@ -12011,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
|
|||||||
void **ppOut /* OUT: Buffer containing output changeset */
|
void **ppOut /* OUT: Buffer containing output changeset */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
|
||||||
*/
|
|
||||||
SQLITE_API int sqlite3changeset_upgrade(
|
|
||||||
sqlite3 *db,
|
|
||||||
const char *zDb,
|
|
||||||
int nIn, const void *pIn, /* Input changeset */
|
|
||||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Changegroup Handle
|
** CAPI3REF: Changegroup Handle
|
||||||
**
|
**
|
||||||
|
4
deps/sqlite/sqlite3ext.h
vendored
4
deps/sqlite/sqlite3ext.h
vendored
@ -366,6 +366,8 @@ struct sqlite3_api_routines {
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
void *(*get_clientdata)(sqlite3*,const char*);
|
void *(*get_clientdata)(sqlite3*,const char*);
|
||||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
int (*setlk_timeout)(sqlite3*,int,int);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -699,6 +701,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- upload to Apple with dist-ios on macos
|
- upload to Apple with dist-ios on macos
|
||||||
- nix
|
- nix
|
||||||
- june and december: update release version
|
- june and december: update release version
|
||||||
- run `nix flake update`
|
- run `nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update`
|
||||||
- comment out the hash in default.nix
|
- comment out the hash in default.nix
|
||||||
- update the version
|
- update the version
|
||||||
- run `nix-build`
|
- run `nix-build`
|
||||||
|
6
flake.lock
generated
6
flake.lock
generated
@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1745279238,
|
"lastModified": 1748037224,
|
||||||
"narHash": "sha256-AQ7M9wTa/Pa/kK5pcGTgX/DGqMHyzsyINfN7ktsI7Fo=",
|
"narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9684b53175fc6c09581e94cc85f05ab77464c7e3",
|
"rev": "f09dede81861f3a83f7f06641ead34f02f37597f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 275 KiB |
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
Binary file not shown.
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 108 KiB |
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.unprompted.tildefriends"
|
package="com.unprompted.tildefriends"
|
||||||
android:versionCode="37"
|
android:versionCode="38"
|
||||||
android:versionName="0.0.31">
|
android:versionName="0.0.32-wip">
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.31</string>
|
<string>0.0.32</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>13</string>
|
<string>14</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
23
src/ssb.db.c
23
src/ssb.db.c
@ -277,6 +277,13 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, "
|
"CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, "
|
||||||
"old.content); END");
|
"old.content); END");
|
||||||
|
|
||||||
|
if (_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'trigger' AND name = 'messages_ai_refs' AND NOT sql LIKE '%ltrim%'"))
|
||||||
|
{
|
||||||
|
tf_printf("Deleting incorrect messages_refs...\n");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TABLE IF EXISTS messages_refs");
|
||||||
|
tf_printf("Done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
||||||
{
|
{
|
||||||
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
@ -291,8 +298,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO messages_refs(message, ref) "
|
"INSERT INTO messages_refs(message, ref) "
|
||||||
"SELECT messages.id, j.value FROM messages, json_tree(messages.content) AS j WHERE "
|
"SELECT messages.id, j.value FROM messages, json_tree(messages.content) AS j WHERE "
|
||||||
"j.value LIKE '&%.sha256' OR "
|
"j.value LIKE '&%.sha256' OR "
|
||||||
"j.value LIKE '%%%.sha256' OR "
|
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||||
"j.value LIKE '@%.ed25519' "
|
"j.value LIKE '@%.ed25519' OR "
|
||||||
|
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||||
"ON CONFLICT DO NOTHING");
|
"ON CONFLICT DO NOTHING");
|
||||||
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
tf_printf("Done.\n");
|
tf_printf("Done.\n");
|
||||||
@ -304,8 +312,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO messages_refs(message, ref) "
|
"INSERT INTO messages_refs(message, ref) "
|
||||||
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) AS j WHERE "
|
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) AS j WHERE "
|
||||||
"j.value LIKE '&%.sha256' OR "
|
"j.value LIKE '&%.sha256' OR "
|
||||||
"j.value LIKE '%%%.sha256' OR "
|
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||||
"j.value LIKE '@%.ed25519' "
|
"j.value LIKE '@%.ed25519' OR "
|
||||||
|
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||||
"ON CONFLICT DO NOTHING; END");
|
"ON CONFLICT DO NOTHING; END");
|
||||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
||||||
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END");
|
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END");
|
||||||
@ -541,7 +550,7 @@ static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
|
|||||||
|
|
||||||
if (sqlite3_prepare_v2(db,
|
if (sqlite3_prepare_v2(db,
|
||||||
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
||||||
"json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
"json.value LIKE '&%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||||
@ -1888,13 +1897,15 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
if (sqlite3_prepare_v2(db, "SELECT host, port, key FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare_v2(db, "SELECT host, port, key, last_attempt, last_success FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1));
|
result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1));
|
||||||
result[count] = (tf_ssb_db_stored_connection_t) {
|
result[count] = (tf_ssb_db_stored_connection_t) {
|
||||||
.port = sqlite3_column_int(statement, 1),
|
.port = sqlite3_column_int(statement, 1),
|
||||||
|
.last_attempt = sqlite3_column_int64(statement, 3),
|
||||||
|
.last_success = sqlite3_column_int64(statement, 4),
|
||||||
};
|
};
|
||||||
snprintf(result[count].address, sizeof(result[count].address), "%s", (const char*)sqlite3_column_text(statement, 0));
|
snprintf(result[count].address, sizeof(result[count].address), "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||||
snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2));
|
snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2));
|
||||||
|
@ -340,6 +340,10 @@ typedef struct _tf_ssb_db_stored_connection_t
|
|||||||
int port;
|
int port;
|
||||||
/** The identity. */
|
/** The identity. */
|
||||||
char pubkey[k_id_base64_len];
|
char pubkey[k_id_base64_len];
|
||||||
|
/** Time of last attempted connection. */
|
||||||
|
int64_t last_attempt;
|
||||||
|
/** Time of last successful connection. */
|
||||||
|
int64_t last_success;
|
||||||
} tf_ssb_db_stored_connection_t;
|
} tf_ssb_db_stored_connection_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -988,6 +988,8 @@ static void _tf_ssb_stored_connections_after_work(tf_ssb_t* ssb, int status, voi
|
|||||||
JS_SetPropertyStr(context, connection, "address", JS_NewString(context, work->connections[i].address));
|
JS_SetPropertyStr(context, connection, "address", JS_NewString(context, work->connections[i].address));
|
||||||
JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, work->connections[i].port));
|
JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, work->connections[i].port));
|
||||||
JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, work->connections[i].pubkey));
|
JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, work->connections[i].pubkey));
|
||||||
|
JS_SetPropertyStr(context, connection, "last_attempt", JS_NewInt64(context, work->connections[i].last_attempt));
|
||||||
|
JS_SetPropertyStr(context, connection, "last_success", JS_NewInt64(context, work->connections[i].last_success));
|
||||||
JS_SetPropertyUint32(context, result, i, connection);
|
JS_SetPropertyUint32(context, result, i, connection);
|
||||||
}
|
}
|
||||||
tf_free(work->connections);
|
tf_free(work->connections);
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#define VERSION_NUMBER "0.0.31"
|
#define VERSION_NUMBER "0.0.32-wip"
|
||||||
#define VERSION_NAME "This program kills fascists."
|
#define VERSION_NAME "This program kills fascists."
|
||||||
|
@ -124,7 +124,7 @@ try:
|
|||||||
select(driver, ['tf-navigation', 'shadow_root', '#close_error'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '#close_error'], ('click',))
|
||||||
select(driver, ['tf-navigation', 'shadow_root', '=edit'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '=edit'], ('click',))
|
||||||
select(driver, ['#editor', '.cm-content'], ('click',))
|
select(driver, ['#editor', '.cm-content'], ('click',))
|
||||||
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t"<div id=\'test-div\'>Hello, world!</div>"\n);'))
|
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t`<div id=\'test-div\' style=\'color: white; font-size: xx-large\'>\n\t\tHello, world!\n\t</div>`\n);'))
|
||||||
select(driver, ['#save'], ('click',))
|
select(driver, ['#save'], ('click',))
|
||||||
|
|
||||||
select(driver, ['#document', 'frame', '#test-div'])
|
select(driver, ['#document', 'frame', '#test-div'])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user