1
0
Fork 1

Merge branch 'main' into table-improvements

pull/28/head
gravel 1 year ago
commit e3282494e0
Signed by: gravel
SSH Key Fingerprint: SHA256:p4HP49CCk4YQMkJpWJ09L8peEPQWjERtdCRAFxPfbOY

@ -1 +1 @@
Subproject commit b544b6680e834d4359294ed075393de61d3dce8d
Subproject commit f2aaed46ea2ef3a6ff03b6eee6f70c81cc20f069

@ -3,7 +3,11 @@
// change in the foreseeable future.
export const dom = {
/** @return {HTMLTableElement | null} */
tbl_communities: () => document.getElementById("tbl_communities"),
tbl_communities_content_rows:
() => Array.from(dom.tbl_communities()?.rows)?.filter(row => !row.querySelector('th')),
meta_timestamp: () => document.querySelector('meta[name=timestamp]'),
last_checked: () => document.getElementById("last_checked_value"),
qr_modal: (communityID) => document.getElementById(`modal_${communityID}`),
join_urls: () => document.getElementsByClassName("join_url_container"),
@ -56,7 +60,7 @@ export function columnIsSortable(column) {
const TRANSFORMATION = {
numeric: (el) => parseInt(el.innerText),
casefold: (el) => el.innerText.toLowerCase().trim(),
getPubkey: (el) => el.getAttribute("data-pubkey")
getSortKey: (el) => el.getAttribute("data-sort-by")
}
/**
@ -67,7 +71,7 @@ export const COLUMN_TRANSFORMATION = {
[COLUMN.IDENTIFIER]: TRANSFORMATION.casefold,
[COLUMN.NAME]: TRANSFORMATION.casefold,
[COLUMN.DESCRIPTION]: TRANSFORMATION.casefold,
[COLUMN.SERVER_ICON]: TRANSFORMATION.getPubkey
[COLUMN.SERVER_ICON]: TRANSFORMATION.getSortKey
}
/**

@ -22,32 +22,18 @@ import {
// Hidden communities for transparency.
const filteredCommunities = {
tests: [
"2e9345+c7fb", // TestRoom
"762ba9+c7fb", // TesterRoom
"b4d829+c7fb", // Test
"e5853a+c7fb", // testtest
"fishing+8e2e", // Example group from PySOGS documentation
"test+118d", // Testing 1, 2, 3
"test+13f6", // Testing room
"test+c01b", // Testing room
"test+13f6", // Testing room2
"test+fe93", // 测试Test)
"xyz+7908", // XYZ Room
"xyz+efca", // XYZ Room
],
offensive: [
"60fa60+c7fb", // "N-word" Community
"ab1a4d+c7fb", // zUnsensored Group (CSAM)
"AlexMed+e093", //
"AlexMed+e093", // drug trading?
"gore+e5e0", // gore
"RU-STEROID+e093" //
"RU-STEROID+e093" // drug trading?
],
// These communities should be checked regularly
// in case they update their PySOGS version
legacy: [
"Ukraine+02bd" // https://reccacon.com/view/room/Ukraine
]
};
// This can be achieved with `text-overflow: ellipsis` instead
@ -60,13 +46,27 @@ const transformJoinURL = (join_link) => {
});
}
function onLoad(timestamp) {
setLastChecked(timestamp);
function getTimestamp() {
const timestampRaw = dom.meta_timestamp()
?.getAttribute('content');
if (!timestampRaw) return null;
const timestamp = parseInt(timestampRaw);
if (Number.isNaN(timestamp)) return null;
return timestamp;
}
function onLoad() {
const timestamp = getTimestamp();
if (timestamp !== null) {
setLastChecked(timestamp);
}
hideBadCommunities();
// Sort by server to show off new feature & align colors.
sortTable(COLUMN.SERVER_ICON);
createJoinLinkButtons();
markSortableColumns();
addQRModalHandlers();
addServerIconInteractions();
}
function displayQRModal(communityID) {
@ -77,6 +77,24 @@ function hideQRModal(communityID) {
dom.qr_modal(communityID).style.display = "none";
}
function addQRModalHandlers() {
const rows = dom.tbl_communities_content_rows();
if (!rows) throw new Error("Rows not found");
for (const row of rows) {
const communityID = row.getAttribute('data-identifier');
row.querySelector('.td_qr_code').addEventListener(
'click',
() => displayQRModal(communityID)
);
const closeButton =
dom.qr_modal(communityID).querySelector('.qr-code-modal-close');
closeButton.addEventListener(
'click',
() => hideQRModal(communityID)
);
}
}
function createJoinLinkButtons() {
const join_URLs = dom.join_urls();
Array.from(join_URLs).forEach((td_url) => {
@ -89,7 +107,7 @@ function createJoinLinkButtons() {
function hideBadCommunities() {
let numberOfHiddenCommunities = 0;
for (const category of ['tests', 'offensive', 'legacy']) {
for (const category of ['tests', 'offensive']) {
numberOfHiddenCommunities +=
filteredCommunities[category]
.map(hideElementByID)
@ -138,6 +156,19 @@ function setLastChecked(last_checked) {
timestamp_element.innerText = `${time_passed_in_minutes} minutes ago`;
}
// TODO: Move info into dynamic modal.
function addServerIconInteractions() {
const rows = dom.tbl_communities_content_rows();
for (const row of rows) {
const hostname = row.getAttribute('data-hostname');
const publicKey = row.getAttribute('data-pubkey');
const serverIcon = row.querySelector('.td_server_icon');
serverIcon.addEventListener('click', () => {
alert(`Host: ${hostname}?>\n\nPublic key:\n${publicKey}?>`);
});
}
}
/**
* Function comparing two elements.
*
@ -253,9 +284,13 @@ function markSortableColumns() {
const table = dom.tbl_communities();
const header_cells = table.querySelectorAll('th');
for (let colno = 0; colno < header_cells.length; colno++) {
if (!columnIsSortable(colno)) continue;
if (!columnIsSortable(colno)) continue;
header_cells[colno].classList.add('sortable');
}
header_cells[colno].addEventListener(
'click',
() => sortTable(colno)
)
};
}
/**
@ -279,13 +314,7 @@ function sortTable(column) {
setSortState(table, { ascending, column });
}
// html.js for styling purposes
window.document.documentElement.classList.add("js");
// Crude way to export from module script due to inline event handlers.
// Ideally, all handlers would be attached from JS via addEventListener.
Object.assign(window, {
onLoad, sortTable, displayQRModal,
hideQRModal, copyToClipboard
});
// `html.js` selector for styling purposes
document.documentElement.classList.add("js");
document.addEventListener('DOMContentLoaded', () => onLoad());

@ -1,8 +1,4 @@
:root {
/*--session-classic-dark-green: #31f196;*/
/*--session-classic-dark-gray-one: #414141;*/
/*--session-classic-dark-gray-two: #2d2d2d;*/
/*--session-classic-dark-gray-three: #1b1b1b;*/
--alternate-row-color: #e8e8e8;
}
@ -92,8 +88,8 @@ header {
font-size: 1em;
}
#toggle-show-room-ids:not(:checked)
~ #tbl_communities :is(#th_identifier, .td_identifier) {
#toggle-show-room-ids:not(:checked)
~ #tbl_communities :is(#th_identifier, .td_identifier) {
display: none;
}
@ -142,7 +138,6 @@ header {
-webkit-line-clamp: 3;
}
.td_description::after {
/* Cover overflowing description lines with internal border. */
position: absolute;
@ -154,7 +149,7 @@ header {
background-color: var(--row-color);
}
.td_users {
.td_users {
text-align: center;
/* font-weight: bold; */
}
@ -185,7 +180,7 @@ a[href^="https:"] .protocol-indicator::after {
content: "HTTPS";
}
.td_qr_code {
.td_qr_code {
padding: var(--cell-padding-small) !important;
}
@ -237,7 +232,7 @@ a[href^="https:"] .protocol-indicator::after {
display: none;
}
#th_preview, .td_preview {
#th_preview, .td_preview {
display: none;
}
}

@ -1,3 +1,11 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/svg+xml" href="favicon.svg" sizes="any">
<link rel="icon" type="image/svg+xml" href="favicon.svg" sizes="any">
<meta name="keywords" content="Session, Community, Anonymous, Chat">
<meta
http-equiv="Content-Security-Policy"
content="
script-src 'self'; img-src 'self' data:; connect-src 'self'; font-src 'none';
object-src 'none'; media-src 'none'; form-action 'none'; base-uri 'self';
"
>

@ -34,7 +34,7 @@
<?php foreach ($rooms as $id => $room): ?>
<div id="modal_<?=$id?>" class="qr-code-modal">
<div class="qr-code-modal-content">
<span class="qr-code-modal-close" onclick='hideQRModal("<?=$id?>")'>
<span class="qr-code-modal-close">
&times;
</span>
<img

@ -6,14 +6,15 @@
// Join URL contents are not guaranteed to have visible text.
return $id != "qr" && $id != "preview" && $id != "join_url";
}
function sort_onclick($colno) {
global $TABLE_COLUMNS;
$column = $TABLE_COLUMNS[$colno];
if (!column_sortable($column['id'])) return "";
return " onclick='sortTable($colno)' title='Click to sort this column'";
$name = $column['name'];
return " title='Click to sort by $name'";
}
$TABLE_COLUMNS = [
['id' => "identifier", 'name' => "Identifier"],
['id' => "language", 'name' => "L"],
@ -51,48 +52,62 @@
$hostname = explode("//", $room->join_link)[1];
$hostname = explode("/", $hostname)[0];
// Escape external input.
// Ternaries prevent passing null-equal strings, which produce warnings.
$id = htmlspecialchars($id);
$language = $room->language ? htmlspecialchars($room->language) : "";
$name = htmlspecialchars($room->name);
$desc = $room->description ? htmlspecialchars($room->description) : "";
$users = htmlspecialchars($room->active_users);
$preview_link = htmlspecialchars($room->preview_link);
$join_link = htmlspecialchars($room->join_link);
// TODO: Do not forget to rename this escape when merging!
$token = htmlspecialchars($token);
$hostname = htmlspecialchars($hostname);
?>
<tr id="<?=$id?>">
<td class="td_identifier"><?=$id?></td>
<td class="td_language"><?=$room->language?></td>
<td class="td_name" title="'<?=$room->name?>' preview">
<tr id="<?=$id?>" itemscope itemtype="https://schema.org/EntryPoint"
data-identifier="<?=$id?>"
data-pubkey="<?=$pubkey?>"
data-hostname="<?=$hostname?>"
>
<td class="td_identifier" itemprop="identifier"><?=$id?></td>
<td class="td_language"><?=$language?></td>
<td class="td_name" title="'<?=$room->name?>' preview" itemprop="name">
<a href="<?=$room->preview_link?>" target="_blank" rel="noopener noreferrer">
<?=$room->name?>
</a>
</td>
<td class="td_description"
><?=$room->description?></td>
<td class="td_users"><?=$room->active_users?></td>
<td class="td_preview">
<a href="<?=$room->preview_link?>" target="_blank" rel="noopener noreferrer">
<td class="td_description" itemprop="description"><?=$desc?></td>
<td class="td_users"><?=$users?></td>
<td class="td_preview" itemprop="url">
<a href="<?=$preview_link?>" target="_blank" rel="noopener noreferrer nofollow">
<span class="protocol-indicator"></span>
</a>
</td>
<td class="td_qr_code">
<img
class="qr-code-icon"
<img
class="qr-code-icon"
src="qrcode-solid.svg"
onclick='displayQRModal("<?=$id?>")'
alt="Pictogram of a QR code"
>
</td>
<td class="td_server_icon"
data-pubkey="<?=$pubkey?>"
title="<?=$hostname?> (<?=$pubkey?>)"
<?php /* Mobile UX. This is not ideal. Move info once implemented. */ ?>
onclick="alert('Host: <?=$hostname?>\n\nPublic key:\n<?=$pubkey?>')"
<td class="td_server_icon"
data-sort-by="<?=$pubkey?>"
title="<?=$hostname?> (<?=$token?>)"
item="image"
>
<div class="td_server_icon-circle" style="background-color: <?=$icon_color?>">
<span><?=strtoupper($pubkey[0] . $pubkey[1])?></span>
</div>
</td>
<td class="td_join_url">
<div class="join_url_container" data-url="<?=$room->join_link?>">
<a class="join_url show-from-w5" title="<?=$room->join_link?>"
><?=$room->join_link?></a>
<a class="noscript" href="<?=$room->join_link?>"
<div class="join_url_container" data-url="<?=$join_link?>">
<a class="join_url show-from-w5" title="<?=$join_link?>"
><?=truncate($join_link, 32)?></a>
<a class="noscript" href="<?=$join_link?>" rel="external nofollow"
>Copy link</a>
</div>
</td>

@ -0,0 +1,12 @@
Mobiililaitteella:
- Napsauta Kopioi-nappia kopioidaksesi ryhmän URL-osoitteen.
- Avaa Session, paina plussan painiketta ja valitse "Liity ryhmään" ("Join Community").
- Liitä kopioitu URL-osoite "Syötä yhteisön URL-osoite"-kenttään, ja paina"Liity".
Mobiililaitteella, kun tämä sivusto on auki tietokoneellasi:
- Paina valitsemasi yhteisön QR-koodipainiketta tietokoneellasi.
- Avaa Session, paina plussan painiketta ja valitse "Liity ryhmään" ("Join Community").
- Valitse ylhäältä "Skannaa QR-koodi" ja skannaa puhelimen kameralla ryhmän QR-koodiin.

@ -0,0 +1,12 @@
На мобильном девайсе:
- Нажмите кнопку «Копировать», чтобы скопировать URL-адрес нужной группы.
- Откройте Session, нажмите на кнопку с плюсом и выберите «Присоединиться к сообществу».
- Вставьте скопированный URL-адрес в поле «Введите URL-адрес сообщества», и нажмите «Присоединиться».
На мобильном девайсе, когда этот сайт открыт на вашем компьютере:
- Нажмите кнопку QR-кода для выбранного сообщества на вашем компьютере.
- Откройте Session, нажмите на кнопку с плюсом и выберите «Присоединиться к сообществу».
- Выберите «Сканировать QR-код» наверху и наведите камеру телефона на QR-код.

@ -13,17 +13,35 @@
<head>
<?php include "+components/page-head.php" ?>
<link rel="canonical" href="https://sessioncommunities.online/">
<link rel="stylesheet" href="styles2.css">
<script type="module" src="main.js"></script>
<script type="module" src="./main.js"></script>
<title>Self-updating list of active Session communities</title>
<meta name="description" content="
Directory of Session Open Groups — public chatrooms within Session Messenger.
Copy these Communities into the Session app
and talk anonymously about Privacy, Security, or Cryptocurrency.
">
<meta name="modified" content="<?=date("Y-m-d H:i:s", $timestamp)?>">
<meta property="og:title" content="Click here for Session Communities">
<meta
property="og:description"
content="<?=count($rooms_assoc)?> Communities and counting — updated every day!"
>
<meta property="og:url" content="https://sessioncommunities.online/">
<meta property="og:type" content="website">
<meta property="og:locale" content="en_US"/>
<meta name="timestamp" content="<?=$timestamp?>">
</head>
<body onload="onLoad(<?php echo $timestamp ?>)">
<body>
<header>
<div id="header-start"></div>
<div id="header-end">
<a
id="link-instructions"
target="_blank"
rel="help"
title="Mutli-language guide to joining communities using the site."
href="instructions.html"
>Instructions</a>
</div>
@ -50,10 +68,16 @@
</p>
<p id="disclaimer">
This site is not affiliated with
<a href="https://optf.ngo">Oxen Privacy Tech Foundation</a>.
<a
href="https://optf.ngo"
target="_blank"
>Oxen Privacy Tech Foundation</a>.
<br>
Communities shown are fetched automatically from
various sources.
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online#which-sources-are-crawled"
target="_blank"
>various sources</a>.
<br>
We make an attempt to hide communities containing
objectionable or illegal content, but
@ -65,8 +89,13 @@
only available with JS enabled.
</p>
<p>
<label for="toggle-show-room-ids" class="clickable anchorstyle">
Toggle room identifier display
<label
for="toggle-show-room-ids"
class="clickable anchorstyle"
tabindex="0"
title="Shows a column with community identifiers
for developers or language data contributors.">
Toggle room identifier display
</label>
</p>
<nav>
@ -83,17 +112,30 @@
<a
href="https://github.com/oxen-io/session-pysogs"
target="_blank"
title="Information about running a community server"
title="Information about running a community server."
>Host Your Own Community</a>
<a
href="https://getsession.org/terms-of-service"
target="_blank"
>Session Terms Of Service</a>
</nav>
<nav>
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online"
target="_blank"
title="sessioncommunities.online repository on GitHub."
>Source Code & Contact</a>
>Source Code</a>
<a
href="https://lokilocker.com/someguy/sessioncommunities.online"
target="_blank"
title="sessioncommunities.online repository on Lokinet Gitea."
>Source Code (Mirror)</a>
<a
href="https://github.com/mdPlusPlus/sessioncommunities.online#contact"
target="_blank"
rel="author"
title="Information on how to contact the maintainer of sessioncommunities.online"
>Contact</a>
</nav>
</footer>
<div id="copy-snackbar">

@ -1,46 +1,66 @@
<?php
include_once "+getenv.php";
$instruction_files = glob("+instructions/*.txt");
function file_language($file) { return pathinfo($file)['filename']; }
?>
<!DOCTYPE html>
<html>
<head>
<?php include "+components/page-head.php" ?>
<link rel="canonical" href="https://sessioncommunities.online/instructions.php">
<link rel="stylesheet" href="css/instructions.css">
<style type="text/css">
<?php foreach ($instruction_files as $i => $file): ?>
#language-selection-<?=$i?>:checked ~
#language-selection-<?=$i?>:checked ~
#instructions #instructions-<?=$i?> {
display: block;
}
<?php endforeach; ?>
</style>
<meta name="description" content="
Discover how to use the sessioncommunities.online website
to help you join Session Open Groups; available in <?php
// Print languages instructions are available in.
$languages = array_map('file_language', array_slice($instruction_files, 0, 10));
echo(join(", ", $languages));
?>. In short: Use the Copy button or scan the QR code!
">
<meta property="og:title" content="How to join Session Communities">
<meta property="og:description" content="Learn how to use sessioncommunities.online to join">
<meta property="og:url" content="https://sessioncommunities.online/instructions.php">
<meta property="og:type" content="article">
</head>
<body>
<header>
<h1>Instructions for joining Session Communities</h1>
</header>
<main>
<p>
<a href="/" title="Return to the Session Community listing">
Go back to Community list
</a>
</p>
Choose your language:
<?php foreach ($instruction_files as $i => $file): ?>
<br>
<input
<input
id="language-selection-<?=$i?>"
class="language-selection"
name="language"
type="radio"
<?=file_language($file) == 'English' ? 'checked="checked"' : ''?>
>
<label for="language-selection-<?=$i?>">
<?=
// Name of the language
// Can be later parsed from i.e. first line of file
pathinfo($file)['filename']
file_language($file);
?>
</label>
<?php endforeach; ?>
<article id="instructions">
<?php foreach ($instruction_files as $i => $file): ?>
<section id="instructions-<?=$i?>" class="instructions"><?php