]> Untitled Git - lemmy.git/commitdiff
Add a simple linked instances page. Fixes #1070 (#1071)
authorDessalines <dessalines@users.noreply.github.com>
Tue, 11 Aug 2020 14:58:32 +0000 (10:58 -0400)
committerGitHub <noreply@github.com>
Tue, 11 Aug 2020 14:58:32 +0000 (10:58 -0400)
* Add a simple linked instances page. Fixes #1070

* Changing allowed_instances to federated_instances.

14 files changed:
server/lemmy_utils/src/settings.rs
server/src/api/site.rs
server/src/apub/mod.rs
server/src/routes/index.rs
ui/src/components/admin-settings.tsx
ui/src/components/footer.tsx
ui/src/components/instances.tsx [new file with mode: 0644]
ui/src/components/main.tsx
ui/src/components/navbar.tsx
ui/src/components/post.tsx
ui/src/components/user.tsx
ui/src/index.tsx
ui/src/interfaces.ts
ui/translations/en.json

index 6a566de7ef2b38afd341fcf73f158fda2a208362..55b6cbb32a9bd4543ec8718b67bd6fd500ed8cfe 100644 (file)
@@ -133,6 +133,20 @@ impl Settings {
     fs::read_to_string(CONFIG_FILE)
   }
 
+  pub fn get_allowed_instances(&self) -> Vec<String> {
+    let mut allowed_instances: Vec<String> = self
+      .federation
+      .allowed_instances
+      .split(',')
+      .map(|d| d.to_string())
+      .collect();
+
+    // The defaults.hjson config always returns a [""]
+    allowed_instances.retain(|d| !d.eq(""));
+
+    allowed_instances
+  }
+
   pub fn save_config_file(data: &str) -> Result<String, Error> {
     fs::write(CONFIG_FILE, data)?;
 
index dcbd621677bdb44cbd14d5160cd1cfbda3f08c04..515c3e5bed0649f284566424546041edce915d55 100644 (file)
@@ -130,6 +130,7 @@ pub struct GetSiteResponse {
   pub online: usize,
   version: String,
   my_user: Option<User_>,
+  federated_instances: Vec<String>,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -433,6 +434,7 @@ impl Perform for Oper<GetSite> {
       online,
       version: version::VERSION.to_string(),
       my_user,
+      federated_instances: Settings::get().get_allowed_instances(),
     })
   }
 }
@@ -659,6 +661,7 @@ impl Perform for Oper<TransferSite> {
       online: 0,
       version: version::VERSION.to_string(),
       my_user: Some(user),
+      federated_instances: Settings::get().get_allowed_instances(),
     })
   }
 }
index f67471982592e9b05d25e987800eb419a16eb18a..491762680a5c7447e722910a887f5839facf7116 100644 (file)
@@ -76,12 +76,8 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
     return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into());
   }
 
-  let mut allowed_instances: Vec<String> = Settings::get()
-    .federation
-    .allowed_instances
-    .split(',')
-    .map(|d| d.to_string())
-    .collect();
+  let mut allowed_instances: Vec<String> = Settings::get().get_allowed_instances();
+
   // need to allow this explicitly because apub activities might contain objects from our local
   // instance. replace is needed to remove the port in our federation test setup.
   let settings = Settings::get();
index b579a1958384fb625680319b77370fe3578c17c5..88a36c3983b9aa6ae4b46e9c60e669b9abff44ba 100644 (file)
@@ -40,7 +40,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
     )
     .route("/search", web::get().to(index))
     .route("/sponsors", web::get().to(index))
-    .route("/password_change/{token}", web::get().to(index));
+    .route("/password_change/{token}", web::get().to(index))
+    .route("/instances", web::get().to(index));
 }
 
 async fn index() -> Result<NamedFile, Error> {
index 8157d4a3a0beb71f717a5936b877d59974c04fbb..fe50b1e94714a914d54f1b6be09fe0818d62899d 100644 (file)
@@ -48,6 +48,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
       banned: [],
       online: null,
       version: null,
+      federated_instances: null,
     },
     siteConfigForm: {
       config_hjson: null,
index e911370fab858b1e166b3428f0540166a090ff94..6e7acb7a0ac4a770bbf996615a46982584670d15 100644 (file)
@@ -51,6 +51,11 @@ export class Footer extends Component<any, FooterState> {
                 {i18n.t('modlog')}
               </Link>
             </li>
+            <li class="nav-item">
+              <Link class="nav-link" to="/instances">
+                {i18n.t('instances')}
+              </Link>
+            </li>
             <li class="nav-item">
               <a class="nav-link" href={'/docs/index.html'}>
                 {i18n.t('docs')}
diff --git a/ui/src/components/instances.tsx b/ui/src/components/instances.tsx
new file mode 100644 (file)
index 0000000..ae4e3f1
--- /dev/null
@@ -0,0 +1,98 @@
+import { Component } from 'inferno';
+import { Helmet } from 'inferno-helmet';
+import { Subscription } from 'rxjs';
+import { retryWhen, delay, take } from 'rxjs/operators';
+import {
+  UserOperation,
+  WebSocketJsonResponse,
+  GetSiteResponse,
+} from '../interfaces';
+import { WebSocketService } from '../services';
+import { wsJsonToRes, toast } from '../utils';
+import { i18n } from '../i18next';
+
+interface InstancesState {
+  loading: boolean;
+  siteRes: GetSiteResponse;
+}
+
+export class Instances extends Component<any, InstancesState> {
+  private subscription: Subscription;
+  private emptyState: InstancesState = {
+    loading: true,
+    siteRes: undefined,
+  };
+
+  constructor(props: any, context: any) {
+    super(props, context);
+    this.state = this.emptyState;
+    this.subscription = WebSocketService.Instance.subject
+      .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+      .subscribe(
+        msg => this.parseMessage(msg),
+        err => console.error(err),
+        () => console.log('complete')
+      );
+
+    WebSocketService.Instance.getSite();
+  }
+
+  componentWillUnmount() {
+    this.subscription.unsubscribe();
+  }
+
+  get documentTitle(): string {
+    if (this.state.siteRes) {
+      return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`;
+    } else {
+      return 'Lemmy';
+    }
+  }
+
+  render() {
+    return (
+      <div class="container">
+        <Helmet title={this.documentTitle} />
+        {this.state.loading ? (
+          <h5 class="">
+            <svg class="icon icon-spinner spin">
+              <use xlinkHref="#icon-spinner"></use>
+            </svg>
+          </h5>
+        ) : (
+          <div>
+            <h5>{i18n.t('linked_instances')}</h5>
+            {this.state.siteRes &&
+            this.state.siteRes.federated_instances.length ? (
+              <ul>
+                {this.state.siteRes.federated_instances.map(i => (
+                  <li>
+                    <a href={`https://${i}`} target="_blank" rel="noopener">
+                      {i}
+                    </a>
+                  </li>
+                ))}
+              </ul>
+            ) : (
+              <div>{i18n.t('none_found')}</div>
+            )}
+          </div>
+        )}
+      </div>
+    );
+  }
+
+  parseMessage(msg: WebSocketJsonResponse) {
+    console.log(msg);
+    let res = wsJsonToRes(msg);
+    if (msg.error) {
+      toast(i18n.t(msg.error), 'danger');
+      return;
+    } else if (res.op == UserOperation.GetSite) {
+      let data = res.data as GetSiteResponse;
+      this.state.siteRes = data;
+      this.state.loading = false;
+      this.setState(this.state);
+    }
+  }
+}
index 9fbd1ae24f3f1b3dcfbf9dcd900b27034bc33b3f..246db674644f4522f2513f7f53bfbdeef69dd4a8 100644 (file)
@@ -114,6 +114,7 @@ export class Main extends Component<any, MainState> {
       banned: [],
       online: null,
       version: null,
+      federated_instances: null,
     },
     showEditSite: false,
     loading: true,
index 676ab8d4f4050cd6041ef833bbff4081134738aa..351d00b8c92559f4a52fc1cf1be20ec042574359 100644 (file)
@@ -82,6 +82,7 @@ export class Navbar extends Component<any, NavbarState> {
       banned: [],
       online: null,
       version: null,
+      federated_instances: null,
     },
     searchParam: '',
     toggleSearch: false,
index 3778ec90b7284fa2e66da4ba64e4d0ea268f21a0..06f461f3aa794e6d74ee757f45990fa06a9fe9a9 100644 (file)
@@ -97,6 +97,7 @@ export class Post extends Component<any, PostState> {
       },
       online: null,
       version: null,
+      federated_instances: undefined,
     },
   };
 
index e14f56a19c000d434ca7d4c113e6085eac147089..d7db0ae2a72ce4e619fbcd5b6a70ad3e312a10d0 100644 (file)
@@ -142,6 +142,7 @@ export class User extends Component<any, UserState> {
       },
       version: undefined,
       my_user: undefined,
+      federated_instances: undefined,
     },
   };
 
index 8e49df9fbb09c5d88b99834a59a4527a4f699a33..aa373de4a302bf4dc847677e32339d0307d05ddd 100644 (file)
@@ -19,6 +19,7 @@ import { AdminSettings } from './components/admin-settings';
 import { Inbox } from './components/inbox';
 import { Search } from './components/search';
 import { Sponsors } from './components/sponsors';
+import { Instances } from './components/instances';
 import { Symbols } from './components/symbols';
 import { i18n } from './i18next';
 
@@ -89,6 +90,7 @@ class Index extends Component<any, any> {
                   path={`/password_change/:token`}
                   component={PasswordChange}
                 />
+                <Route path={`/instances`} component={Instances} />
               </Switch>
               <Symbols />
             </div>
index 1e8899b9192079d377f095a25d3916e40ea481f4..b449060df5135f510d0565f27cb53c2ee7c0750f 100644 (file)
@@ -867,6 +867,7 @@ export interface GetSiteResponse {
   online: number;
   version: string;
   my_user?: User;
+  federated_instances: Array<string>;
 }
 
 export interface SiteResponse {
index a537252e6ba1475874a95678ed629421c73fc737..e3ccf8d9b5154b28251e2f49f1ef54cc61273fe9 100644 (file)
     "invalid_post_title": "Invalid post title",
     "invalid_url": "Invalid URL.",
     "play_captcha_audio": "Play Captcha Audio",
-    "bio": "Bio"
+    "bio": "Bio",
+    "instances": "Instances",
+    "linked_instances": "Linked Instances",
+    "none_found": "None found."
 }