Finish MVP of remoteStorage service pages/UI #202

Merged
greg merged 7 commits from feature/rs_service_page into master 2024-08-17 12:33:49 +00:00
21 changed files with 220 additions and 31 deletions

View File

@ -61,7 +61,7 @@ gem "sentry-rails"
# Services # Services
gem 'discourse_api' gem 'discourse_api'
gem "lnurl" gem "lnurl"
gem 'manifique' gem 'manifique', '~> 1.1.0'
gem 'nostr', '~> 0.6.0' gem 'nostr', '~> 0.6.0'
group :development, :test do group :development, :test do

View File

@ -245,7 +245,7 @@ GEM
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
manifique (1.0.1) manifique (1.1.0)
faraday (~> 2.9.0) faraday (~> 2.9.0)
faraday-follow_redirects (= 0.3.0) faraday-follow_redirects (= 0.3.0)
nokogiri (~> 1.16.0) nokogiri (~> 1.16.0)
@ -515,7 +515,7 @@ DEPENDENCIES
listen (~> 3.2) listen (~> 3.2)
lnurl lnurl
lockbox lockbox
manifique manifique (~> 1.1.0)
net-ldap net-ldap
nostr (~> 0.6.0) nostr (~> 0.6.0)
pagy (~> 6.0, >= 6.0.2) pagy (~> 6.0, >= 6.0.2)

View File

@ -8,8 +8,7 @@ class Services::RemotestorageController < Services::BaseController
# unless current_user.service_enabled?(:remotestorage) # unless current_user.service_enabled?(:remotestorage)
# redirect_to service_remotestorage_info_path # redirect_to service_remotestorage_info_path
# end # end
@rs_auths = current_user.remote_storage_authorizations # @rs_apps_connected = current_user.remote_storage_authorizations.any?
# TODO sort by app name
end end
private private

View File

@ -3,7 +3,12 @@ class Services::RsAuthsController < Services::BaseController
before_action :require_feature_enabled before_action :require_feature_enabled
before_action :require_service_available before_action :require_service_available
# before_action :require_service_enabled # before_action :require_service_enabled
before_action :find_rs_auth before_action :find_rs_auth, only: [:destroy, :launch_app]
def index
@rs_auths = current_user.remote_storage_authorizations
# TODO sort by app name?
end
def destroy def destroy
@auth.destroy! @auth.destroy!

View File

@ -4,7 +4,7 @@ class WellKnownController < ApplicationController
def nostr def nostr
http_status :unprocessable_entity and return if params[:name].blank? http_status :unprocessable_entity and return if params[:name].blank?
domain = request.headers["X-Forwarded-Host"].presence || Setting.primary_domain domain = request.headers["X-Forwarded-Host"].presence || Setting.primary_domain
relay_url = Setting.nostr_relay_url relay_url = Setting.nostr_relay_url.presence
if params[:name] == "_" if params[:name] == "_"
# pubkey for the primary domain without a username (e.g. kosmos.org) # pubkey for the primary domain without a username (e.g. kosmos.org)

View File

@ -43,7 +43,7 @@
</p> </p>
<p> <p>
We have run two 6-month trials so far, with the next trial period We have run two 6-month trials so far, with the next trial period
starting sometime in Q2 2024. Watch your email for notifications about it! starting sometime soon. Watch your email for notifications about it!
</p> </p>
</section> </section>
<% end %> <% end %>

View File

@ -41,15 +41,16 @@
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<% if Setting.discourse_enabled? %> <% if Setting.remotestorage_enabled? &&
Flipper.enabled?(:remotestorage, current_user) %>
<div class="border border-gray-300 rounded-md hover:border-gray-400 <div class="border border-gray-300 rounded-md hover:border-gray-400
bg-[length:80%] bg-center bg-no-repeat bg-[length:80%] bg-[center_top_-156px] bg-no-repeat
bg-[url(/img/logos/icon_discourse.svg)]"> bg-[url(/img/logos/icon_remotestorage.svg)]">
<%= link_to "#{Setting.discourse_public_url}/session/sso?return_path=/", <%= link_to services_storage_path,
class: "block h-full px-6 py-6 rounded-md" do %> class: "block h-full px-6 py-6 rounded-md" do %>
<h3 class="mb-3.5">Discourse</h3> <h3 class="mb-3.5">Storage</h3>
<p class="text-gray-600"> <p class="text-gray-600">
Community forums and support/help site Sync your data between apps and devices
</p> </p>
<% end %> <% end %>
</div> </div>
@ -67,16 +68,15 @@
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
<% if Setting.remotestorage_enabled? && <% if Setting.discourse_enabled? %>
Flipper.enabled?(:remotestorage, current_user) %>
<div class="border border-gray-300 rounded-md hover:border-gray-400 <div class="border border-gray-300 rounded-md hover:border-gray-400
bg-[length:80%] bg-[center_top_-156px] bg-no-repeat bg-[length:80%] bg-center bg-no-repeat
bg-[url(/img/logos/icon_remotestorage.svg)]"> bg-[url(/img/logos/icon_discourse.svg)]">
<%= link_to services_storage_path, <%= link_to "#{Setting.discourse_public_url}/session/sso?return_path=/",
class: "block h-full px-6 py-6 rounded-md" do %> class: "block h-full px-6 py-6 rounded-md" do %>
<h3 class="mb-3.5">Storage</h3> <h3 class="mb-3.5">Discourse</h3>
<p class="text-gray-600"> <p class="text-gray-600">
Sync your data between apps and devices Community forums and support/help site
</p> </p>
<% end %> <% end %>
</div> </div>

View File

@ -2,15 +2,143 @@
<%= render MainSimpleComponent.new do %> <%= render MainSimpleComponent.new do %>
<section> <section>
<h3 class="mb-10">Connected Apps</h3> <p class="mb-6">
<% if @rs_auths.any? %> Store and synchronize your app data across different devices.
<div class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-y-10 gap-x-12"> </p>
<% @rs_auths.each do |auth| %> </section>
<%= render RsAuthComponent.new(auth: auth) %>
<% end %> <%= render partial: "shared/tabnav_remotestorage" %>
<section>
<h3>Your Storage Address</h3>
<p class="mb-6">
In order to connect an app to your storage account, give it your address:
</p>
<p data-controller="clipboard" class="flex gap-1 sm:w-2/5">
<input type="text" id="user_address" class="grow"
value=<%= current_user.address %> disabled="disabled"
data-clipboard-target="source" />
<button id="copy-user-address" class="btn-md btn-icon btn-outline shrink-0"
data-clipboard-target="trigger" data-action="clipboard#copy"
title="Copy to clipboard">
<span class="content-initial">
<%= render partial: "icons/copy", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
</span>
<span class="content-active hidden">
<%= render partial: "icons/check", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
</span>
</button>
</p>
</section>
<section>
<h3>Recommended Apps</h3>
<div data-controller="tabs"
data-tabs-active-tab-class="-mb-px border-gray-200 border-l border-t border-r rounded-t text-indigo-600 hover:text-indigo-600"
data-tabs-inactive-tab-class="text-gray-500 hover:text-gray-700"
class="mb-12">
<select data-action="tabs#change" data-tabs-target="select"
class="block w-full mb-8 sm:hidden">
<option>Productivity</option>
<option>Bookmarks</option>
<option>Reading</option>
<option>File sharing</option>
<option>Learning</option>
</select>
<ul class="hidden sm:flex list-reset mb-8 border-gray-200 border-b">
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
Productivity
</a>
</li>
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
Bookmarks
</a>
</li>
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
Reading
</a>
</li>
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
File sharing
</a>
</li>
<li class="mr-2" data-tabs-target="tab" data-action="click->tabs#change:prevent">
<a href="#" class="bg-white inline-block py-2 px-4 font-semibold no-underline">
Learning
</a>
</li>
</ul>
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
<%= render AppInfoComponent.new(
name: "Hyperdraft",
description: "Create text notes and (optionally) turn them into a website",
icon_path: "/img/app_icons/hyperdraft.png",
links: [
["Web App", "https://hyperdraft.rosano.ca"],
]
) %>
<%= render AppInfoComponent.new(
name: "Notes Together",
description: "A powerful note-taking app, with support for attaching images and other files",
icon_path: "/img/app_icons/notes-together.png",
links: [
["Web App", "https://notestogether.hominidsoftware.com"],
]
) %>
<%= render AppInfoComponent.new(
name: "Papiers",
description: "A simple note-taking app",
icon_path: "/img/app_icons/papiers.png",
links: [
["Web App", "https://papiers.gitlab.io"],
]
) %>
</div>
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
<%= render AppInfoComponent.new(
name: "Webmarks",
description: "Archive your bookmarks in your remote storage",
icon_path: "/img/app_icons/webmarks.png",
links: [
["Web App", "https://webmarks.5apps.com"],
]
) %>
</div>
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
<%= render AppInfoComponent.new(
name: "Pétrolette",
description: "A news aggregator that syncs with your remote storage",
icon_path: "/img/app_icons/petrolette.png",
links: [
["Web App", "https://petrolette.space"],
]
) %>
</div>
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
<%= render AppInfoComponent.new(
name: "Sharesome",
description: "Quickly and easily share files from your remote storage",
icon_path: "/img/app_icons/sharesome.png",
links: [
["Web App", "https://sharesome.5apps.com"],
]
) %>
</div>
<div class="hidden grid grid-cols-1 gap-6" data-tabs-target="panel">
<%= render AppInfoComponent.new(
name: "Kommit",
description: "Create flashcards and learn them with spaced-repetition",
icon_path: "/img/app_icons/kommit.png",
links: [
["Web App", "https://kommit.rosano.ca"],
]
) %>
</div>
</div> </div>
<% else %>
<p>No apps connected yet.</p>
<% end %>
</section> </section>
<% end %> <% end %>

View File

@ -0,0 +1,33 @@
<%= render HeaderComponent.new(title: "Storage") %>
<%= render MainSimpleComponent.new do %>
<section>
<p class="mb-6">
Store and synchronize your app data across different devices.
</p>
</section>
<%= render partial: "shared/tabnav_remotestorage" %>
<section>
<% if @rs_auths.any? %>
<div class="w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-y-10 gap-x-12 mt-4">
<% @rs_auths.each do |auth| %>
<%= render RsAuthComponent.new(auth: auth) %>
<% end %>
</div>
<% else %>
<div class="text-center">
<p class="mt-4 mb-12 inline-flex align-center items-center">
<%= image_tag("/img/illustrations/undraw_friends_r511.svg", class: 'h-48') %>
</p>
<h3>
No apps connected
</h3>
<p class="text-gray-500">
When connected, your apps will show up here.
</p>
</div>
<% end %>
</section>
<% end %>

View File

@ -0,0 +1,14 @@
<section>
<div class="border-b border-gray-200">
<nav class="-mb-px flex" aria-label="Tabs">
<%= render TabnavLinkComponent.new(
name: "Info", path: services_storage_path,
active: current_page?(services_storage_path)
) %>
<%= render TabnavLinkComponent.new(
name: "Connected Apps", path: apps_services_storage_path,
active: current_page?(apps_services_storage_path)
) %>
</nav>
</div>
</section>

View File

@ -48,7 +48,8 @@ Rails.application.routes.draw do
end end
resource :storage, controller: 'remotestorage', only: [:show] do resource :storage, controller: 'remotestorage', only: [:show] do
resources :rs_auths, only: [:destroy] do get :apps, to: "rs_auths#index"
resources :rs_auths, only: [:index, :destroy] do
member do member do
get :revoke, to: 'rs_auths#destroy' get :revoke, to: 'rs_auths#destroy'
get :launch_app get :launch_app

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -47,6 +47,10 @@ RSpec.describe "Well-known URLs", type: :request do
end end
context "without relay configured" do context "without relay configured" do
before do
Setting.nostr_relay_url = ""
end
it "does not include a recommended relay" do it "does not include a recommended relay" do
get "/.well-known/nostr.json?name=bobdylan" get "/.well-known/nostr.json?name=bobdylan"
res = JSON.parse(response.body) res = JSON.parse(response.body)

View File

@ -4,6 +4,10 @@ RSpec.describe NostrManager::PublishZapReceipt, type: :model do
let(:user) { create :user, ln_account: "123456abcdef" } let(:user) { create :user, ln_account: "123456abcdef" }
let(:zap) { create :zap, user: user } let(:zap) { create :zap, user: user }
before do
Setting.nostr_relay_url = ""
end
describe "Default/delayed execution" do describe "Default/delayed execution" do
it "publishes zap receipts to all requested relays" do it "publishes zap receipts to all requested relays" do
expect(NostrPublishEventJob).to receive(:perform_later) expect(NostrPublishEventJob).to receive(:perform_later)