Merge branch 'dev' into parallel-fetching

dev
gravel 1 year ago
commit 0f5d03d23c
Signed by: gravel
GPG Key ID: C0538F3C906B308F

@ -35,12 +35,14 @@ Run at least once: `make fetch` to query servers. This can take around 5 minutes
Run when developing: `make dev` to watch for changes & serve HTML locally in browser.
Symlink the commit hook provided in [`etc/hooks`](etc/hooks/) to `.git/hooks/<hook>` to run a full test cycle when committing to main.
See [`Makefile`](Makefile) for more details.
### Running your own copy
- point your webserver at the [`output`](output/) folder
- install and enable systemd services from the [`systemd`](systemd/) folder or an equivalent timer
- install and enable systemd services from the [`etc/systemd`](etc/systemd/) folder or an equivalent timer
## Code style guidelines

@ -7,7 +7,7 @@ export const dom = {
tbl_communities: () => document.getElementById("tbl_communities"),
tbl_communities_content_rows:
() => Array.from(dom.tbl_communities()?.rows)?.filter(row => !row.querySelector('th')),
community_row: (communityID) => document.getElementById(communityID),
community_row: (communityID) => document.querySelector(`.room-row[${ATTRIBUTES.ROW.IDENTIFIER}="${communityID}"]`),
row_info: (row) => {
const dateCreated = new Date(row.getAttribute(ATTRIBUTES.ROW.DATE_CREATED) * 1000);
/** @type {string[]} */
@ -49,6 +49,8 @@ export const STAFF_ID_PASTE = "Copied staff ping to clipboard. Use it in the sel
export const IDENTIFIER_PASTE = "Copied internal room identifier. Use it to identify a room, such as when contributing language labels."
export const DETAILS_LINK_PASTE = "Copied link to Community details.";
export const communityQRCodeURL = (communityID) => `qr-codes/${communityID}.png`
export const COLUMN = {

@ -17,7 +17,7 @@
import {
dom, COLUMN, COLUMN_LITERAL, COMPARISON, ATTRIBUTES,
columnAscendingByDefault, columnIsSortable, COLUMN_TRANSFORMATION,
element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE
element, JOIN_URL_PASTE, communityQRCodeURL, STAFF_ID_PASTE, IDENTIFIER_PASTE, DETAILS_LINK_PASTE
} from './js/constants.js';
// Hidden communities for transparency.
@ -51,7 +51,7 @@ const preloadedImages = [];
/**
* Create an interactive version of the Community join link.
* @param {string} join_link
* @param {string} join_link
* @returns {HTMLElement}
*/
const transformJoinURL = (join_link) => {
@ -76,6 +76,32 @@ function getTimestamp() {
return timestamp;
}
/**
* Processes URL hash and parameter to trigger actions on the page.
*/
function reactToURLParameters() {
const hash = location.hash;
if (hash == "") return;
const communityID = hash.slice(1);
const row = dom.community_row(communityID);
if (row == null || !(row instanceof HTMLTableRowElement)) {
return;
}
// manual scrolling to prevent jumping after every modal open
row.scrollIntoView({
behavior: "smooth"
});
try {
displayQRModal(communityID);
} catch (e) {
console.error("Could not navigate to community " + communityID);
console.error(e);
}
}
/**
* Triggers all actions dependent on page load.
*/
@ -92,6 +118,7 @@ function onLoad() {
addQRModalHandlers();
addServerIconInteractions();
preloadImages();
reactToURLParameters();
}
/**
@ -111,7 +138,7 @@ const tagBody = ({text, type, description}) => element.span({
/**
* Shows the details modal hydrated with the given community's details.
* @param {string} communityID
* @param {string} communityID
* @param {number} pane Pane number to display in modal
*/
function displayQRModal(communityID, pane = 0) {
@ -160,6 +187,8 @@ function displayQRModal(communityID, pane = 0) {
document.getElementById('details-modal-panes').setAttribute('data-pane', pane);
location.hash=`#${communityID}`;
modal.showModal();
}
@ -254,6 +283,13 @@ function addQRModalHandlers() {
}
)
document.querySelector('#details-modal-copy-room-details-link')?.addEventListener(
'click',
function() {
copyToClipboard(location.href, DETAILS_LINK_PASTE);
}
)
for (const anchor of dom.qr_code_buttons()) {
// Disable QR code links
anchor.setAttribute("href", "#");
@ -310,7 +346,7 @@ function hideBadCommunities() {
for (const category of ['tests', 'offensive']) {
numberOfHiddenCommunities +=
filteredCommunities[category]
.map(hideElementByID)
.map(hideCommunity)
.reduce((a, b) => a + b);
}
@ -319,10 +355,10 @@ function hideBadCommunities() {
}
/**
* Removes an element by its ID and returns the number of elements removed.
* Removes a Community by its ID and returns the number of elements removed.
*/
function hideElementByID(id) {
const element = document.getElementById(id);
function hideCommunity(communityID) {
const element = dom.community_row(communityID);
element?.remove();
return element ? 1 : 0;
}

@ -55,7 +55,7 @@
"sogs.k9net.org" => "fdcb047eb78520e925fda512a45ae74c6e2de9e0df206b3c0471bf1509919559",
);
/**
/**
* @var string[] $ICON_ALLOWLIST
* These hostnames are considered to have safe room icons.
*/
@ -65,7 +65,7 @@
"sog.zcyph.cc"
];
/**
/**
* @var string[] $ICON_BLOCKLIST
* These hostnames or rooms are considered to have unsafe room icons.
*/

@ -47,7 +47,7 @@
$room_id = $room->get_room_identifier();
$icon_cached = room_icon_path($room_id);
$icon_resized = room_icon_path_resized($room_id, $size);
$icon_expired = file_exists($icon_cached) && filemtime($icon_cached) < strtotime("-1 hour");
$icon_expired = file_exists($icon_cached) && filemtime($icon_cached) < strtotime("-1 day");
// Re-fetch icons periodically.
if (!file_exists($icon_cached) || $icon_expired) {
@ -100,7 +100,7 @@
}
return 0;
}
file_exists($ROOM_ICONS_CACHE) or mkdir($ROOM_ICONS_CACHE, 0755, true);
file_exists($ROOM_ICONS) or mkdir($ROOM_ICONS, 0755, true);
?>

@ -17,7 +17,7 @@
return "$QR_CODES_RELATIVE/$room_id.png";
}
/**
* Fetch QR invite of the given room and return its relative path.
* @param \CommunityRoom $room
@ -26,7 +26,7 @@
function room_qr_code($room): string {
$room_id = $room->get_room_identifier();
$png_cached = room_qr_code_path($room_id);
$image_expired = file_exists($png_cached) &&
$image_expired = file_exists($png_cached) &&
filemtime($png_cached) < strtotime("-12 hour");
if (file_exists($png_cached) && !$image_expired) {
return room_qr_code_path_relative($room_id);

@ -599,7 +599,10 @@
* Return information for JSON serialization.
*/
function jsonSerialize(): array {
return get_object_vars($this);
$details = get_object_vars($this);
unset($details['room_hints']);
unset($details['merge_error']);
return $details;
}
/**

@ -8,7 +8,7 @@
}
/**
* Create new instance of this source from contents.
* Create new instance of this source from contents.
* Returns false if processing the source fails.
* @return \SDIRCommunitySource|false
*/
@ -30,8 +30,8 @@
private array $tags;
private static function sdir_validate_entry(
array $room_entry,
bool &$missing_url,
array $room_entry,
bool &$missing_url,
bool &$missing_tags
): bool {
if (!isset($room_entry['url']) || !is_string($room_entry['url'])) {
@ -89,7 +89,7 @@
$tags = $room_entry['tags'];
$room_id = url_get_room_id($url);
$this->tags[$room_id] = explode(',', $tags);
}
@ -171,7 +171,7 @@
}
if (str_starts_with($line, "hashtag")) {
$room_tags = ASGLCommunitySource::read_asgl_tags($line);
$room_tags = ASGLCommunitySource::read_asgl_tags($line);
}
}
@ -181,9 +181,9 @@
// Return first group matches.
return $matches[1];
}
public function get_tags(): array {
return $this->tags;
}
}
@ -235,9 +235,9 @@
log_info('Done fetching sources.');
$this->contents_aggregated =
$this->contents_asgl .
$this->contents_fark .
$this->contents_loki .
$this->contents_asgl .
$this->contents_fark .
$this->contents_loki .
// Slashes are escaped when served, unescape them
str_replace("\\/", "/", $this->contents_sdir);
}
@ -274,17 +274,17 @@
private function process_sources(): bool {
$source_sdir = SDIRCommunitySource::from_contents($this->contents_sdir);
$source_asgl = ASGLCommunitySource::from_contents($this->contents_asgl);
$source_sdir && $this->add_tags($source_sdir->get_tags());
$source_asgl && $this->add_tags($source_asgl->get_tags());
if (!$source_sdir) {
return false;
}
if (!$source_asgl) {
return false;
}

@ -16,7 +16,7 @@
) {
$this->text = $text;
$this->type = $tag_type;
$this->description =
$this->description =
empty($description) ? "Tag: $text" : $description;
}
@ -114,7 +114,7 @@
public static function from_details_array(array $details_array): array {
return CommunityTag::from_user_tags($details_array);
}
/**
* @param \CommunityTag[] $tags
* @return \CommunityTag[]
@ -155,7 +155,7 @@
];
private const SHOWCASED_TAGS = ["official", "new", "we're here"];
private const REDUNDANT_TAGS = ["session"];
public const NSFW_KEYWORDS = ["nsfw", "porn", "erotic", "18+"];

@ -8,7 +8,7 @@
</div>
<div id="details-modal-close-hint">
(Esc to close.)
</div>
</div>
<div id="details-modal-flow">
<div id="details-modal-panes" data-pane="0">
<div id="details-modal-start" class="details-modal-pane" data-pane="0">
@ -23,7 +23,7 @@
width="64"
height="64"
data-hydrate-with="icon:src;icon_safety:data-icon-safety"
/></div><h1><a
/></div><h1><a
id="details-modal-community-name"
data-hydrate-with="name;preview_link:href"
title="Open preview in new tab"
@ -54,7 +54,7 @@
</p>
<p id="details-modal-host">
Server:
<a
<a
title="Open server in new tab"
data-hydrate-with="hostname;hostname:href"
target="_blank"
@ -75,7 +75,7 @@
>
<div id="details-modal-qr-code-label">
Scan QR code in Session to join
<span
<span
id="details-modal-qr-code-label-name"
>'<span data-hydrate-with="name"></span>'</span>
</div>
@ -99,11 +99,20 @@
Copy mod ping
</button>
<button
id="details-modal-copy-room-id"
id="details-modal-copy-room-id"
class="themed-button"
data-hydrate-with="identifier:data-identifier"
title="Copy this room's identifier to uniquely identify this room to the sessioncommunities.online team"
>
Copy ID
</button>
<button
id="details-modal-copy-room-details-link"
class="themed-button"
data-hydrate-with="identifier:data-identifier"
title="Copy this room's identifier to uniquely identify this room to the sessioncommunities.online team"
>Copy Community ID</button>
title="Copy link to show details about this Community"
>
Share
</button>
<gap></gap>
<div id="details-modal-pane-selection" class="hidden">
<button

@ -66,12 +66,12 @@
$join_link = html_sanitize($room->get_join_url());
$pubkey = html_sanitize($pubkey);
$hostname = html_sanitize($hostname);
$staff_json = json_encode(array_map('html_sanitize', $room->get_staff()));
$tags_json = json_encode($room->get_room_tags());
?>
<tr id="<?=$id?>" class="room-row" itemscope itemtype="https://schema.org/EntryPoint"
<tr class="room-row" itemscope itemtype="https://schema.org/EntryPoint"
data-identifier="<?=$id?>"
data-pubkey="<?=$pubkey?>"
data-hostname="<?=$hostname?>"
@ -118,10 +118,10 @@
title="'<?=$name?>' has had <?=$users?> active users in the last <?=$users_cutoff?>."
><?=$users?></td>
<td class="td_preview" itemprop="url">
<a
href="<?=$preview_link?>"
title="Click here to preview '<?=$name?>'"
target="_blank"
<a
href="<?=$preview_link?>"
title="Click here to preview '<?=$name?>'"
target="_blank"
rel="noopener noreferrer nofollow"
>
<span class="protocol-indicator"></span>

@ -58,7 +58,7 @@
<header>
<div id="header-start"></div>
<div id="header-end">
<label
<label
for="toggle-theme-switch"
class="anchorstyle"
title="Switch color theme"

Loading…
Cancel
Save