Skip to content

Commit

Permalink
Merge pull request #1 from hyphacoop/initial
Browse files Browse the repository at this point in the history
Initial Implementation
  • Loading branch information
akhileshthite committed May 10, 2024
2 parents 57d812b + b99d3bc commit 21e6e8d
Show file tree
Hide file tree
Showing 47 changed files with 5,489 additions and 45 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
# TODO: Uncomment once released
# branches: [ main ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Publish to Distributed Press
uses: hyphacoop/actions-distributed-press@v1.1.0
with:
publish_dir: ./
dp_url: https://api.distributed.press
refresh_token: ${{ secrets.DISTRIBUTED_PRESS_TOKEN }}
site_url: reader.distributed.press
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# reader.distributed.press
Read and follow federated microblogs.
# Social Reader
A P2P and offline-first ActivityPub client for reading and following microblogs on the Fediverse, avoiding dependency on always-online HTTP servers, allowing access to content anytime, anywhere.

For more information, please visit [docs.distributed.press](https://docs.distributed.press/social-reader).
36 changes: 36 additions & 0 deletions about.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.about-container {
flex: 1;
max-width: 600px;
width: 100%;
margin: 0 20px;
margin-top: 10px;
}

/* Apply general styles to all section elements within about-container */
.about-container > section {
text-align: left;
color: var(--rdp-text-color);
width: 100%;
margin-bottom: 2rem;
}

.about-info a,
.faq-section a {
color: var(--rdp-link-color);
text-decoration: underline;
}

.about-info a:hover,
.faq-section a:hover {
text-decoration: none;
}

.faq-section details {
margin-bottom: 1rem;
border-bottom: 1px solid var(--rdp-border-color);
padding-bottom: 1rem;
}

.faq-section summary {
font-weight: bold;
}
102 changes: 102 additions & 0 deletions about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>About Reader</title>
<style>
@import url("./about.css");
@import url("./common.css");
@import url("./theme.css");
@import url("./index.css");
@import url("./timeline.css");
@import url("./post.css");
</style>
<div class="container">
<sidebar-nav></sidebar-nav>
<section class="about-container main-content">
<section class="about-info">
<p>
Social Reader is a P2P and offline ActivityPub client for reading and
following microblogs on the
<a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a>.
</p>
<p>
Unlike traditional platforms, Social Reader does not index data on a
server. It empowers you to load public ActivityPub data directly,
turning your device into a personal indexer. This means
<b>your content, your control</b>.
</p>
<p>
Social Reader natively supports content loading over P2P protocols such
as
<code><a href="https://ipfs.tech/">ipfs://</a></code> and
<code><a href="https://holepunch.to/">hyper://</a></code
>. This innovation bypasses the need for always-online HTTP servers,
allowing you to access content anytime, anywhere—even offline.
</p>
<p>
Social Reader is built on principles of low-tech; minimal dependencies,
vanilla JavaScript, unminified scripts, and IndexedDB for local data
storage. View and contribute to our open-source code on
<a href="https://github.com/hyphacoop/reader.distributed.press"
>GitHub</a
>.
</p>
</section>
<!-- FAQ Section -->
<section class="faq-section">
<h2>FAQs</h2>
<details open>
<summary>How do I create an account on Social Reader?</summary>
<p>
Social Reader is designed as a reading and following client, which
means you cannot create an account directly within the app. To
actively write and contribute to the Fediverse, you would need to
interact with the
<a href="https://hypha.coop/dripline/announcing-dp-social-inbox/"
>Social Inbox</a
>
API. This can be done through platforms like
<a href="https://sutty.nl/">Sutty CMS</a> or by forking and hosting
your own instance of
<a href="https://github.com/RangerMauve/staticpub.mauve.moe"
>Staticpub</a
>
repository.
</p>
</details>
<details>
<summary>
Why is Social Reader different from mainstream social platforms?
</summary>
<p>
Social Reader eliminates the middleman, ensuring direct communication
with your audience without the interference of third-party algorithms.
This ad-free experience prioritizes user autonomy and engagement,
making it ideal for community leaders and organizations seeking
genuine reach and engagement. Unlike traditional social networks where
follower engagement often requires payment, Social Reader and the
broader Fediverse allow for genuine reach and engagement.
</p>
</details>
<details>
<summary>I found a bug. Where do I report it?</summary>
<p>
If you encounter any issues or have feedback, please file a report on
our
<a
href="https://github.com/hyphacoop/reader.distributed.press/issues/new"
>GitHub issues</a
>
page. We appreciate your input as it helps us improve Social Reader
for everyone.
</p>
</details>
</section>
</section>
<div class="right-column">
<!-- This is an empty column to balance the layout -->
</div>
</div>
<script type="module" src="./sidebar.js"></script>
<script type="module" src="followed-accounts.js"></script>
<script type="module" src="./theme-selector.js"></script>
32 changes: 32 additions & 0 deletions actor-mini-profile.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.mini-profile {
display: flex;
align-items: center;
text-align: left;
cursor: pointer;
background: none;
border: none;
padding: 0;
margin-bottom: 4px;
color: inherit;
font: inherit;
}

.profile-mini-icon {
width: 28px;
height: 28px;
border-radius: 50%;
background-color: #000000;
margin-right: 6px;
}

.profile-mini-name {
color: var(--rdp-text-color);
}

.profile-followed-date {
text-align: center;
font-size: 0.875rem;
color: var(--rdp-details-color);
margin-left: 34px;
margin-bottom: 6px;
}
80 changes: 80 additions & 0 deletions actor-mini-profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { db } from './dbInstance.js'

class ActorMiniProfile extends HTMLElement {
static get observedAttributes () {
return ['url']
}

constructor () {
super()
this.url = ''
}

connectedCallback () {
this.url = this.getAttribute('url')
this.fetchAndRenderActorInfo(this.url)
}

attributeChangedCallback (name, oldValue, newValue) {
if (name === 'url' && newValue !== oldValue) {
this.url = newValue
this.fetchAndRenderActorInfo(this.url)
}
}

async fetchAndRenderActorInfo (url) {
try {
const actorInfo = await db.getActor(url)
if (actorInfo) {
this.renderActorInfo(actorInfo)
}
} catch (error) {
console.error('Error fetching actor info:', error)
}
}

renderActorInfo (actorInfo) {
// Clear existing content
this.innerHTML = ''

// Container for the icon and name, which should be a button for clickable actions
const clickableContainer = document.createElement('button')
clickableContainer.className = 'mini-profile'
clickableContainer.setAttribute('type', 'button')

let iconUrl = './assets/profile.png'
if (actorInfo.icon) {
iconUrl = actorInfo.icon.url || (Array.isArray(actorInfo.icon) ? actorInfo.icon[0].url : iconUrl)
}

// Actor icon
const img = document.createElement('img')
img.className = 'profile-mini-icon'
img.src = iconUrl
img.alt = actorInfo.name ? actorInfo.name : 'Actor icon'
clickableContainer.appendChild(img)

// Actor name
if (actorInfo.name) {
const pName = document.createElement('div')
pName.classList.add('profile-mini-name')
pName.textContent = actorInfo.name
clickableContainer.appendChild(pName)
}

// Append the clickable container
this.appendChild(clickableContainer)

// Add click event to the clickable container for navigation
clickableContainer.addEventListener('click', () => {
window.location.href = `/profile.html?actor=${encodeURIComponent(this.url)}`
})

const pDate = document.createElement('span')
pDate.classList.add('profile-followed-date')
pDate.textContent = ` - Followed At: ${this.getAttribute('followed-at')}`
this.appendChild(pDate)
}
}

customElements.define('actor-mini-profile', ActorMiniProfile)
101 changes: 101 additions & 0 deletions actor-profile.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
.profile {
margin-top: 20px;
}

.profile-container {
text-align: center;
}

.distributed-post-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}

.profile-icon {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #000000;
margin-right: 8px;
margin-bottom: 8px;
}

.profile-details {
display: flex;
flex-direction: column;
}

.profile-name {
color: var(--rdp-text-color);
font-weight: bold;
}

.profile-username {
color: var(--rdp-text-color);
margin-top: 1px;
}

.profile-summary {
color: var(--rdp-details-color);
width: 500px;
margin-left: auto;
margin-right: auto;
margin-top: 8px;
margin-bottom: 10px;
overflow-wrap: break-word;
}

follow-button {
appearance: none;
border: 1px solid var(--rdp-border-color);
border-radius: 4px;
box-shadow: rgba(27, 31, 35, 0.1) 0 1px 0;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-family: inherit;
font-size: inherit;
font-weight: 600;
line-height: 20px;
padding: 4px 16px;
position: relative;
text-align: center;
text-decoration: none;
touch-action: manipulation;
vertical-align: middle;
white-space: nowrap;
}

follow-button[state="follow"],
follow-button[state="unfollow"] {
color: #fff;
}

follow-button[state="follow"] {
background-color: #3b82f6;
}
follow-button[state="follow"]:hover {
background-color: #2563eb;
}

follow-button[state="unfollow"] {
background-color: #ef4444;
}
follow-button[state="unfollow"]:hover {
background-color: #dc2626;
}

.actor-profile {
flex: 1;
max-width: 600px;
width: 100%;
margin: 0 20px;
}

@media (max-width: 768px) {
.profile-summary {
width: 100%;
}
}
Loading

0 comments on commit 21e6e8d

Please sign in to comment.