This introduces a basic implementation to not display the content of
messages. It still shows that a message exists but shows localized
copy indicating why the content was hidden.
We should consider additional filters or queries in different views to actually
remove these posts but I'll leave that as up for discussion since I can see
value in showing that a blocked comment exists in a conversation.
Problem: The latest release seems to have some index problems, which is
frustrating, but might be able to be circumvented with a database
rebuild. The rebuild was something that Ahau funded and I'm excited to
be able to finally expose this to users without asking them to `rm -rf`
random files in their `~/.ssb` directory.
Solution: Replaced unused 'progress' bars (always at 100%) with rebuild
button.
Use `test` instead of `check`.
This was changed in common-good 3.0.
See the [commit](3dc492ea7d)
Also added a pretty aggressive ignore-list for dev-dependencies for depcheck.
Problem: There were lots of CSS changes in the preview + blob PR and I
didn't take enough time to look at them. It looks like a few of them
made style problems (e.g. extra specing in the 'next' / 'previous' links
on the profile pages) and it made it harder (IMO) to determine that
links are links (since we've been using font weight to distinguish).
Solution: Revert those changes back to the previous defaults. If the
margin glitch was on purpose or anyone feels strongly about
distinguishing links using some other style (color?) then I'm happy to
merge those, but I'd like to fix the extra margin and have *some* way to
distinguish links in the mean time.
Problem: With the new ssb-db@20 we have to explicitly add ssb-private1
to ensure that we can see private messages.
Solution: Add ssb-private1 as a plugin.
Problem: Our tests seem to have been disabled in various places, which
means that type errors, typos, and other small problems were introduced.
Solution: Remove the comments and fix the underlying problems without
disabling the linters.
Problem: I've been worried about merging these version bump pull
requests since we don't have many tests, and I *really* don't want to
merge any catostrophic breaking changes anywhere.
Solution: Add some tests that are more comprehensive, starting by
creating a temporary SSB database and keypair and then editing the
profile, previewing a message, publishing a message, etc., until we have
a handful of small functions actually being tested. This won't ensure
that everything works forever, and it *really* doesn't test replication,
but it should help increase our confidence that test success means that
fewer things are broken.
Problem: I keep forgetting to run tests and my commits aren't actually
passing in CI. I'd really like for these to be run automatically so that
I don't have to remember my pre-commit hooks.
Solution: Revert commit d9e829e2e8, which
Nick Wynja added to remove Husky as a pre-commit hook. I hope that
anyone who doesn't want to run the pre-commit hook will run Git with the
`--no-verify` option, and anyone bummed at this change will open a
conversation to discuss in more details (or just revert *this* commit).
See-also: https://github.com/fraction/oasis/issues/346
Problem: I thought it would be a good idea to keep our SSB stack in
another module but it just creates friction and adds another layer to
our stack that has to be independently upgraded and versioned.
Solution: Merge the @fraction/flotilla source code into Oasis so that we
don't have to maintain two separate projects.
Problem: 5 is not a lot and I'd rather have the builds all done and
ready when I'm ready to merge them instead of having to do them in
chunks of 5.
Solution: Allow 16 open PRs at a time. We can change as needed.
Problem: You can't publish email addresses or user@host combos or
anything like that because the regex is super liberal in what it
matches.
Solution: Ensure that the first word in a message is a mention *or*
ensure that there's whitespace before a mention. This also adds a
regex101 link (in lieu of proper testing, for now) which makes this
easier to test and track over time.
See-also: https://github.com/fraction/oasis/issues/466
Problem: Recently there was a PR [0] merged with a quickfix to avoid
some test failures, which is something I've been trying to avoid. While
`process.exit()` works fine, I'm worried that it means we don't
understand what's happening under the hood, *plus* I have the [maybe
unjustified?] worry that it might kill the process during a database
write or something dangerous. It looks like this particular test hang
was caused by both a stream and some number of intervals that were left
open.
Solution: Provide a way to close the stream and intervals in `index.js`
and ensure that we do that before closing the server.
[0]: https://github.com/fraction/oasis/pull/462
after having restored the package-lock.json to that of the master branch, this
PR now uses common-good@2.0.3, which has common-good check as a command.
i ran it manually as $(npm bin)/common-good check, and fixed whatever
it complained about.
the purpose is to only add the two new dependencies this PR introduces, piexifjs
and file-types (and their transitive dependencies, ofc), without affecting any
other dependencies, as compared to oasis's current master branch
we had duplicates and _old_ names in the first attempt,
with this new strucutre only one name per feed is kept (the most recent)
also: tweak regexp some more
should not correctly match end of string and NOT match inside md link
https://regex101.com/r/Uu6iTj/3/
replaces immediatly if there is one match,
but just spews multiple ones.
* add silly in-memory about:name cache
rational is also commented the code. the tl:dr; is oasis already doesnt
use ssb-about or other indexing plugins.
butt for @mentions we need a quick way to lookup names to feeds,
otherwise we block the preview flow with long query times.
This allows blobs to load when the blob reference in the markdown has
`?contentType=image/jpeg` or similar hints attached.
It does so by simply removing the GET parameters from the URL though.
It would be a bit nicer to actually use those hints to set the right
HTTP response headers, but somehow koa keeps ignoring / overwriting (?)
the manually-set `Content-Type` headers, so this has to do for now.
If a message had no obvious text because it didn't have a content type
of post, use the mystery description as to not break the `postSnippet()`
function and give a valid snippet in the title.
Problem: The Docker example was using Alpine with the Tini dependency
and some neat custom stuff, but it wasn't super clear to understand
everything that was going on. It was also slow because lots of stuff was
compiling from source.
Solution: Replace neat custom stuff with boring defaults for clarity and
much faster builds.
Problem: The auto-stage code was a horrible hack that may be causeing an
OOM bug if too many peers are staged too quickly.
Solution: Since SSB-CONN now supports a variable number of connection
hops, we can remove the auto-stage hack and use the default scheduler
instead of trying to staple on our own hacky scheduler fix.
Problem: The words "comment" and "reply" are super ambiguous, but
renaming them is hard.
Solution: I think "subtopic" captures the sentiment well, and makes it
clear that you're leaving the current topic to start a new topic (rather
than just replying to an individual message).
Problem: The indented summary took up lots of visual attention which
isn't what we wanted. Also a bad merge broke the previous `<summary>`/
Solution: Fix the bad merge and remove the dashes style from the
indented summary elements.
Problem: The "metric CSS" was kind of clever but mostly just confusing.
Solution: Use `--size-N` for the size, where the size is `2^n`. Double
size is `--size-1`, half size is `--size--1` (negative one). The
negatives kind of suck but I couldn't think of a better solution that
didn't give up the flexibility, brevity, or the ability to sort
alphabetically.
Fixes: https://github.com/fraction/oasis/issues/100
Problem: the `<summary>` element had a broken bottom margin on content
warnings, and isn't obvious that it's not just text.
Solution: Fix the margin and used a dashed border to indicate that the
content can be expanded. I think this is more intuitive than no border,
and I think the dashed line draws parallels with "cut outs" that might
make expansion intuitive (?), but either way I think it's better than
the previous design.
Problem: The sync goes forever, which isn't good if we're in an edge
case (like having peers who are offline with us).
Solution: Add a 5-minute timeout and refactor to use a design that's
maybe simpler? This is just my stab at the previous behavior, but there
are definitely improvements that could be made!
Rework how I'm doing progress checking and be very verbose about it
until I understand how this all works. Also refactors connected peer
function into shared model for use on settings page.
Problem: If you call `ensureConnection()` two times really fast, it'll
spin off two separate connection attempts, each with their own socket,
which keeps the tests from closing.
Solution: If there's a pending connection, keep track of it, and only
attempt a new connection if there are no pending connections.
Problem: The FR translation is added in `i18n.js` but it's not available
in the drop-down language selection menu.
Solution: Add it to the menu and ensure that the menu is sorted
alphabetically by the name of the language.
Problem: I think that if `open()` is called after `close()`, it keeps a
server open. This keeps the tests running forever.
Solution: Just before `open()` resolves, check `closing` and [if
necessary] close the server that we just started by re-calling
`close()`.
Problem: When we use `ensureConnection()`, it doesn't have any handling
for when the database is being closed, and therefore can keep the
database up and running forever (which fails the tests).
Solution: Use `cooler.open()`, which has some database close handling
built in, and doesn't keep the tests open forever (causing them to time
out and fail).
Problem: Unfortunately `setImmediate()` doesn't actually ensure that
`server !== null`, so we can get fun race condition errors that have
been reported on Windows.
Solution: Replace `setImmediate()` hack with event emitter that actually
ensures that the server is listening (and hopefully isn't `null)`.
Fixes: https://github.com/fraction/oasis/issues/401
Problem: When proxying Oasis, the CSRF + DNS rebind security precautions
will respond with HTTP 400 if you use a hostname that Oasis doesn't
know about. For example, if Oasis is listening on `localhost` and you
use Caddy/Nginx/etc to proxy that to `oasis.example.com`, then Oasis
will see GET requests as DNS rebind attacks and POST requests as CSRF
attacks.
Solution: Add `--allow-host` command-line flag so that you can
`--allow-host oasis.example.com` and ensure that the host is allowed by
the security measures.
Problem: During a refactor the SSB connection management was changed to
be more conservative, so it only ensures that we have a connection once
the server is started. This isn't good, because it means `oasis
--no-open` no longer starts an SSB service in the background.
Solution: Run `ensureConnection()` to ensure that we have a connection
to the SSB service regardless of regardless of whether we've received
any requests over HTTP.
Problem: When replying to a private thread you only see the root post
because it's hiding all private comments. Instead, it should be hiding
all *encrypted* comments that we can't decrypt.
Solution: Use `isNotEncrypted()` instead of `isNotPrivate()`.
Problem: The Threads view advertises that it only shows public posts,
but is showing some private posts as well. This is not a security
concern, it's just a UI inconsistency.
Solution: Ensure that private posts are filtered from the view before
rendering. This also integrates a refactor that uses functions to check
for basic info like whether a message is a post, whether it's
encrypted/decrypted, whether it has a root, whether it has a fork, etc.
Problem: There's an intermittent test failure on Travis CI that seems on
only happen when we have two test suites. Since the lifecycle suite only
tests a subset of the basic test, we can safely remove it without
compromising any test coverage.
Solution: Remove the redundant lifecycle test and try not to think about
the problem too much. Maybe in the future when we need a second test
suite we can resolve this problem, or maybe the underlying bug will be
resolved downstream (e.g. https://github.com/tapjs/node-tap/pull/662),
but right now I think the benefits of "no intermittent test failures"
outweights the costs of "all tests must go in one file".
Problem: The Windows tests seem to be failing, it looks like a timeout
problem? Also there are a handful of debug statements around the code
that can be safely removed.
Solution: Double the timeout length and remove debug statements.
Problem: The test suite isn't closing the database because `app.close()`
only affects the HTTP server. This means that tests don't exit cleanly
and sockets remain open and all sorts of really fun stuff that we don't
want while writing tests.
Solution: Refactor `src/ssb.js` so that we can exit cleanly and have
less rope to hang ourselves with. Add a small lifecycle test that can
help us ensure that the bare minimum lifecycle events are working
correctly, plus now the previous tests are passing on my machine too.
Problem: ESLint and TypeScript help catch some types of regressions, but
they don't protect us against obvious stuff like "the server won't
start". This means that humans need to test a bunch of stuff manually,
and that can be really tedious and exhausting.
Solution: Yesterday someone invented this cool concept called "testing"
where you write automated tests for your software to ensure it actually
works the way you expect. It might have beeen invented before yesterday,
I don't know. Anyway, this solution adds a bunch of tests that send HTTP
GET requests to a bunch of endpoints to make sure the server is at least
returning HTTP 200 responses. It also fixes a race condition where HTTP
server was available before the readme / version strings were loaded.
Problem: I made a bad merge in 2836c80 which broke the server in some
environments. I had issues running the globablly installed binary,
whereas `npm start` seemed to work fine. Anyway, there's an error about
some missing config options because we weren't importing SSB-Config into
the server configuration. Instead, the full config was just:
```json
{ "conn": { "autostart": true } }
```
This lacks important properties like `shs`, which means that the server
can't start. Fun!
Solution: Pass SSB-Config first and then overlay our custom config on
top. I've also added comments and changed the variable names so that
this is harder to miss in the future.
Problem: Our dependencies are getting ahead of us!
Solution: Catch up with `npm update`. I think Markdown-It was the only
breaking change, and it didn't affect us.
Problem: Project root has a handful of files that aren't used at all or
could be placed elsewhere.
Solution: Delete unused files and move the changelog to the
documentation directory with the other Markdown files (other than the
readme, of course).
Problem: Footer buttons have the normal button background hover state
that makes them difficult to read. This was meant to be fixed in another
PR but I think I got the CSS order wrong.
Solution: Reorder the CSS hierarchy to fix the bug. For real this time.
- Instead of having a custom on hover effect, just add the Liked by
message to the title of the heart.
- When there are > 16 likes on a post, show +X more to convey this to
users.
Previous query incorrectly pulled and sorted posts, often showing years
old posts from newly followed people. This now behaves more consistently
as "recent threads from people in your extended network".
- Adds a hover popup that shows the names of everyone who liked a post
when hovering of the heart.
- Add new call to post.get that retrieves the names of all voters and
returns them instead of their ID's.
Problem: A `null` relationship represents when it's your profile, which
isn't very intuitive. It causes an error when we try to check for the
`blocking` and `following` properties, which don't exist on `null`.
Solution: Instead of `null`, set a `me` property to tell whether this
relationship is our own profile.
Problem: We have a way to follow and unfollow, but there's no way to
block or unblock. Also if you go to the profile of a blocked peer, their
posts still show up.
Solution: Add block and unblock and hide messages from blocked peers on
their profile page.
Fixes: https://github.com/fraction/oasis/issues/370
Problem: We had our contract in `contributing.md` but that didn't really
give actionable information on how to contribute or how to maintain this
project.
Solution: Move contract to `contract.md` and start documents for
contributing and maintaining.
Problem: Messages are being fetched with `post.get()`, which runs the
`transform()` function to decorate them with Markdown/etc, but then
they're being passed through `transform()` a second time at the end of
the function. This is inefficient and applies side-effects (like adding
channels to the post) twice.
Solution: Remove the final `transform()` so that these posts are only
decorated once.
Fixes: https://github.com/fraction/oasis/issues/358
Problem: The workaround is no longer necessary now that the new version
of @types/koa has been published. Thanks to @peterblazejewicz,
@harryparkdotio, and @orta for the quick upstream fix.
Solution: Run `npm update` and remove the `@ts-ignore` comment.
Problem: We still have heaps of implicit 'any' types that our linters
can't make sense of.
Solution: Keep on adding type documentation! Slowly but surely I'm
hoping we can reduce the amount of untyped JavaScript in the repo.
Problem: Since we don't validate the referer hostname or the HTTP host
header, an attacker who can trick an Oasis user into clicking a
malicious link can submit HTTP requests to localhost, potentially
publishing messages or extracting information.
Solution: Ensure that all requests have valid hostnames in the referer
to protect against CSRF attacks, ensure that all requests have valid
hostnames in the HTTP header to protect against DNS rebind attacks, and
ensure that errors are only output in the HTTP response when we're sure
that the request is valid.
Problem: After some fantastic new pull requests, there are some
inconsistencies between how each author implemented different features.
Solution: Fix regressions and iron out inconsistencies. This fixes the
heart highlight bug, uses the same indentation for Summaries + Threads +
the thread view, tightens up the CSP now that we don't need
unsafe-inline styles, and uses a neutral tone for indents with the same
width as the blockquote border (so far violet has been reserved for
private messages).
Problem: Some sigil links were broken because of a bug in SSB-Markdown
that was recently resolved.
Solution: Upgrade SSB-Markdown.
Fixes: https://github.com/fraction/oasis/issues/242
Problem: We're only attempting connection via Unix sockets, but some SSB
services may choose to only listen on TCP sockets.
Solution: Start by attempting connection to Unix socket and fallback to
the TCP socket remote if that doesn't work, before just trying to start
our own SSB service.
Problem: Some of us have a few different emails, which show up as
different people when we commit. It would be great to merge these Git
identities so that they don't show up as different people.
Solution: Use `.mailmap`, which lets people choose their favorite
canonical email address for this project.
While reading long conversations, it's easy to get a bit lost as to whom
is saying what. With some basic CSS, we can keep the header of the post
sticky so you can see more context above the message.
This allow someone to pass in the theme as a command line arg or set the
theme in defaults.json, while still allowing someone to set the theme on
a per-browser basis as a cookie.
Problem: The peer list shows peers that are connected and in the process
of establishing a new connection, which has lots of turbulence. It's
confusing to see dozens of "connections" that only exist for a few
milliseconds when they're actually just connection attempts.
Solution: Only show peers with the state "connected".
Problem: When publishing a root post, other clients give you the option
of adding a `channel` property, which is basically just a hashtag. We
show this at the end of the message, but since replies often copy the
`channel` property then it gets appended to every reply.
Solution: Only append the channel as a hashtag for root posts.
Problem: We have TypeScript enabled on the project but have been missing
lots of definition files, so there are like 500 errors when you enable
`--noImplicitAny`.
Solution: Add definitions and fix the bugs that they point out. This
reduces the number of errors with `--noImplicitAny` to 285.
Problem: When publishing a root post, other clients give you the option
of adding a `channel` property, which is basically just a hashtag. We
show this at the end of the message, but since replies often copy the
`channel` property then it gets appended to every reply.
Solution: Only append the channel as a hashtag for root posts.
Problem: Our dependencies are falling behind and it'd be nice to have
them up-to-date. The only big update it that Common-Good upgraded to the
latest version of Prettier.
Solution: Update them!
Problem: By default we only connect to people that we're directly
following, which often don't have public IP addresses.
Solution: Automatically connect to all available peers within our
replication hops (default 2) to make peering more reliable.
Problem: Trying to view a private message crashes the server and leaks a
bunch of memory. That's bad. This problem is caused by faulty handling
in the function that finds thread ancestors. There's some code that says
"if the next ancestor is a private message, just return the ancestors
that we know about", which returns nothing when we're looking up a
private post (because we can't identify *any* ancestors).
Solution: Ensure that we're only resolving the promise once in the
function by chaining `else if`s and ensure that we only return the
ancestor list if there are actually ancestor in it. If the ancestor list
is empty, we can just return the single message that we know about and
pass it off.
Problem: Since the `errorView()` has a refresh we can probably just call
it `indexingView()` and add the indexing-specific code.
Solution: Rename `errorView()` to `indexingView()` and add a progress
bar for it.
Problem: The indexing message is being thrown as an error, which is
pasted as plaintext, so we can't do a page refresh. This is frustrating
when you're waiting for the indexes to finish because you have to
manually refresh a bunch while you wait.
Solution: Use the prototype from @justinabrahms to add an HTML message
for the indexing error and automatically refresh the page every ten
seconds.
Previously you would get this error due to imperfect path creation
```
node contrib/install-systemd-service.js
fs.js:114
throw err;
^
Error: ENOENT: no such file or directory, copyfile '/home/justin/src/github.com/fraction/oasis/contrib/oasis.service' -> '/home/justin/.config/systemd/user/oasis.service'
at Object.copyFileSync (fs.js:1728:3)
at Object.<anonymous> (/home/justin/src/github.com/fraction/oasis/contrib/install-systemd-service.js:27:6)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
at startup (internal/bootstrap/node.js:283:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
```
Problem: When our views are still indexing the database they apparently
don't respond over MuxRPC, which means that we're just waiting forever
until they finish. This means that people who are indexing the database
get an HTTP response that might take an hour to finish.
Solution: If we have more than 1 mebibyte of backlog, present an error
message explaining the situation and asking for a bit of patience.
Problem: Patchwork is soon going to release with a new SSB-Backlinks,
and since we share a database with Patchwork we should make sure we're
on the same version. If we're on different versions, we'll be constantly
regenerating indexes and that isn't fun for anyone.
Solution: Upgrade npm dependencies.
Problem: The SSB-Markdown library has some SSB-flavored quirks, like
messing with newline behavior, and is inappropriate for rendering plain
Markdown like the readme.
Solution: Since SSB-Markdown uses Markdown-It under the hood, we can use
the same library and just render our Markdown *without* the SSB-flavored
quirks. This gives us the ability to wrap text and avoid SSB-Markdown
problems without having to rewrite all of our Markdown.
Patchwork freezes on launch if Oasis is already running (because of an SSB server conflict, I assume). I added an FAQ to guide people around that pothole.
The example command `oasis` all by itself wasn't obviously a command-line command, so I added a sentence about it.
Also added a sentence about installing Node, geared at non-technical people.
Problem: A big chunk of both the readme and the startup output is
dedicated to configuration, which feels to me to be focused on
developers and advanced users rather than beginners who just want to try
using Oasis.
Solution: Move readme configuration info to its own file and hide all
config output (except one line) behind `--debug`. While doing this I
noticed that we're `require()`ing a few modules that we don't need
before setting `process.env.DEBUG`, which I've reorganized so that we
don't accidentally disable debug mode for those modules.
Problem: I have debug mode manually enabled and have `console.log()`
calls sprinkled through the code and I'm doing `require("crypto")` too
many times.
Solution: Remove debugging cruft so that we don't have a perma-debug
mode.
Problem: We can set our name and profile description but profile images
are expected by most people and supported by most clients and we don't
have them. A profile without an image can sometimes lack the intimacy
you'd get if you let people upload profile images that they can use as a
visual avatar.
Solution: Add profile image upload to the Edit Profile page and add a
bunch of plumbing for `ssb.blobs.add()` to add the blob and publish a
message setting it as a profile image.
Problem: The install instructions in the readme contains quotes because
it has a `*`, but `#semver:` does what we need without the quotes. The
`docs/install.md` file also has some unnecessary complexity, like
cloning via SSH (only useful for maintainers), which I think we can
safely remove.
Solution: Change the install instruction and reorganize
`docs/install.md` to be more relevant to people who are installing from
source.
Problem: Other clients render a `channel` property, which is basically a
way to add a single hashtag to your post, but Oasis doesn't support
that. This means that someone might post "this is fun!" under the
channel #running and people using Oasis would just see "this is fun!"
without any context.
Solution: Add the hashtag to the bottom of the post, which visually
looks the same as someone adding two newlines and a hashtag to the end
of the text in their post.
Problem: The Extended page is sorting by received timestamp, not
asserted timestamp.
Solution: Find the `Date.now` and replace it with `Date.now()`, which
refers to the current date instead of `undefined`, which is what you get
if you try to `JSON.stringify(Date.now)`.
Problem: Searching for a hashtag should bring you to the hashtag page,
and the hashtag page should have some useful information about which
page you're on instead of just showing you the messages.
Solution: Add code so that if you search for a hashtag you're brought to
that page, and display some useful help text at the top of the page
explaining the hashtag page. While ensuring that the hashtag page showed
posts from around the network (instead of only people you're following)
I fixed the popular page so that it has the same behavior again (fixing
a regression) and filtered out gatherings (fixing *another* regression)
on the popular page. These probably should've happened in another commit
but I got carried away. :/
Happy to split this commit into two if it hurts to bundle the popular
fixes with the hashtag fix, but I'm low on energy and want to at least
open a PR in case it doesn't bother anyone.
Problem: The previous code was printing debug output so that I could
ensure that my argument checking worked correctly, but we don't want to
distribute a binary that echoes back your arguments to you.
Solution: Remove the debug output and add lots of comments to document
all of my misunderstandings of how the current code works. :~)
Problem: I don't know what I'm doing and can't figure out how to
cross-compile this simple C program to macOS and Windows
Solution: @cryptix reminded me that Go makes cross-compiling easy, so I
took a stab at writing my first Go program. It seems to be working on
macOS and Linux, but I haven't tested on Windows.
Problem: Apparently one of the previous commits switched us to
shell-style argument expansion.
Solution: Use the appropriate solution on Windows, `%*`.
Problem: We were using `darwin` instead of `win`.
Solution: Replace `darwin` with `win` to ensure that we're using the
correct path in the batch file we produce.
Problem: The search results weren't hiding results from blocked feeds.
Solution: Add `socialFilter()` to ensure that all blocked feeds are
removed from search results as the first step in the stream processing.
Problem: Sometimes you try to run Oasis twice and the second time you
run the program it blows up in your face about the port being taken.
Most other software just focuses or reopens the window instead of
throwing errors at you.
Solution: Implement `/.well-known/oasis` as a sanity check to see
whether the thing listening on the HTTP port. If it replies "oasis" then
we can open the tab (unless `config.open === false`) and avoid throwing
the error.
Problem: An SSB client should allow you to declare your own name, but
Oasis didn't support that behavior at all.
Solution: Add a basic 'Edit Profile' page that lets you set your name
and description. This doesn't implement profile images because I had
limited time and didn't want to think about encoding formats, but it's
worth mentioning that I remember something about binary uploads with the
default form encoding actually send 3 times as much data because of
escapes or something? This might not effect us because we're on
localhost, but I want to make a note that this isn't implemented yet.
This also makes a small change regarding the `<label>` element --
previously we were writing them as siblings to the input and using the
`for` attribute to target the input, but while messing with the CSS I
tried putting them directly in the `label()` and it ends up having the
same effect with less code.
Problem: Trying to use the DTA index when using SSB-Query doesn't work,
because that index only exists on SSB-Backlinks.
Solution: Change the filter to ensure that we're sorted by the asserted
timestamp and ignoring messages from the future. The SSB-Backlinks
plugin was taking the minimum between `.value.timestamp` and `timestamp`
but since we're not using SSB-Backlinks in this query (or maybe at all?)
we get to make our own little hacky query. Cel taught me that the
`$sort` option causes problems here because it buffers the entire result
in memory before doing the sort. Boo. Instead we just need to reference
the property that we want to be sorted by with some operation, even if
it does nothing (like `{ $gt: null }`), but since we're filtering time
travelers we actually need `{ $lge: Date.now() }` there anyway.
Problem: When running the Darwin binary from another directory, the
script would look in the user's current working directory for both
`node` and `src`, which is wrong.
Solution: Set `BASEDIR` and use it so that we use the correct paths.
Problem: I've quickly learned that cross-compiling is a major headache
and that C programming lacks both rainbows and butterflies. It seems
that my executable only works on Linux, but probably not macOS or
Windows.
Solution: Use shell scripts on macOS, which work when you double-click
them, and batch scripts on Windows, which I *think* work when you
double-click them. I haven't tested this on a macOS device yet, but I
tested the previous shell script on macOS and it seemed to work fine.
Unless I've done something silly, this should work on macOS, but the
Windows batch script is just me writing code from memory and probably
doesn't work correcly. I'll probably beg @SoapDog to help me fix it.
Problem: Writing C in a heredoc is super janky and I think it's worth
avoiding whenever we can. We've also been getting compiler warnings that
we should probably resolve.
Solution: Move the C to its own file and resolve the compiler warnings.
Problem: The shell script didn't work on other platforms and the
AppImage hack ended up confusing people who tried to install it with
AppImageLauncher.
Solution: This feels absolutely ridiculous, but I've written the worst C
program in the world which does the equivalent of `exec ./node src "$@"`
and immediately gets out of our way. This ends up being an executable
that both Nautilus and Dolphin (GNOME and KDE file managers) let us
double-click, and should also work on macOS. It might even work on
Windows if we rename it as an `.exe`, but I'm not sure and *definitely*
haven't tested that.
Problem: We try to `require()` our native modules to ensure that they
work correctly, but when they don't work we need to produce an error
that will help us debug the problem.
Solution: Switch from `node -p` to `node -e` to avoid printing
unnecessary output and don't redirect output for mandatory modules so
that we can debug them.
Problem: The installer contained npm and npx and a bunch of code that we
really don't need unless we're building from source. Since we don't want
our end-users to build anything from source, we can remove these.
Solution: Use shell-check to make some small fixups to the code and
remove everything we're downloading via wget except the `node` binary.
This requires moving the "build from source" fallback, and also includes
the `.appimage` hack when building from Linux to get double-click
opening working.
Problem: The previous approach of having the installer run `npm install`
was hazardous at best and didn't work well offline.
Solution: Package all of the source code with the application and use
the installer to ensure that it works on the target device. The
installer should ensure that we can `require()` the module, and if that
doesn't work then it should choose between two options:
- If the module is optional, `rm -rf` it and don't worry about it.
- If the module is mandatory, try running `npm install` with some flags
to ensure that we stay offline and print the absolute minimal output.
This should never happen, because we should never be packaging Oasis
for architectures and platforms that we don't have prebuilds for, but
it's a fine fallback behavior for experimental hackery.
Problem: We load the default config with `require()`, which would allow
arbitrary JavaScript execution, are producing debug output everywhere
with `console.log()` statements, and don't have instructions for how to
write the config file. The config file is also `.config/**/config.json`,
and I think it would be helpful to specify that this is the **default**
config rather than any other kind of config.
Solution: Rename the config to `~/.config/oasis/default.json`, replace
the `require()` with `fs.readFile()`, remove `console.log()` for
debugging, and add some example JSON for how to use the config file.
Problem: Running `./oasis --version` should output the current version,
it shouldn't just start the server by default.
Solution: Pass through command-line arguments with `$@` so that we
respect what the user is asking us to do.
This should give pretty much expected behaviour. Each config value
can be set by three sources:
1. By command-line argument. If it is not given, then
2. By config file. Or, lastly
3. By default value in the source code.
I can't test that the config file is searched and read from the right
place on windows or macOS, but on linux it works.
Problem: Most people don't have npm installed and we probably shouldn't
force them to have a full development environment to use Oasis. It would
be nice to have a way to install Oasis without any development tools.
Solution: Add a build script that can create .zip files that work on
Unix systems like macOs and Linux. Users will need to download the zip,
extract it, and double-click the `oasis` script. This script will run
`npm start || npm install && npm start`, which uses binaries that we
ship in the zip file so that they don't need to have `node` or `npm`
installed.
Problem: We're doing tons of unnecessary cryptography by encrypting the
connection between the "client" and "server", which are often running in
the same process.
Solution: Instead of connecting to the SSB service over TCP and
encrypting the stream, just connect over a socket (supported on Windows,
macOS, and Linux) and don't bother encrypting anything. This is what
Patchwork and Patchbay do already, and since our secret is at
`~/.ssb/secret` then we should be comfortable with `~/.ssb/socket` being
a trusted file where access implies authentication.
Local tests suggest that when sodium-native is available, this commit
reduces the time to render the 'Popular (Day)' page by 17%, but when we
have to fall back to JavaScript cryptography the same page now takes 30%
less time to render. My intuition is that this improvement is more
dramatic on mobile, but requires further testing before we can pat
ourselves on the back too much. :)
Problem: I thought `--no-optional` would just ignore our top-level
optional dependencies, but it ends up ignoring deep optionl dependencies
like sodium-native. We really want these dependencies if we can build
them.
Solution: Remove the `--no-optional` suggestion and add a note about
what to do if Sharp doesn't install correctly, which is very likely
since Termux doesn't have libvips available.
Problem: The Termux install is new and exciting and there isn't any
documentation on how to experiment with it.
Solution: Add some documentation like we've done with systemd and
Docker so that we can collaborate and figure it out in the repo!
Problem: We've been using a workaround to filter the results of
SSB-Mentions, but that's just been fixed upstream and so we don't need
the workaround anymore.
Solution: Update the SSB-Mentions package and remove the workaround.
Problem: During the SSB-Query refactor I used `{ private: false }` in an
attempt to hide private messages, but apparently this option is only
available via SSB-Backlinks, so it was ignored without any warning (!).
This allowed private messages to be displayed in public views.
Solution: Remove the unused option and add a filter to public views that
ensures that private messages are always hidden.
Problem: Private messages should only be showing up in the Private page,
not the Mentions page, but right now they're showing up in Mentions.
Solution: Add a check to ensure that Mentions doesn't have any private
messages.
Problem: When the port was already taken by another process we've been
dumping a big esoteric error message that really isn't helpful for most
people.
Solution: Catch the error and give our users a friendly error message
with a suggestion on how to run Oasis on another port.
Problem: I added an image border to try to make it easier to see the
boundaries of images but it's not really necessary and at least one
person didn't love it, which is grounds for removal.
Solution: Get it out of here! This commit removes the image border and
also removes a random `console.log()` that shouldn't have been left in
`src/index.js`.
Problem: After publishing a message you were redirected to the Popular
view, which wasn't good feedback that your message had been published.
Solution: After publishing a new message, redirect to the Latest view.
Problem: It was impossible to tell which version of Oasis you're
running, which makes it hard to know when you need to update.
Solution: Put the version in the settings so it's easy to reference.
Problem: The `messagesByType()` queries don't let us sort by asserted
time, which means that we can get messages from a long time ago that
just ended up on our computer.
Solution: Use SSB-Query everywhere. The query syntax isn't really my
favorite but it lets us make declarative queries that respect the
asserted timestamp instead of just the received timestamp.
Problem: The npm registry is great for proprietary modules who need to
ship binaries but not really useful for free and open source software.
It creates a divide between users and our Git repo, and introduces a
for-profit company as an intermediary to distribute Oasis.
Solution: Remove the npm registry as a distribution platform and just
use our Git repository instead. This removes npm registry references
from the readme, instead prioritizing the GitHub repository, and leaves
room for others to mirror the Git repository (for allows us to move to
less-Microsofty Git hosting).
Problem: It's hard to know who has worked on the project and who is
maintaining the project, for security and code of conduct issues.
Solution: Add AUTHORS and MAINTAINERS files to track who has contributed
code and mixed in their copyright (woo!) and keep track of who is
committed to maintaining the project and merging patches.
Problem: There wasn't an easy way to copy and paste a message ID, feed
ID, or blob ID and get directed to that page in Oasis.
Solution: When someone pastes one of the above into the search, just
redirect the browser to the correct page rather than searching for that
text.
Problem: Until we have a way to change the view of each page, it seems
to me that each of the views that we're experimenting with should be on
their own page rather than modifying the existing pages to add more
functionality. I'm also noticing that I can't process all of the
information that's on the screen when the new horizontal layout is used,
and I think we would benefit from using the previous layout
consistently.
Solution: Move the summary view to its own page so that we can continue
to iterate on it without modifying the popular view, which is already
complex, and revert the style changes to maintain consistency with the
rest of the linear design (threads go downward, indentation denotes
sub-threads, etc).
Problem: Recently we refactored the code so that we calls over MuxRPC
used promises, which was incompatible with the client-side SSB-Tangle
plugin that was expecting a callback. While debugging this error, I also
noticed that our `{ ws: { http: false } }` code wasn't doing anything,
and that our connection error handler was handling all kinds of
unrelated errors that would make debug more difficult.
Solution: Use `util.promisify()` to convert the callback-style function
into a promise-style function, remove the unused SSB-WS code, and reduce
the scope of the error catcher so that it'll continue to output errors
unless they're "cannot connect to sbot".
Problem: When we couldn't figure out someone's name or description we
were using `null`, which got coerced to a string and ended up with us
calling people `"@null"`. Not ideal!
Solution: Support a use-case where someone doesn't have a profile
description or a name, and use the first 8 characters of their public
key for their name if they haven't set a name themselves.
Problem: The new custom publish was called 'Manual mode', which I'm
afraid isn't super easy to understand for people who don't already know
SSB. The custom publishing page didn't follow the same conventions as
other pages, either.
Solution: Hide the custom publish mode behind the publish page, with a
warning that it's for 'advanced users', and ensure that it uses the same
conventions and visual styles as other pages and code around the app.
Problem: The new content warning code was applying a content warning to
all posts, which tried to publish an invalid content warning `""` and
threw an error.
Solution: Only publish a content warning if it's a string with non-zero
length.
Problem: Emoji from Markdown posts were being displayed in the default
font, which doesn't always use color emoji.
Solution: Use the `.emoji` CSS class on those emoji to ensure that the
correct emoji font is used.
Problem: The roadmap should exist in the issue tracker, not the repo.
Solution: Since all of the roadmap items have been converted into
issues, we're now good to remove the roadmap from the repo.
Problem: When replying to a private thread we already know who the
recipients are, and they're already going to get the notification, so
there's really no reason to add a mention.
Solution: Remove the mention when the message is private.
Problem: Previously we were only counting likes from people you follow,
but showing messages from anyone. This was backward, and could
potentially show messages from blocked authors that were liked by people
you follow.
Solution: Move the `socialFilter()` invocation down the pipeline so that
it sorts the output messages, not the likes.
Problem: Any error thrown while looking for thread ancestors was
throwing a "message not found" error, which was incorrect and useless.
This error caused me to his refresh repeatedly, not understanding that I
was publishing multiple messages. Super bad. While investigating this I
found that there's a slightly different problem where someone can post
an *invalid* message link, which we don't currently have handling for.
Solution: Only show that error when it's actually happening, and add
support for just ignoring when we see an invalid message link as `root`
or `fork`.
Problem: In the old `cooler.read()` implementation, sources returned
promises, but that isn't the case in the native SSB-Client promise
impelementation. This means that in at least one place, there's a bug
where it tries to call `.then()` on a non-promise.
Solution: Remove promise-based code from streams, which don't require
any special handling anymore.
Problem: The previous readme didn't really have very much information
about what Oasis is or what we want it to be. It wasn't very enticing.
Solution: Zoom out a bit and give a top-3 list of features that
distinguishes Oasis from other social networking applications and SSB
applications in general.
Problem: Your posts show up in Extended, which is unexpected because I'm
the center of my network, not some rando at the periphery.
Solution: Use the `socialFilter()` function to make sure that the
extended view only shows people in your extended network, not you. :)
Problem: We started using `cooler.get()` and `cooler.read()` because it
was impossible to use promises with SSB-Client.
Solution: I made some downstream pull requests into the MuxRPC module
and the SSB-Client module, which means that both of them now natively
support promises. This commit removes the weird convenience methods and
replaces them with the native promise support, which should hopefully
make the code easier to read and write.
Problem: I didn't really like random text posts from me being in the
readme, and the aspect ratio of the screenshot meant that it ended up
taking up lots of vertical space.
Solution: Use a wide screenshot of some concept art to keep it from
stretching vertically very far while still showing off the absolute
basics of Oasis.
Problem: There was one missing component that would filter out nicknames
from other people. This caused a problem where we could get a name for a
feed but it could've been assigned by a friend, which we don't want
right now.
Solution: Ensure that the subject of the message is the same as the
author of the message.
Problem: Search engines are controversial and my understanding is that
most people on SSB don't want their messages indexed by search engines.
If that's the case, we should probably disable it.
Solution: Add basic `robots.txt` file to ask search engines to stay away
and please don't save info. I'm concerned that, like `publicWebHosting`
redactions, it gives a false sense of privacy, but it seems like this is
probably what most people would want?
Problem: The SSB-About plugin is incompatible with our needs. More info
in the GitHub issue linked below and in the code comments.
Solution: Unfortunately, roll our own alternative to the SSB-About
plugin so that we can be 100% sure that it pulls the latest 'about'
published by an author about themselves and doesn't just skip `false`.
Problem: The `socialFilter()` function wasn't documented and contained a
bug where it wouldn't show your posts when `following = true`. This is
because you usually don't follow yourself, so `following = true` was
basically equivalent to `me = false`.
Solution: Add some documentation and resolve the bug by adding special
handling for when the message is from us *before* passing to the general
implementation for follow/block checking.
Resolves https://github.com/fraction/oasis/issues/155
Resolves https://github.com/fraction/oasis/issues/177
Problem: It's hard to show off Oasis or take screenshots without
respecting the `publicWebHosting` convention. While `publicWebHosting`
lacks a formal specification and I'm a bit confused about what its
boundaries are, it sounds like some of our friends would like to avoid
us publishing any of their content on the public web if we can avoid it.
Solution: Add --public option that turns Oasis into a public web viewer.
This makes it **slightly inconvenient** to see these public posts, but
should absolutely not be mistaken for a privacy guarantee. Only HTTP GET
endpoints are allowed, so random people can't publish or change
settings. The name, avatar, description, content warning, and message
contents are replaced with "Redacted", but again, this is all public
information that we can never provide real privacy for.
Resolves https://github.com/fraction/oasis/issues/48
Problem: We use nosniff to keep the web browser from getting confused
about what kinds of content we're serving in Oasis, but this causes
problems for blob URLs that have arbitrary data.
Solution: Remove nosniff on blob URLs to let the browser figure out what
kind of content we're serving.
Resolves https://github.com/fraction/oasis/issues/138
Problem: The regular space in the sidebar emoji was breaking the line at
a specific viewport width and the emoji were being shown as the wrong
font.
Solution: Use a non-breaking space and `font-family: initial` for
full-color emoji instead of using the system font.
Resolves https://github.com/fraction/oasis/issues/150
Resolves https://github.com/fraction/oasis/issues/153
Problem: The socialFilter was hiding posts published by the user, which
felt weird and uncanny.
Solution: Fix the default so that `{ me }` isn't hidden from a view
unless the model specifically wants that to happen.
Resolves https://github.com/fraction/oasis/issues/156
Problem: The Private and Mentions page didn't have view labels yet, and
Publish should be its own page instead of being at the top of every
page.
Solution: Inspired by @cinnamon-bun's work to add friendly view labels,
plus a new Publish page. This also moves the period selection from the
popular page into the view label, which felt better to me with the
previous `<section>` background. I also tried a different text format
for describing the pages, using a common form and using `<strong>` to
draw attention to any change from "Posts from people you follow, sorted
by recency" which feels like the expected default for most people.
@cinnamon-bun: To me this feels like a fun back-and-forth where I'm
riffing on your work and hoping that you do the same, but if it feels
wrong/rude please let me know. Trying to work on designs with a system
like C4 is super new for me and I wouldn't be surprised if there are
pain points to fix!
Resolves https://github.com/fraction/oasis/issues/160
Problem: There was no way to onboard new users since we couldn't redeem
invites.
Solution: Add basic follow-back invites to the settings page. This takes
an invite string, runs it through invite.accept, and either returns the
error in full *or* redeems the invite quietly.
Problem: @masukomi pointed out that the 'latest' view doesn't show the
awesome slice of content that you'll see if you just look at root posts.
Solution: Let's experiment with them! This commit adds a 'Topics' page
that has the latest root posts from people you're following.
Problem: The nav is aligned to the left of the screen on mobile, which
feels off-center and unbalanced on mobile. https://github.com/fraction/oasis/issues/135
Solution: Center the menu to optimize for space around the links.
Problem: We shouldn't be showing any strangers on the popular or latest
pages. We shouldn't be showing anyone who's been blocked on any of those
feeds.
Solution: Don't show blocked people on any pages and don't show
strangers on the popular / latest pages.
Problem: If the user tries to see a thread and the link points to a
message we don't have, then we don't have any way to display anything in
the thread. How could we even know which thread it's in?
Solution: Throw the error but make it more useful and fix the "non-error
thrown" verbiage that we've inherited from a dependency trying to throw
a non-error.
Problem: I think during a refactor this code was changed and ended up
breaking the "fake image" that we return when the user doesn't have an
image. We also don't see image errors because they aren't in the browser
viewport if they return text and we don't `console.error()` our errors.
Solution: Fix the image code to return a PNG as a buffer and duplicate
errors to stderr.
Problem: I still haven't found any best practices for how to run HTML on
your domain without any security concerns. I feel reasonably confident
that our current security precautions mitigate any potential attacks,
but I want to be very up-front with any security mitigations that aren't
so common that they're boring and predictable. Resolves https://github.com/fraction/oasis/issues/5.
Solution: Add a document to describe our current security model and
mitigations so that we can point at it when asking friends to review the
model and ensure we don't have any holes.
Problem: SSB-Search has a bug where too few characters just hangs the
search indefinitely and never returns. https://github.com/fraction/oasis/issues/107
Solution: Enforce a minimum length of 3 characters in the search. I
bumped into another bug where HyperScript, a dependency of HyperAxe,
doesn't support the `minlength` attribute, so I had to deploy a small
workaround for that too. The fixes aren't very pretty but they're better
than just ignoring the problem.
Problem: Help link in readme was pointing to the issue type selection
which is no longer useful because we just have one template.
Solution: Fix the link and point directly to the issue creation link.
Problem: The previous commits didn't persist language choices and the
drop-down's initial value wasn't respecting the language you selected.
Solution: Persist the language choice in a cookie, defaulting to
English, and build the drop-down with the selected language. This also
changes the word "Spanish" to "Español", and slightly refactors
`http.js` to accept *middleware* rather than just routes. This lets us
add other middleware, such as the language selection middleware added in
this commit.
Problem: The previous commit added English as a language but didn't add
other languages or ways to switch between them.
Solution: Add the most primitive language selection possible and a few
small translations contributed by @bramdroid during a totally unrelated
conversation. This does not persist the language selection and doesn't
auto-select the current language from the dropdown, but those should be
easy to add in the future.
Problem: It was impossible to do any internationalization because
strings were all embedded throughout Oasis.
Solution: Add an internationalization submodule that provides
language-specific strings for the text elements in views. In future
commits we can add language selection and fallbacks for when the
selected language doesn't support the text we need to have translated.
Problem: The 'Following' page was super slow because we were doing a
MuxRPC request on every single message.
Solution: Do one request to see who we have relationships with, filter
out the people we aren't following, and then check against that list
instead of doing a bunch of MuxRPC calls.
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `curl http://localhost:3000/public/latest/following` | 500.9 ± 163.4 | 392.1 | 881.4 | 1.00 |
| `curl http://localhost:3456/public/latest/following` | 4663.7 ± 184.6 | 4438.6 | 5075.2 | 9.31 ± 3.06 |
Problem: Most browsers parse SVG files as XML and refuse to display it
in an `<img>` tag. It's usually unsafe to have browsers try to sniff the
file type themselves, because they can be tricked into running unsafe
code, so we want to set the file type ourselves in the server.
Solution: Use the Is-SVG library for a quick-n-dirty check for whether a
buffer is an SVG. If so, we set the file type accordingly.
Problem: The model code was doing our Markdown rendering, which feels to
me like a layer violation because *generally* the model is meant to be a
thin abstraction over the underlying database without any concept of the
presentation layer.
Solution: Move the Markdown renderer to the view module and manage
Markdown rendering in the presentation layer.
Problem: Targeting only the latest version of Node.js and the latest LTS
means that we have a sort of rolling backward-compatibility that's hard
to reason about. If we break compatibility, we should release those
changes as a major version, but the current strategy makes it difficult
to tell exactly when we're breaking compatibility.
Solution: Specify the exact Node.js versions that we want to support in
`package.json` and add the specific tests to `.travis.yml`. In the
future we can break compatibility if it's helpful but we'll have to do
so explicitly and release the change as a major version.
Problem: We don't always want to see all of the messages on our
computer, sometimes we just want to see messages from the people we're
explicitly following. The 'Popular' and 'Latest' pages don't help with
that.
Solution: Add a super basic page that just shows the latest messages
from the people you're explicitly following.
Problem: The popular page is ungodly slow.
Solution: Make it faster! This is done by checking the timestamp before
the other constraints that we add to messages, which is mostly useful
because most messages that fail the filter will fail on the timestamp
check.
Problem: Showing private posts on profiles is scary and may give people
the impression that these posts are visible.
https://github.com/fraction/oasis/issues/113
Solution: Hide private messages when rendering public profiles and
change the method name to be very clear that it only returns public
messages.
Problem: Stopping the networking would sometimes allow peers to remain
connected, which was confusing.
Solution: Upgrade to latest SSB-CONN to pull in a bugfix for this
behavior. Huge thanks to @staltz for the quick fix!
Problem: On short pages, like the search page, there's no scrollbar
gutter on the right side of the page, which moves everything a few
pixels to the right. After banging my head against this for an hour I finally
realized that it only happens when your viewport is taller than the
content on the page, which creates the scrollbar. This was reported as:
https://github.com/fraction/oasis/issues/96
Solution: Change the display so that we consistently show a scrollbar
gutter on the right side of the page regardless of whether we need it
(e.g. on very short pages or on very tall monitors). This means that
when we center the content on the page it won't move depending on the
height of the page, which was frustrating and janky.
Problem: The `<section>` elements on some pages have different sizes,
which means that the sidebar is doing wonky stuff.
Solution: Make the sections all have a consistent width. Note that
there's still a few pixels of jank on the 'search' page, I'm not sure
why that's happening. The element inspector is reporting that everything
is the same width but that isn't true.
Problem: Putting the navigation at the top of the screen makes it
impossible to use when you're scrolling through a page, which isn't a
good user experience. It was never meant to be permanent, and I think
everyone has pointed out that it's been a pain.
Solution: Super simple sidebar nav when people are on bigger screens.
This doesn't solve the problem on mobile, and it doesn't incorporate the
'popular' page's interval settings, but I think it's a step in the right
direction.
Problem: Using all-lowercase-everything isn't really a standard around
the web and it might be better to use consistent capitalization. This
was brought up in: https://github.com/fraction/oasis/issues/98
Solution: This changes the main navigation to use links with the first
letter capitalized, like how Patchwork + Twitter + Mastodon + etc do it.
This means that we're consistently using sentence case everywhere, which
I think is our best option. Originally I tried experimenting with
all-caps for actions, which I found aesthetically pleasing, but you have
to reduce the font size to make it look good (bad!) and I was reading
that all-caps text is harder for friends with dyslexia or vision
impairments.
Problem: We were overwriting SSB-Tangle with itself, which is fine, but
kind of confusing and requires some knowledge on what is and isn't safe
for Secret-Stack.
Solution: Only inject SSB-Tangle if it doesn't already exist, which
makes the code easier to reason about.
Problem: The --offline documentation doesn't mention that networking
status can be changed, which may give the false understanding that the
networking is permanently offline when you use that flag.
Solution: Add a note that mentions that the 'meta' page lets you change
your networking status, and that --offline is only applicable to the
starting state of Oasis.
Problem: Sometimes you want to disconnect from the network but you don't
want to have to restart Oasis with the `--offline` flag
(https://github.com/fraction/oasis/issues/89). Sometimes networking gets
stuck and you need to run the equivalent of `ssb gossip reconnect`
(https://github.com/fraction/oasis/issues/63).
Solution: Buttons on the 'meta' page that let you start, stop, or
restart SSB-CONN whenever you want. Note that this commit includes an
update to SSB-CONN, but this version and the previous version both have
a bug where hitting 'stop' twice in a row will throw an error. This
commit implements a workaround for the bug, but it's something we'll
want to remove later once the underlying bug is fixed.
Problem: Patchwork is missing a plugin that we need to set the `branch`
property when posting a message. This property is important because it
helps us sort threads, so we're throwing an error when it isn't
available. See: https://github.com/fraction/oasis/issues/21
Solution: HACK THE ~~PLANET~~ API. This commit injects the plugin we
need via Oasis, which is a bit of a duct tape solution but it *is* a
solution.
Problem: When we don't have any documentation for variable types it's
difficult for both humans and machines to parse our code.
Solution: As discussed in https://github.com/fraction/oasis/issues/78,
adding some JSDoc information on function signatures would be a nice
step in the right direction and could make debugging easier.
Problem: CG's dependency, CSpell, had problems with Windows support, so
I was using a forked module as an alias to circumvent the problem. This
only works on very recent version of npm.
Solution: This morning the CSpell maintainer resolved the problem, so
the latest CG doesn't have to depend on an alias. This commit runs `npm
update` to get the latest version of CG so we don't have this problem.
Problem: The /inbox page was being rendered super slowly because it was
reading through tons of messages.
Solution: There isn't a way to query the database for "private messages
for me", although maybe there should be, but one way we can get
something close is querying for "messages that reference me". Every
message that's encrypted for us will have a `.value.content.recps`
property that includes our feed ID, so we just have to filter out the
public messages and we're about 4 times faster than the previous
implementation.
```console
$ hyperfine 'curl -I http://localhost:4515/inbox' 'curl -I http://localhost:3000/inbox'
Benchmark #1: curl -I http://localhost:4515/inbox
Time (mean ± σ): 3.352 s ± 0.093 s [User: 2.0 ms, System: 4.3 ms]
Range (min … max): 3.231 s … 3.483 s 10 runs
Benchmark #2: curl -I http://localhost:3000/inbox
Time (mean ± σ): 811.8 ms ± 88.3 ms [User: 2.7 ms, System: 2.9 ms]
Range (min … max): 709.1 ms … 972.5 ms 10 runs
Summary
'curl -I http://localhost:3000/inbox' ran
4.13 ± 0.46 times faster than 'curl -I http://localhost:4515/inbox'
```
Problem: Trying to reply to some messages sent with Patchbay fail
because the schema check is throwing an error.
Solution: When we encounter `recps` like `{ name, link }`, normalize it
to just `link` and publish a well-formed message that passes the schema
check.
Problem: Common-Good had a bug in the Prettier glob where the recursion
wasn't working correctly. This meant that we weren't actually testing or
fixing some of the deeper files.
Solution: Update CG to the latest version and run `npm run fix` to fix
the linter stuff.
Problem: Some of the icon was being clipped in my browser. I think that
this is because the SVG text actuall extends down under the line (like a
`g` or `y`).
Solution: Change SVG viewBox dimensions and SVG size to fit correctly.
Problem: Travis builds PRs and branches, which means that local branches
get built twice (!).
Solution: Travis should only build `master`, and maybe tags.
Problem: I've had a few friends have trouble installing Oasis with an older version of Node.js.
Solution: Add supported versions to the readme in a way that we don't have to update (like version numbers) and link to the Node.js website for people who want to learn more about the release schedule.
I was having a problem where I'd start my computer but the script wasn't
restarting audomatically, and after some searching I found this:
https://github.com/systemd/systemd/issues/2690#issuecomment-186973730
It looks like we should be using `default.target` instead of
`multi-user.target`.
**Friendly Scuttlebutt interface designed for simplicity and accessibility.**
This is an experimental client built with HTML, CSS, and Node.js without any
front-end JavaScript. The goal is to support basic social messaging schemas with
some extra tools for debugging, *not* to support all known message types.
Oasis is a **free, open-source, peer-to-peer social application** that helps
you follow friends and discover new ones on [Secure Scuttlebutt (SSB)][ssb].
## Usage
**🦀 Powered by SSB.**
You're the center of your own distributed network. Online or offline, SSB works
anywhere that you are. Follow the people you want to see and never worry about
spam again. Switch to any SSB app you want at any time.
```console
$ oasis --help
Usage: oasis [options]
**🌐 Bring your own browser.**
Use your favorite web browser to read and write messages to the people you care
about. Oasis runs a small HTTP server on your own computer, so you don't need
to worry about adding another Electron app to your computer.
Options:
--help Show help [boolean]
--version Show version number [boolean]
--open Automatically open app in web browser [boolean] [default: true]
--host Hostname for web app to listen on[string] [default: "localhost"]
--port Port for web app to listen on [number] [default: 3000]
--debug Use verbose output for debugging [boolean] [default: false]
**🏰 Just HTML and CSS.**
No browser JavaScript! Oasis has strict security rules that prevent any
JavaScript from running in your browser, which helps us make Oasis accessible
and easy to improve.
## Example
After installing, launch Oasis from the command line by running:
```sh
oasis
```
It will then pop open a browser window for you.
![Screenshot of Oasis](./docs/screenshot.png)
Use `oasis --help` to get configuration options. You can change the default
values with a custom [configuration](./docs/configuring.md).
## Installation
With [npm](https://npmjs.org/):
First, you'll need Node.js and npm on your computer. Run `node --version` to see if you have it. If not, or if it's older than the [**current** or **active LTS** version](https://nodejs.org/en/about/releases/), you should [download Node.js](https://nodejs.org/en/about/releases/) first.
Then you can install the stable version of Oasis:
```shell
npm -g install @fraction/oasis@latest
npm -g install fraction/oasis#semver:
```
With [yarn](https://yarnpkg.com/en/):
Or, for faster updates and less stability, install Oasis from GitHub and upgrade often.
```shell
yarn global add @fraction/oasis@latest
npm-g install fraction/oasis
```
Check out [`install.md`](https://github.com/fraction/oasis/blob/master/docs/install.md)
for more information.
## FAQ
### Can I use the same profile from multiple computers?
No, this is a limitation of SSB. You'll need to make a separate profile on each device. There is a workaround [we'd like to implement](https://github.com/fraction/oasis/issues/267) which ties your multiple profiles together.
### Can I run this at the same time as Patchwork and other SSB apps?
Yes! They will both use the same data and profile. You can either run them one at a time or simultaneously. If you run them at the same time, start Patchwork first, then Oasis.
Details: SSB apps have two parts: a "server" that manages the database (on your own computer), and a user interface that gets things from the server to display. Only one server can run at a time, but multiple apps can use it.
| App | Runs its own SSB server | Can use SSB server of another app |
The default options can be permanently set with a configuration file found in a
standard folder for configuration, depending on your operating system:
- Linux: `$XDG_CONFIG_HOME/oasis/default.json`.
Usually this is `/home/<your username>/.config/oasis/default.json`
- Windows `%APPDATA%\oasis\default.json`.
- Mac OS, `/Users/<your username>/Library/Preferences/oasis/default.json`
The configuration file can override any or all of the command-line _defaults_.
Here is an example customizing the port number and the "open" settings:
```json
{
"open": false,
"port": 19192
}
```
If you run `oasis --help` you'll see all of the parameters, which are also defined in the source code here: https://github.com/fraction/oasis/blob/master/src/cli.js
## Semantics
Which value is given is decided like this:
1. If an argument is given on the command-line, use that value.
2. Otherwise, use the value from the configuration file if present.
3. If neither command-line nor configuration file are given, use the built-in default value.
# Custom Styles
The stylesheet values may be overridden by adding custom values to a file found in a
standard folder for configuration, depending on your operating system:
# Collective Code Construction Contract - Oasis Implementation
The Collective Code Construction Contract (C4) is an evolution of the github.com [Fork + Pull Model](https://help.github.com/articles/about-pull-requests/), aimed at providing an optimal collaboration model for free software projects.
This is the Oasis-specific implementation, based on [revision 2 of C4](https://github.com/zeromq/rfc/blob/63024673f19ad136652ff7b3bfb3a6547811e006/42/README.md).
## Summary
Thank you for contributing to Oasis! Here we try capture how we collaborate, and why we do it this way.
This entire document is open for changes, if there is anything that is confusing or can be improved, please start a discussion with us!
## License
Copyright (c) 2009-2016 Pieter Hintjens.
This Specification is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
This Specification is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses>.
## Abstract
C4 provides a standard process for contributing, evaluating and discussing improvements on software projects. It defines specific technical requirements for projects like a style guide, unit tests, `git` and similar platforms. It also establishes different personas for projects, with clear and distinct duties. C4 specifies a process for documenting and discussing issues including seeking consensus and clear descriptions, use of "pull requests" and systematic reviews.
## Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://tools.ietf.org/html/rfc2119).
## 1. Goals
C4 is meant to provide a reusable optimal collaboration model for open source software projects. It has these specific goals:
1. To maximize the scale and diversity of the community around a project, by reducing the friction for new Contributors and creating a scaled participation model with strong positive feedbacks;
1. To relieve dependencies on key individuals by separating different skill sets so that there is a larger pool of competence in any required domain;
1. To allow the project to develop faster and more accurately, by increasing the diversity of the decision making process;
1. To support the natural life cycle of project versions from experimental through to stable, by allowing safe experimentation, rapid failure, and isolation of stable code;
1. To reduce the internal complexity of project repositories, thus making it easier for Contributors to participate and reducing the scope for error;
1. To enforce collective ownership of the project, which increases economic incentive to Contributors and reduces the risk of hijack by hostile entities.
## 2. Design
### 2.1. Preliminaries
1. The project MUST use the git distributed revision control system.
1. The project MUST be hosted on github.com or equivalent, herein called the "Platform".
1. The project MUST use the Platform issue tracker.
1. The project SHOULD have clearly documented guidelines for code style.
1. A "Contributor" is a person who wishes to provide a patch, being a set of commits that solve some clearly identified problem.
1. A "Maintainer" is a person who merges patches to the project. Maintainers are not developers; their job is to enforce process.
1. Contributors MUST NOT have commit access to the repository unless they are also Maintainers.
1. Maintainers MUST have commit access to the repository.
1. Everyone, without distinction or discrimination, MUST have an equal right to become a Contributor under the terms of this contract.
### 2.2. Licensing and Ownership
1. The project MUST use a share-alike license such as the MPLv2, or a GPLv3 variant thereof (GPL, LGPL, AGPL).
1. All contributions to the project source code ("patches") MUST use the same license as the project.
1. All patches are owned by their authors. There MUST NOT be any copyright assignment process.
1. Each Contributor MUST be responsible for identifying themselves in the project Contributor list.
### 2.3. Patch Requirements
1. Maintainers and Contributors MUST have a Platform account and SHOULD use their real names or a well-known alias.
1. A patch SHOULD be a minimal and accurate answer to exactly one identified and agreed problem.
1. A patch MUST adhere to the code style guidelines of the project if these are defined.
1. A patch MUST adhere to the "Evolution of Public Contracts" guidelines defined below.
1. A patch MUST NOT include non-trivial code from other projects unless the Contributor is the original author of that code.
1. A patch MUST compile cleanly and pass project self-tests on at least the principal target platform.
1. A patch commit message SHOULD consist of a single short (less than 50 characters) line stating the problem ("Problem: ...") being solved, followed by a blank line and then the proposed solution ("Solution: ...").
1. A "Correct Patch" is one that satisfies the above requirements.
### 2.4. Development Process
1. Change on the project MUST be governed by the pattern of accurately identifying problems and applying minimal, accurate solutions to these problems.
1. To request changes, a user SHOULD log an issue on the project Platform issue tracker.
1. The user or Contributor SHOULD write the issue by describing the problem they face or observe.
1. The user or Contributor SHOULD seek consensus on the accuracy of their observation, and the value of solving the problem.
1. Users MUST NOT log feature requests, ideas, suggestions, or any solutions to problems that are not explicitly documented and provable.
1. Thus, the release history of the project MUST be a list of meaningful issues logged and solved.
1. To work on an issue, a Contributor MUST fork the project repository and then work on their forked repository.
1. To submit a patch, a Contributor MUST create a Platform pull request back to the project.
1. A Contributor MUST NOT commit changes directly to the project.
1. If the Platform implements pull requests as issues, a Contributor MAY directly send a pull request without logging a separate issue.
1. To discuss a patch, people MAY comment on the Platform pull request, on the commit, or elsewhere.
1. To accept or reject a patch, a Maintainer MUST use the Platform interface.
1. Maintainers SHOULD NOT merge their own patches except in exceptional cases, such as non-responsiveness from other Maintainers for an extended period (more than 1-2 days).
1. Maintainers MUST NOT make value judgments on correct patches.
1. Maintainers MUST merge correct patches from other Contributors rapidly.
1. Maintainers MAY merge incorrect patches from other Contributors with the goals of (a) ending fruitless discussions, (b) capturing toxic patches in the historical record, (c) engaging with the Contributor on improving their patch quality.
1. The user who created an issue SHOULD close the issue after checking the patch is successful.
1. Any Contributor who has value judgments on a patch SHOULD express these via their own patches.
1. Maintainers SHOULD close user issues that are left open without action for an uncomfortable period of time.
### 2.5. Branches and Releases
1. The project MUST have one branch ("master") that always holds the latest in-progress version and SHOULD always build.
1. The project MUST NOT use topic branches for any reason. Personal forks MAY use topic branches.
1. To make a stable release a Maintainer must tag the repository. Stable releases MUST always be released from the repository master.
### 2.6. Evolution of Public Contracts
1. All Public Contracts (APIs or protocols) MUST be documented.
1. All Public Contracts SHOULD have space for extensibility and experimentation.
1. A patch that modifies a stable Public Contract SHOULD not break existing applications unless there is overriding consensus on the value of doing this.
1. A patch that introduces new features SHOULD do so using new names (a new contract).
1. New contracts SHOULD be marked as "draft" until they are stable and used by real users.
1. Old contracts SHOULD be deprecated in a systematic fashion by marking them as "deprecated" and replacing them with new contracts as needed.
1. When sufficient time has passed, old deprecated contracts SHOULD be removed.
1. Old names MUST NOT be reused by new contracts.
### 2.7. Project Administration
1. The project founders MUST act as Administrators to manage the set of project Maintainers.
1. The Administrators MUST ensure their own succession over time by promoting the most effective Maintainers.
1. A new Contributor who makes correct patches, who clearly understands the project goals, and the process SHOULD be invited to become a Maintainer.
1. Administrators SHOULD remove Maintainers who are inactive for an extended period of time, or who repeatedly fail to apply this process accurately.
1. Administrators SHOULD block or ban "bad actors" who cause stress and pain to others in the project. This should be done after public discussion, with a chance for all parties to speak. A bad actor is someone who repeatedly ignores the rules and culture of the project, who is needlessly argumentative or hostile, or who is offensive, and who is unable to self-correct their behavior when asked to do so by others.
## Further Reading
- [Argyris' Models 1 and 2](http://en.wikipedia.org/wiki/Chris_Argyris) - the goals of C4 are consistent with Argyris' Model 2.
- [Toyota Kata](http://en.wikipedia.org/wiki/Toyota_Kata) - covering the Improvement Kata (fixing problems one at a time) and the Coaching Kata (helping others to learn the Improvement Kata).
## Implementations
- The [ZeroMQ community](http://zeromq.org) uses the C4 process for many projects.
- [OSSEC](http://www.ossec.net/) [uses the C4 process](https://ossec-docs.readthedocs.org/en/latest/development/oRFC/orfc-1.html).
- The [Machinekit](http://www.machinekit.io/) community [uses the C4 process](http://www.machinekit.io/about/).
This is a guide on how to download the source code for Oasis so that you can
build and install it on your computer. If you'd like an easier installation
option, try one of the options in the readme.
If you want to run Oasis in the background, see [`with-systemd.md`](./with-systemd.md).
If you want to run Oasis in a container, see [`with-docker.md`](./with-docker.md).
If you want to run Oasis on Android via Termux, see [`with-termux.md`](./with-termux.md).
## Download
### HTTP
Download Oasis from GitHub over HTTPS.
```shell
git clone https://github.com/fraction/oasis.git
```
## Install dependencies
Most people should build and install Oasis with npm.
```shell
cd oasis
npm install --only=prod
```
### SSH
## Start
You can try Oasis without installing with:
```shell
git clone git@github.com:fraction/oasis.git
cd oasis
node .
```
## Dependencies
## Install globally
### Yarn
If you want to install to make `oasis` available globally:
```shell
yarn
npm -g install .
```
### npm
```shell
npm install
```
If you see a permission error, see [resolving EACCESS permission errors](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally). If you any other problems, please [reach out for help](https://github.com/fraction/oasis/issues/new).
", sorted by recency. When you follow someone you may download messages from the people they follow, and those messages show up here.",
],
popular:"Popular",
popularDescription:[
"Posts from people in your network, sorted by ",
strong("hearts"),
" in a given period. Hearts are counted from ",
em("everyone"),
", including people you don't follow, so this shows posts from your friends that are popular in your extended network.",
],
latest:"Latest",
latestDescription:
"Posts from yourself and people you follow, sorted by recency.",
topics:"Topics",
topicsDescription:[
strong("Topics"),
" from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread.",
],
summaries:"Summaries",
summariesDescription:[
strong("Topics and some comments"),
" from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread.",
],
threads:"Threads",
threadsDescription:[
strong("Posts that have comments"),
" from people you follow and your extended network, sorted by recency. Select the timestamp of any post to see the rest of the thread.",
],
profile:"Profile",
manualMode:"Manual Mode",
mentions:"Mentions",
mentionsDescription:[
strong("Posts that mention you"),
" from ",
strong("anyone"),
" sorted by recency. Sometimes people may forget to @mention you, and those posts won't show up here.",
],
private:"Private",
privateDescription:[
"The latest comment from ",
strong("private threads that include you"),
", sorted by recency. Private posts are encrypted for your public key, and have a maximum of 7 recipients. Recipients cannot be added after the thread has started. Select the timestamp to view the full thread.",
],
search:"Search",
imageSearch:"Image Search",
settings:"Settings",
// post actions
comment:"Comment",
subtopic:"Subtopic",
json:"JSON",
// relationships
unfollow:"Unfollow",
follow:"Follow",
block:"Block",
unblock:"Unblock",
newerPosts:"Newer posts",
olderPosts:"Older posts",
feedRangeEmpty:"The given range is empty for this feed. Try viewing the ",
seeFullFeed:"full feed",
feedEmpty:"The local client has never seen posts from this account.",
beginningOfFeed:"This is the beginning of the feed",
noNewerPosts:"No newer posts have been received yet.",
relationshipNotFollowing:"No one is following the other",
relationshipTheyFollow:"They follow you",
relationshipMutuals:"You are mutuals",
relationshipFollowing:"You are following",
relationshipYou:"This is you",
relationshipBlocking:"You are blocking",
relationshipBlockingPost:"This message hides content from a blocked user.",
relationshipNone:"You are neither following or blocking",
relationshipConflict:"You are somehow both following and blocking",
// author view
viewLikes:"View likes",
// likes view
likedBy:"'s likes",
// composer
attachFiles:"Attach files",
mentionsMatching:"Matching Mentions",
preview:"Preview",
publish:"Publish",
contentWarningPlaceholder:"Optional content warning for this post",
" below. This may be useful for prototyping or publishing messages that Oasis doesn't support. This message cannot be edited or deleted.",
],
commentWarning:[
" Published comments cannot be edited or deleted. To respond to an individual message, select ",
strong("subtopic"),
" instead.",
],
commentPublic:"public",
commentPrivate:"private",
commentLabel:({publicOrPrivate,markdownUrl})=>[
"Write a ",
strong(`${publicOrPrivate} comment`),
" on this thread with ",
a({href:markdownUrl},"Markdown"),
". Preview shows attached media.",
],
publishLabel:({markdownUrl,linkTarget})=>[
"Write a new public post in ",
a(
{
href:markdownUrl,
target:linkTarget,
},
"Markdown"
),
". Published posts cannot be edited or deleted. Preview to see attached media before publishing.",
],
publishCustomInfo:({href})=>[
"If you're an advanced user, you can also ",
a({href},"publish a custom message"),
".",
],
publishBasicInfo:({href})=>[
"If you're not an advanced user, you should ",
a({href},"publish a post"),
".",
],
publishCustom:"Publish custom",
subtopicLabel:({markdownUrl})=>[
"Create a ",
strong("public subtopic"),
" of this message with ",
a({href:markdownUrl},"Markdown"),
". Messages cannot be edited or deleted. To respond to an entire thread, select ",
strong("comment"),
" instead. Preview shows attached media.",
],
// settings
settingsIntro:({readmeUrl,version})=>[
`You're using Oasis ${version}. Check out `,
a({href:readmeUrl},"the readme"),
", configure your theme, or view debugging information below.",
],
theme:"Theme",
themeIntro:
"Choose from any theme you'd like. The default theme is Atelier-SulphurPool-Light. You can also set your theme in the default configuration file.",
setTheme:"Set theme",
language:"Language",
languageDescription:
"If you'd like to use Oasis in another language, choose one below. Please be aware that this is very new and very basic. We'd love your help translating Oasis to other languages and locales.",
setLanguage:"Set language",
status:"Status",
peerConnections:"Peer Connections 💻⚡️💻",
connectionsIntro:
"Your computer is syncing data with these other computers. It will connect to any scuttlebutt pub and peer it can find, even if you have no relationship with them, as it looks for data from your friends.",
noConnections:"No peers connected.",
connectionActionIntro:
"You can decide when you want your computer to network with peers. You can start, stop, or restart your networking whenever you'd like.",
startNetworking:"Start networking",
stopNetworking:"Stop networking",
restartNetworking:"Restart networking",
sync:"Connect and Sync",
indexes:"Indexes",
indexesDescription:
"Oasis keeps a cache of common calculations so that we can save time. Unfortunately this is a common source of bugs. Rebuilding your indexes is safe, and may fix some types of bugs.",
invites:"Invites",
invitesDescription:
"Redeem an invite by pasting it below. If it works, you'll follow the feed and they'll follow you back.",
acceptInvite:"Accept invite",
// search page
searchLabel:"Add word(s) to look for in downloaded messages.",
// image search page
imageSearchLabel:"Enter words to search for images labelled with them",
// posts and comments
commentDescription:({parentUrl})=>[
" commented on ",
a({href:parentUrl}," thread"),
],
commentTitle:({authorName})=>[`Comment on @${authorName}'s message`],
subtopicDescription:({parentUrl})=>[
" created a subtopic from ",
a({href:parentUrl}," a message"),
],
subtopicTitle:({authorName})=>[`Subtopic on @${authorName}'s message`],
"Posts from people in your network that reference this hashtag, sorted by recency.",
rebuildName:"Rebuild database indexes",
},
/* spell-checker: disable */
es:{
latest:"Novedades",
profile:"Mi Perfil",
search:"Buscar",
imageSearch:"Buscar fotos",
settings:"Configuración",
// navbar items
extended:"Red extendida",
extendedDescription:[
"Publicaciones de ",
strong("personas que no sigues"),
", ordenadas por las más recientes. Quando sigas a una persona, podrás descargar publicaciones de otras personas que ella sige, y esas publicaciones aparecerán aquí.",
],
popular:"Populares",
popularDescription:[
"Publicaciones de personas de tu red, ordenadas por cantidad de ",
strong("Me Gusta"),
" en determinados periodos. Se cuentan los Me Gusta de ",
em("todos"),
", incluidos aquellos que no sigues. Esta es una lista de las publicaciones más populares de tu red de contacto.",
],
latestDescription:
"Publicaciones de aquellos que sigues, ordenadas por las más recientes.",
topics:"Tópicos",
topicsDescription:[
strong("Tópicos"),
" de las personas que sigues, ordenados por los más recientes. Selecciona la hora de una publicación para leer el hilo completo.",
],
summaries:"Resumen",
summariesDescription:[
strong("Tópicos y algunos comentarios"),
" de las personas que sigues, ordenados por los más recientes. Selecciona la hora de una publicación para leer el hilo completo.",
],
threads:"Hilos",
threadsDescription:[
strong("Posts que tienen comentarios"),
" de personas que sigues y de tu red extendida, ordenados por los más recientes. Selecciona la hora de una publicación para leer el hilo completo.",
],
manualMode:"Modo manual",
mentions:"Menciones",
mentionsDescription:[
strong("Publicaciones de "),
strong("cualquier persona"),
" que te mencionan, ordenadas por las más recientes. Solo aparecen menciones en el formato @mención.",
],
private:"Privado",
privateDescription:[
"Los comentarios más recientes de ",
strong("hilos privados que te incluyen"),
". Las publicaciones privadas están cifradas para ti, y contienen un máximo de 7 destinatarios. No se podrán adicionar nuevos destinarios después que empieze el hilo. Selecciona la hora de una publicación para leer el hilo completo.",
],
// post actions
comment:"Comentar",
reply:"Responder",
subtopic:"Subhilo",
json:"JSON",
// relationships
unfollow:"Dejar de seguir",
follow:"Seguir",
relationshipNotFollowing:"Ustedes dos no se siguen",
relationshipTheyFollow:"Ellos te siguen",
relationshipMutuals:"Ustedes se siguen mutuamente",
relationshipFollowing:"Siguiendo",
relationshipYou:"Tú",
relationshipBlocking:"Bloqueado",
relationshipNone:"No estás siguiendo ni bloqueando",
relationshipConflict:"De alguna forma lo estás siguiendo y bloqueando",
// author view
viewLikes:"Ver Me Gusta",
// likes view
likedBy:"le gusta",
// composer
attachFiles:"Agregar archivos",
mentionsMatching:"Menciones coincidentes",
preview:"Vista previa",
publish:"Publicar",
contentWarningPlaceholder:"Advertencia opcional para esta publicación",
". Esto puede ser útil para prototipar o componer tipos de mensaje que Oasis aún no soporta. Este mensaje no podrá ser editado o borrado.",
],
commentWarning:[
" Los mensajes no podrán ser editados o borrados. Para responder a mensajes, selecciona ",
strong("Responder"),
".",
],
commentPublic:"público",
commentPrivate:"privado",
commentLabel:({publicOrPrivate,markdownUrl})=>[
"Escribe un ",
strong(`${publicOrPrivate} comentario`),
" con ",
a({href:markdownUrl},"Markdown"),
" en este hilo.",
],
publishLabel:({markdownUrl,linkTarget})=>[
"Escribe un mensaje público con ",
a(
{
href:markdownUrl,
target:linkTarget,
},
"Markdown"
),
". Los mensajes no podrán ser editados o borrados.",
],
publishCustomInfo:({href})=>[
"Si eres un usuario avanzado, puedes también ",
a({href},"publicar un mensaje avanzado"),
".",
],
publishBasicInfo:({href})=>[
"Si no eres un usuario avanzado, lo mejor es ",
a({href},"publicar un mensaje normal."),
".",
],
publishCustom:"Publicar avanzado",
replyLabel:({markdownUrl})=>[
"Escribe una ",
strong("respuesta pública"),
" a este mensaje con ",
a({href:markdownUrl},"Markdown"),
". Los mensajes no podrán ser editados o borrados. Para responder a todo un hilo, selecciona ",
strong("comentario"),
".",
],
// settings
settingsIntro:({readmeUrl,version})=>[
`Estás usando Oasis ${version}. Lee `,
a({href:readmeUrl},"el Readme"),
", configura un tema, o consulta información de debug abajo.",
],
theme:"Tema",
themeIntro:
"Elige un tema. Atelier-SulphurPool-Light es el tema por defecto.",
setTheme:"Elige el tema",
language:"Idioma",
languageDescription:
"Si queres usar Oasis en otro idioma, elige aquí. Atención, que esta funcionalidad es aún nueva y básica. Necesitamos ayuda con traducciones para otros idiomas y formatos.",
setLanguage:"Seleccionar idioma",
status:"Estado",
peerConnections:"Conexiones de pares 💻⚡️💻",
connectionsIntro:
"Tu computadora se está sincronizando con las siguientes computadoras. Se conectará con cualquier par de scuttlebutt que encuentre, aún si no los conoce, ya que podrían tener información de tus amigos.",
noConnections:"Sin pares conectados.",
connectionActionIntro:
"Podrás decidir cuándo conectar tu computadora a la red de pares. Podrás iniciar, detener o reiniciar las conexiones siempre que quieras.",
startNetworking:"Iniciar las conexiones",
stopNetworking:"Detener las conexiones",
restartNetworking:"Reiniciar las conexiones",
indexes:"Índices",
indexesDescription:
"Oasis mantiene una caché con datos precalculados para ahorrar tiempo. Lamentablemente, esto es a menudo una causa de errores. Reconstruir esta chaché puede solucionar algunos errores si se presentan.",
invites:"Invitaciones",
invitesDescription:
"Utiliza una invitación aquí. Si funciona, empezarás a seguir a esa persona y ella te seguirá a ti también.",
acceptInvite:"Aceptar la invitación",
// search page
searchLabel:
"Busca con palabras clave entre las publicaciones que tienes descargadas.",
// posts and comments
commentDescription:({parentUrl})=>[
" comentó en el hilo ",
a({href:parentUrl},""),
],
replyDescription:({parentUrl})=>[
" respondido al ",
a({href:parentUrl},"mensaje "),
],
// image search page
imageSearchLabel:
"Busca con palabras clave entre los títulos de las fotos que tienes descargadas.",
// posts and comments
commentTitle:({authorName})=>[
`Comentó en el mensaje de @${authorName}`,
],
subtopicDescription:({parentUrl})=>[
" creó un nuevo hilo para ",
a({href:parentUrl},"este mensaje"),
],
subtopicTitle:({authorName})=>[
`Nuevo hilo en el mensaje de @${authorName}`,
],
mysteryDescription:"publicó un mensaje misterioso",
// misc
oasisDescription:
"Una bonita interfaz para el amigable vecindario de scuttlebutt",
submit:"Enviar",
editProfile:"Editar perfil",
editProfileDescription:
"Edita tu perfil con Markdown. Los cambios serán publicados permanentemente, y habrá un registro de cada cambio disponeble públicamente, aunque la mayoría de las apps de scuttlebutt no muestran el historial.",
profileName:"Nombre de perfil (texto)",
profileImage:"Imagen de perfil",
profileDescription:"Descripción de perfil (Markdown)",
hashtagDescription:
"Publicaciones de personas en tu red que mencionan este hashtag, ordenadas por las más recientes.",
rebuildName:"Reconstruir índices de la base de datos",
},
de:{
extended:"Erweitert",
extendedDescription:[
"Beiträge von ",
strong("Leuten denen du nicht folgst"),
", sortiert nach Aktualität. Wenn du jemandem folgst lädst du eventuell auch Beiträge von Leuten herunter denen diese Person folgt, hier erscheinen diese Beiträge.",
],
popular:"Beliebt",
popularDescription:[
"Beiträge von Leuten in deinem Netzwerk, sortiert nach ",
strong("Herzen"),
" in der angegebenen Periode. Herzen werden von ",
em("jedem"),
" gezählt, auch von Personen denen du nicht folgst. D.h. hier werden Beiträge von deinen Freund*innen angezeigt die in deinem erweiterten Netzwerk populär sind.",
],
latest:"Aktuell",
latestDescription:
"Beiträge von Leuten denen du folgst, sortiert nach Aktualität.",
topics:"Themen",
topicsDescription:[
strong("Themen"),
" von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen.",
],
summaries:"Übersicht",
summariesDescription:[
strong("Themen und einige Kommentare"),
" von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen.",
],
profile:"Profil",
manualMode:"Manueller Modus",
mentions:"Erwähnungen",
mentionsDescription:[
strong("Beiträge in denen du erwähnt wirst"),
" von ",
strong("allen"),
", sortiert nach Aktualität. Manchmal vergessen Leute dich zu @erwähnen, diese Beiträge werden hier nicht erscheinen.",
],
private:"Privat",
privateDescription:[
"Die letzten Kommentare aus ",
strong("privaten Threads die dich beinhalten"),
", sortiert nach Aktualität. Private Beiträge werden mit deinem öffentlichen Schlüssel verschlüsselt und haben maximal 7 Empfänger*innen. Empfänger*innen können nicht hinzugefügt werden nachdem ein Thread gestartet wurde. Klicke auf den Zeitstämpel um einen komplette Thread anzuzeigen.",
],
search:"Suche",
settings:"Einstellungen",
// post actions
comment:"Kommentieren",
reply:"Antworten",
json:"JSON",
// relationships
unfollow:"Entfolgen",
follow:"Folgen",
relationshipNotFollowing:"No one is following the other",
relationshipTheyFollow:"They follow you",
relationshipMutuals:"You are mutuals",
relationshipFollowing:"Du folgst",
relationshipYou:"Das bist du",
relationshipBlocking:"Du blockierst",
relationshipNone:"Weder folgst noch blockst du",
relationshipConflict:"Irgendwie folgst und blockst du gleichzeitig",
// author view
viewLikes:"Likes ansehen",
// likes view
likedBy:"'s Likes",
// composer
attachFiles:"Datei Hinzufügen",
mentionsMatching:"Matching Mentions",
preview:"Vorschau",
publish:"Veröffentlichen",
contentWarningPlaceholder:"Optionale Inhaltswarnung für diesen Beitrag",
publishCustomDescription:[
"Veröffentliche eine benutzerdefinierte Nachricht durch das Eingeben von ",
" unten. Dies kann zum Prototyping oder dem veröffentlichen von Nachrichten die Oasis nicht unterstützt nützlich sein. Diese Nachricht kann nicht bearbeitet oder gelöscht werden.",
],
commentWarning:[
" Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf eine einzelne Nachricht zu antworten, wähle ",
strong("antworten"),
" stattdessen.",
],
commentPublic:"öffentlichen",
commentPrivate:"privaten",
commentLabel:({publicOrPrivate,markdownUrl})=>[
"Verfasse einen ",
strong(`${publicOrPrivate} Kommentar`),
" in diesem Thread mit ",
a({href:markdownUrl},"Markdown"),
".",
],
publishLabel:({markdownUrl,linkTarget})=>[
"Verfasse einen neuen öffentlichen Beitrag in ",
a(
{
href:markdownUrl,
target:linkTarget,
},
"Markdown"
),
". Beiträge können nicht bearbeitet oder gelöscht werden.",
],
publishCustomInfo:({href})=>[
"Wenn du ein erfahrener Benutzer bist kannst du auch ",
". Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf einen kompletten Thread zu antworten, klicke auf ",
strong("kommentieren"),
" stattdessen.",
],
// settings
settingsIntro:({readmeUrl,version})=>[
`Du verwendest Oasis ${version}. Lese `,
a({href:readmeUrl},"die Readme"),
", konfiguriere dein Theme oder schaue dir Debugging-Informationen weiter unten an.",
],
theme:"Theme",
themeIntro:
"Wähle ein Theme das dir gefällt. Das Standard-Theme ist Atelier-SulphurPool-Light.",
setTheme:"Theme einstellen",
language:"Sprache",
languageDescription:
"Wenn du Oasis in einer anderen Sprache nutzen möchtest, wähle unten eine aus. Bitte beachte, dass dies sehr neu und noch am Anfang ist. Wir freuen uns über deine Hilfe bei der Übersetzung von Oasis in andere Sprachen.",
setLanguage:"Sprache einstellen",
status:"Status",
peerConnections:"Verbindungen zu Peers 💻⚡️💻",
connectionsIntro:
"Dein Computer synchronisiert Daten mit diesen anderen Computern. Auf der Suche nach Daten von deinen Freund*innen werden Verbindungen zu allen Scuttlebutt Pubs und Peers aufgenommen die gefunden werden, auch wenn du keine Beziehung mit diesen hast.",
noConnections:"Keine Peers verbunden.",
connectionActionIntro:
"Du kannst entscheiden wann dein Computer mit Peers netzwerken soll. Du kannst das Netzwerken starten, stoppen oder neustarten wann immer du willst.",
startNetworking:"Netzwerken starten",
stopNetworking:"Netzwerken stoppen",
restartNetworking:"Netzwerken neustarten",
indexes:"Indizes",
invites:"Einladungen",
invitesDescription:
"Löse eine Einladung durch einfügen unten ein. Wenn es geklappt hat wirst du dem Feed folgen und sie werden dir folgen.",
acceptInvite:"Einladung annehmen",
// search page
searchLabel:
"Füge Wörte hinzu nach denen in heruntergeladenen Nachrichten gesucht werden soll.",
// posts and comments
commentDescription:({parentUrl})=>[
" kommentierte auf ",
a({href:parentUrl}," Thread"),
],
replyDescription:({parentUrl})=>[
" antwortete auf ",
a({href:parentUrl}," Nachricht"),
],
mysteryDescription:"veröffentlichte eine mysteriöse Nachricht",
"Bearbeite dein Profil mit Markdown. Nachrichten können nicht bearbeitet oder gelöscht werden. Alte Versionen deiner Profilinformationen bleiben existieren und sind öffentliche Informationen, aber die meisten SSB-Apps zeigen diese nicht an.",
"Beiträge von Leuten in deinem Netzwerk die dieses Hashtag referenzieren, sortiert nach Aktualität.",
},
it:{
// navbar items
extended:"Rete estesa",
extendedDescription:[
"Cronologia dei post scritti da ",
strong("persone che non segui"),
". Quando segui qualcuno potresti scaricare post pubblicati da persone a te estranee ma seguite da loro, e questi messaggi compariranno qui.",
],
popular:"In evidenza",
popularDescription:[
"Posts dalle persone nel tuo network, ordinati per ",
strong("like"),
" in un dato periodo. I like vengono contati tra i post di ",
em("chiunque"),
", incluse le persone che non segui, quindi qui vedrai i post dei tuoi amici che sono più popolari nella tua rete estesa.",
],
latest:"Novità",
latestDescription:
"Post scritti da te e dalle persone che segui, ordinati per data.",
topics:"Argomenti",
topicsDescription:[
strong("Argomenti"),
" scritti da te e dalle persone che segui, ordinati per data. Seleziona il timestamp di un post per visualizzarne il thread.",
],
summaries:"Riassunti",
summariesDescription:[
strong("Argomenti e qualche commento"),
" scritto da te e dalle persone che segui, in ordine cronologico. Clicca il timestamp di un post per visualizzarne il thread.",
],
threads:"Thread",
threadsDescription:[
strong("Posts che hanno commenti"),
" scritti da persone che segui e dalla tua rete estesa, ordinati cronologicamente. from people you follow and your extended network, sorted by recency. Clicca il timestamp di un post per visualizzarne il thread.",
],
profile:"Profilo",
manualMode:"Modalità manuale",
mentions:"Menzioni",
mentionsDescription:[
strong("Post che ti menzionano,"),
" scritti da ",
strong("chiunque"),
", ordinati cronologicamente. A volte le persone dimenticano di @menzionarti, in quel caso i loro post non verranno mostrati qui.",
],
private:"Privato",
privateDescription:[
"Gli ultimi commenti su ",
strong("thread privati di cui fai parte"),
", ordinati cronologicamente. I post privati sono crittati con la tua chiave pubblica, e possono avere un massimo di 7 destinatari. Non è possibile aggiungere destinatari una volta che il thread è iniziato. Clicca il timestamp di un post per visualizzarne il thread.",
],
search:"Cerca",
settings:"Impostazioni",
// post actions
comment:"Commenta",
reply:"Rispondi",
json:"JSON",
// relationships
unfollow:"Non seguire più",
follow:"Segui",
relationshipNotFollowing:"No one is following the other",
relationshipTheyFollow:"They follow you",
relationshipMutuals:"You are mutuals",
relationshipFollowing:"Stai seguendo",
relationshipYou:"Sei tu",
relationshipBlocking:"Stai bloccando",
relationshipNone:"Non stai né seguendo né bloccando",
relationshipConflict:
"In qualche modo non meglio precisato stai seguendo e bloccando allo stesso tempo",
// author view
viewLikes:"Visualizza like",
// likes view
likedBy:"Like di ",// here the subject of the sentence should be put at the end (as if it were "liked by X" instead of "X's likes"
// composer
attachFiles:"Aggiungere i file",
mentionsMatching:"Menzioni corrispondenti",
preview:"Visualizza l'anteprima",
publish:"Pubblica",
contentWarningPlaceholder:
"Avviso su possibili contenuti per adulti nel post, opzionale",
publishCustomDescription:[
"Pubblica un messaggio su misura inserendo dati ",
" qui sotto. Torna utile per realizzare prototipi o per pubblicare post ancora non supportati da Oasis. Questo messaggio non potrà essere modificato né rimosso.",
],
commentWarning:[
" I commenti non possono essere modificati né rimossi. Per rispondere ad un messaggio, seleziona ",
strong("rispondi"),
" invece.",
],
commentPublic:"pubblico",
commentPrivate:"privato",
commentLabel:({publicOrPrivate,markdownUrl})=>[
"Scrivi un ",
strong(`${publicOrPrivate} commento`),
" su questo thread con ",
a({href:markdownUrl},"Markdown"),
".",
],
publishLabel:({markdownUrl,linkTarget})=>[
"Scrivi un post pubblico in ",
a(
{
href:markdownUrl,
target:linkTarget,
},
"Markdown"
),
". I post non possono essere modificati né rimossi.",
],
publishCustomInfo:({href})=>[
"Se sei uno smanettone puoi anche ",
a({href},"pubblicare un messaggio su misura"),
".",
],
publishBasicInfo:({href})=>[
"Se non sei uno smanettone ti consigliamo di ",
a({href},"pubblicare un post"),
".",
],
publishCustom:"Pubblica su misuram",
replyLabel:({markdownUrl})=>[
"Scrivi una ",
strong("risposta pubblica"),
" a questo messaggio con ",
a({href:markdownUrl},"Markdown"),
". I messaggi non possono essere modificati né rimossi. Per rispondere ad un intero thread seleziona ",
strong("commenta"),
" invece.",
],
// settings
settingsIntro:({readmeUrl,version})=>[
`Stai usando Oasis ${version}. Dai un'occhiata al `,
a({href:readmeUrl},"file readme"),
", configura il tuo tema o leggi i log di debugging qui sotto.",
],
theme:"Tema",
themeIntro:
"Scegli il tema che più ti piace. Il tema predefinito si chiama Atelier-SulphurPool-Light. Puoi anche selezionare il tuo tema nel file di configurazione.",
setTheme:"Set theme",
language:"Lingua",
languageDescription:
"Se vuoi utilizzare Oasis in un'altra lingua, puoi sceglierla qui sotto. Attenzione, è una nuova funzionalità e la traduzione potrebbe non essere perfetta. Se parli una lingua straniera ci piacerebbe che ci aiutassi a tradurre Oasis in altre lingue.",
setLanguage:"Seleziona lingua",
status:"Stato attuale",
peerConnections:"Connessioni coi peer 💻⚡️💻",
connectionsIntro:
"Il tuo computer sincronizza i dati con questi computer e si connetterà a qualsiasi pub o utente scuttlebutt con cui riesce ad entrare in contatto. Dato che preleva dati dalla tua cerchia di amici, potresti vedere dati scritti da utenti che non conosci.",
noConnections:"Nessun peer connesso.",
connectionActionIntro:
"Puoi decidere se vuoi che il tuo computer entri in contatto con peer connessi alla stessa intranet in cui ti trovi ora. Puoi far partire, fermare o far ripartire questo tipo di connessioni quando vuoi.",
startNetworking:"Inizia networking locale",
stopNetworking:"Ferma networking locale",
restartNetworking:"Ricarica networking locale",
indexes:"Indici",
invites:"Inviti",
invitesDescription:
"Utilizza un invito che hai ricevuto incollandolo qui sotto. Se viene accettato ne seguirai il feed e ne riceverai il follow.",
acceptInvite:"Accetta l'invito",
// search page
searchLabel:"Cerca tra i messaggi che hai scaricato.",
// posts and comments
commentDescription:({parentUrl})=>[
" ha commentato il ",
a({href:parentUrl}," thread"),
],
replyDescription:({parentUrl})=>[
" ha risposto al ",
a({href:parentUrl}," messaggio "),
],
mysteryDescription:"ha postato un messaggio misterioso",
// misc
oasisDescription:"Interfaccia per scuttlebutt facile da usare",
submit:"Vai",
editProfile:"Modifica profilo",
editProfileDescription:
"Modifica il tuo profilo usando Markdown. Le versioni precedenti del tuo profilo continueranno ad esistere e non possono essere eliminate, ma la maggior parte dei client per SSB non le mostreranno.",
profileName:"Nome profilo (testo non formattato)",
profileImage:"Immagine di profilo",
profileDescription:"Descrizione del profilo (Markdown)",
hashtagDescription:
"Post da persone nella tua rete che menzionano questo hashtag, ordinati cronologicamente.",
},
fr:{
// navbar items
extended:"Étendue",
extendedDescription:[
"Messages de ",
strong("quelqu'un que vous ne suivez pas"),
", triées ancienneté. Lorsque vous suivez quelqu'un, vous pouvez télécharger les messages des personnes qu'il suit, et ces messages apparaissent ici.",
],
popular:"Populaire",
popularDescription:[
"Messages des personnes de votre réseau, triés par ",
strong("votes"),
" dans une période donnée. Les votes sont comptés à partir de ",
em("tous"),
", y compris les personnes que vous ne suivez pas, donc cela montre les messages de vos amis qui sont populaires dans votre réseau étendu.",
],
latest:"Dernières nouvelles",
latestDescription:
"Les messages de vous-même et des personnes que vous suivez, triés par ancienneté.",
topics:"Sujets",
topicsDescription:[
strong("Sujets"),
" de vous-même et des personnes que vous suivez, classés par ancienneté. Sélectionnez l'horodatage de n'importe quel message pour voir le reste du fil de discussion.",
],
summaries:"Résumés",
summariesDescription:[
strong("Sujets et commentaires"),
" de vous-même et des personnes que vous suivez, classés par ancienneté. Sélectionnez l'horodatage de n'importe quel message pour voir le reste du fil de discussion.",
],
threads:"Fils de discussion",
threadsDescription:[
strong("Messages avec commentaires"),
" des personnes que vous suivez et de votre réseau étendu, classés par ancienneté. Sélectionnez l'horodatage de n'importe quel message pour voir le reste du fil de discussion.",
],
profile:"Profil",
manualMode:"Mode Manuel",
mentions:"Mentions",
mentionsDescription:[
strong("Postes qui vous mentionnent"),
" from ",
strong("n'importe qui"),
" triées par ancienneté. Parfois, les gens peuvent oublier de vous @mentionner, et ces messages n'apparaîtront pas ici.",
],
private:"Privé",
privateDescription:[
"Le dernier commentaire de ",
strong("des fils de discussion privés qui vous incluent"),
", triées par ancienneté. Les messages privés sont cryptés par clé publique, et ont un maximum de 7 destinataires. Les destinataires ne peuvent pas être ajoutés après le démarrage du fil de discussion. Sélectionnez l'horodatage pour voir le fil de discussion complet.",
],
search:"Rechercher",
settings:"Paramètres",
// post actions
comment:"Commentaire",
reply:"Réponse",
json:"JSON",
// relationships
unfollow:"Ne plus suivre",
follow:"Suivre",
block:"Bloquer",
unblock:"Débloquer",
relationshipNotFollowing:"No one is following the other",
relationshipTheyFollow:"Il/elle te suivent",
relationshipMutuals:"Vous êtes des mutuellements",
relationshipFollowing:"Vous suivez",
relationshipYou:"C'est vous",
relationshipBlocking:"Vous bloquez",
relationshipNone:"Vous ne suivez ni ne bloquez",
relationshipConflict:
"D'une certaine manière, vous suivez et bloquez à la fois",
// author view
viewLikes:"Voir les votes",
// likes view
likedBy:"a voté",
// composer
attachFiles:"Ajouter des fichiers",
mentionsMatching:"Matching Mentions",
preview:"Examiner",
publish:"Publier",
contentWarningPlaceholder:
"Avertissement de contenu facultatif pour ce poste",
" ci-dessous. Cela peut être utile pour le prototypage ou la publication de messages qu'Oasis ne prend pas en charge. Ce message ne peut pas être modifié ou supprimé.",
],
commentWarning:[
" Les commentaires ne peuvent être ni modifiés ni supprimés. Pour répondre à un message individuel, sélectionnez ",
strong("répondre"),
" à la place.",
],
commentPublic:"public",
commentPrivate:"privé",
commentLabel:({publicOrPrivate,markdownUrl})=>[
"Ecrire un ",
strong(`${publicOrPrivate} commentaire`),
" sur ce fil avec ",
a({href:markdownUrl},"Markdown"),
".",
],
publishLabel:({markdownUrl,linkTarget})=>[
"Rédiger un nouveau poste public dans ",
a(
{
href:markdownUrl,
target:linkTarget,
},
"Markdown"
),
". Les messages ne peuvent être ni modifiés ni supprimés.",
],
publishCustomInfo:({href})=>[
"Si vous êtes un utilisateur avancé, vous pouvez également ",
a({href},"publier un message personnalisé"),
".",
],
publishBasicInfo:({href})=>[
"Si vous n'êtes pas un utilisateur avancé, vous devez ",
a({href},"publier un message"),
".",
],
publishCustom:"Publier un type particulier",
replyLabel:({markdownUrl})=>[
"Ecrire un ",
strong("réponse publique"),
" à ce message avec ",
a({href:markdownUrl},"Markdown"),
". Les messages ne peuvent être ni modifiés ni supprimés. Pour répondre à un fil de discussion entier, sélectionnez ",
strong("commentaire"),
" en remplacement.",
],
// settings
settingsIntro:({readmeUrl,version})=>[
`Vous utilisez Oasis ${version}. Consultez `,
a({href:readmeUrl},"le lisez-moi"),
", configurez votre thème, ou consultez les informations de débogage ci-dessous.",
],
theme:"Thème",
themeIntro:
"Choisissez le thème que vous souhaitez. Le thème par défaut est Atelier-SulphurPool-Light. Vous pouvez également définir votre thème dans le fichier de configuration par défaut.",
setTheme:"Choisir thème",
language:"Langue",
languageDescription:
"Si vous souhaitez utiliser Oasis dans une autre langue, choisissez l'une des langues ci-dessous. Sachez qu'il s'agit d'une langue très nouvelle et très basique. Nous aimerions que vous nous aidiez à traduire Oasis dans d'autres langues et d'autres lieux.",
setLanguage:"Choisir la langue",
status:"Status",
peerConnections:"Connexions pair à pair 💻⚡️💻",
connectionsIntro:
"Votre ordinateur synchronise les données avec ces autres ordinateurs. Il se connecte à n'importe quel pub de ragots et peer-to-peer qu'il peut trouver, même si vous n'avez pas de relation avec eux, en cherchant des données de vos amis.",
noConnections:"Aucun pair n'est connecté.",
connectionActionIntro:
"Vous pouvez décider quand vous voulez que votre ordinateur soit en réseau avec vos pairs. Vous pouvez démarrer, arrêter ou redémarrer votre réseau quand vous le souhaitez.",
startNetworking:"Activer le réseau",
stopNetworking:"Désactiver le réseau",
restartNetworking:"Redémarrer le réseau",
indexes:"Index",
invites:"Invitations",
invitesDescription:
"Utilisez une invitation en la collant ci-dessous. Si cela fonctionne, vous suivrez le flux et ils vous suivront en retour.",
acceptInvite:"Accepter l'invitation",
// search page
searchLabel:
"Ajouter un ou plusieurs mots à rechercher dans les messages téléchargés.",
// posts and comments
commentDescription:({parentUrl})=>[
" a commenté ",
a({href:parentUrl}," fil de discussion"),
],
replyDescription:({parentUrl})=>[
" a répondu à ",
a({href:parentUrl}," message"),
],
mysteryDescription:"a posté un message mystérieux",
// misc
oasisDescription:"Une interface conviviale pour des bavardages entre amis",
submit:"Soumettre",
editProfile:"Modifier le profil",
editProfileDescription:
"Modifiez votre profil en Markdown. Les anciennes versions des informations de votre profil existent toujours et ne peuvent pas être supprimées, mais la plupart des applications SSB ne les affichent pas.",
profileName:"Nom du profil (texte en clair)",
profileImage:"Image du profil",
profileDescription:"Description du profil (Markdown)",
hashtagDescription:
"Les messages des personnes de votre réseau qui font référence à ce hashtag, triés par ordre de récence.",