client: give instructions on remote interaction
All checks were successful
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-sw Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
All checks were successful
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-sw Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
closes #279 Changelog: Added
This commit is contained in:
commit
ba1c4e76b9
9 changed files with 130 additions and 13 deletions
|
@ -1339,3 +1339,8 @@ _translationService:
|
||||||
_libreTranslate:
|
_libreTranslate:
|
||||||
endpoint: "LibreTranslate API Endpoint"
|
endpoint: "LibreTranslate API Endpoint"
|
||||||
authKey: "LibreTranslate Auth Key (optional)"
|
authKey: "LibreTranslate Auth Key (optional)"
|
||||||
|
_remoteInteract:
|
||||||
|
title: "I'm sorry, I'm afraid I can't do that."
|
||||||
|
description: "You cannot perform this action right now. You probably need to do it on your own instance, or sign in."
|
||||||
|
urlInstructions: "You can copy this URL. If you paste it into the search field on your instance, you should be taken to the right location."
|
||||||
|
url: "URL"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<slot name="value"></slot>
|
<slot name="value"></slot>
|
||||||
|
<!-- FIXME the button should not be part of the overflow: hidden element to ensure its always visible -->
|
||||||
<button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
|
<button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -126,7 +126,7 @@ import XRenoteButton from './renote-button.vue';
|
||||||
import MkUrlPreview from '@/components/url-preview.vue';
|
import MkUrlPreview from '@/components/url-preview.vue';
|
||||||
import MkInstanceTicker from '@/components/instance-ticker.vue';
|
import MkInstanceTicker from '@/components/instance-ticker.vue';
|
||||||
import MkVisibility from '@/components/visibility.vue';
|
import MkVisibility from '@/components/visibility.vue';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLoginOrRemote, urlForNote } from '@/scripts/please-login';
|
||||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
import { notePage } from '@/filters/note';
|
import { notePage } from '@/filters/note';
|
||||||
|
@ -195,7 +195,8 @@ useNoteCapture({
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
function reply(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(appearNote));
|
||||||
|
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote,
|
reply: appearNote,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
|
@ -205,7 +206,8 @@ function reply(viaKeyboard = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function react(): void {
|
function react(): void {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(appearNote));
|
||||||
|
|
||||||
blur();
|
blur();
|
||||||
reactionPicker.show(reactButton.value, reaction => {
|
reactionPicker.show(reactButton.value, reaction => {
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
|
|
|
@ -115,7 +115,7 @@ import XRenoteButton from './renote-button.vue';
|
||||||
import MkUrlPreview from '@/components/url-preview.vue';
|
import MkUrlPreview from '@/components/url-preview.vue';
|
||||||
import MkInstanceTicker from '@/components/instance-ticker.vue';
|
import MkInstanceTicker from '@/components/instance-ticker.vue';
|
||||||
import MkVisibility from '@/components/visibility.vue';
|
import MkVisibility from '@/components/visibility.vue';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLoginOrRemote, urlForNote } from '@/scripts/please-login';
|
||||||
import { focusPrev, focusNext } from '@/scripts/focus';
|
import { focusPrev, focusNext } from '@/scripts/focus';
|
||||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||||
import { userPage } from '@/filters/user';
|
import { userPage } from '@/filters/user';
|
||||||
|
@ -188,7 +188,8 @@ useNoteCapture({
|
||||||
});
|
});
|
||||||
|
|
||||||
function reply(viaKeyboard = false): void {
|
function reply(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(appearNote));
|
||||||
|
|
||||||
os.post({
|
os.post({
|
||||||
reply: appearNote,
|
reply: appearNote,
|
||||||
animation: !viaKeyboard,
|
animation: !viaKeyboard,
|
||||||
|
@ -198,7 +199,8 @@ function reply(viaKeyboard = false): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function react(): void {
|
function react(): void {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(appearNote));
|
||||||
|
|
||||||
blur();
|
blur();
|
||||||
reactionPicker.show(reactButton.value, reaction => {
|
reactionPicker.show(reactButton.value, reaction => {
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import * as foundkey from 'foundkey-js';
|
import * as foundkey from 'foundkey-js';
|
||||||
import { sum } from '@/scripts/array';
|
import { sum } from '@/scripts/array';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLoginOrRemote, urlForNote } from '@/scripts/please-login';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { useInterval } from '@/scripts/use-interval';
|
import { useInterval } from '@/scripts/use-interval';
|
||||||
|
@ -68,7 +68,7 @@ if (props.note.poll.expiresAt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const vote = async (id) => {
|
const vote = async (id) => {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(props.note));
|
||||||
|
|
||||||
if (props.readOnly || closed.value || isVoted.value) return;
|
if (props.readOnly || closed.value || isVoted.value) return;
|
||||||
|
|
||||||
|
|
88
packages/client/src/components/remote-interact.vue
Normal file
88
packages/client/src/components/remote-interact.vue
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<XModalWindow
|
||||||
|
ref="dialog"
|
||||||
|
:width="500"
|
||||||
|
@close="onClose()"
|
||||||
|
@closed="emit('closed')"
|
||||||
|
>
|
||||||
|
<template #header>{{ i18n.ts._remoteInteract.title }}</template>
|
||||||
|
|
||||||
|
<MkSpacer :margin-min="20" :margin-max="32" class="remote-interact" style="padding-top: 0;">
|
||||||
|
<p>{{ i18n.ts._remoteInteract.description }}</p>
|
||||||
|
<section>
|
||||||
|
<p>{{ i18n.ts._remoteInteract.urlInstructions }}</p>
|
||||||
|
<a :href="remoteUrl" class="_link">{{ remoteUrl }}</a>
|
||||||
|
<button v-tooltip="i18n.ts.copyUrl" class="_textButton" @click="copyUrl"><i class="far fa-copy"></i></button>
|
||||||
|
</section>
|
||||||
|
<aside>
|
||||||
|
<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
|
||||||
|
<button class="_button" @click="onClose()">{{ i18n.ts.cancel }}</button>
|
||||||
|
</aside>
|
||||||
|
</MkSpacer>
|
||||||
|
</XModalWindow>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||||
|
import XSigninDialog from '@/components/signin-dialog.vue';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
import * as os from '@/os';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
remoteUrl: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'closed'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const dialog = $ref<InstanceType<typeof XModalWindow>>();
|
||||||
|
|
||||||
|
function onClose(): void {
|
||||||
|
emit('closed');
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function signin() {
|
||||||
|
os.popup(XSigninDialog, {
|
||||||
|
autoSet: true,
|
||||||
|
}, {}, 'closed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyUrl() {
|
||||||
|
copyToClipboard(props.remoteUrl);
|
||||||
|
os.success();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.remote-interact {
|
||||||
|
section {
|
||||||
|
padding: var(--radius);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
border: solid .2em var(--accentDarken);
|
||||||
|
|
||||||
|
> p {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> button {
|
||||||
|
margin-left: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin-top: 1em;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 2em;
|
||||||
|
background-color: var(--navBg);
|
||||||
|
color: var(--navFg);
|
||||||
|
margin: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -17,7 +17,7 @@
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Note } from 'foundkey-js/built/entities';
|
import { Note } from 'foundkey-js/built/entities';
|
||||||
import XDetails from '@/components/users-tooltip.vue';
|
import XDetails from '@/components/users-tooltip.vue';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLoginOrRemote, urlForNote } from '@/scripts/please-login';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { useTooltip } from '@/scripts/use-tooltip';
|
import { useTooltip } from '@/scripts/use-tooltip';
|
||||||
|
@ -51,7 +51,8 @@ useTooltip(buttonRef, async (showing) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function renote(viaKeyboard = false): void {
|
function renote(viaKeyboard = false): void {
|
||||||
pleaseLogin();
|
pleaseLoginOrRemote(urlForNote(props.note));
|
||||||
|
|
||||||
os.popupMenu([{
|
os.popupMenu([{
|
||||||
text: i18n.ts.renote,
|
text: i18n.ts.renote,
|
||||||
icon: 'fas fa-retweet',
|
icon: 'fas fa-retweet',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { EventEmitter } from 'eventemitter3';
|
import { EventEmitter } from 'eventemitter3';
|
||||||
import { Component, shallowRef, ShallowRef } from 'vue';
|
import { Component, shallowRef, ShallowRef } from 'vue';
|
||||||
import { pleaseLogin } from '@/scripts/please-login';
|
import { pleaseLoginOrPage } from '@/scripts/please-login';
|
||||||
import { safeURIDecode } from '@/scripts/safe-uri-decode';
|
import { safeURIDecode } from '@/scripts/safe-uri-decode';
|
||||||
|
|
||||||
type RouteDef = {
|
type RouteDef = {
|
||||||
|
@ -174,7 +174,7 @@ export class Router extends EventEmitter<{
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.route.loginRequired) {
|
if (res.route.loginRequired) {
|
||||||
pleaseLogin('/');
|
pleaseLoginOrPage('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSamePath = beforePath === path;
|
const isSamePath = beforePath === path;
|
||||||
|
|
|
@ -2,8 +2,10 @@ import { defineAsyncComponent } from 'vue';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { popup } from '@/os';
|
import { popup } from '@/os';
|
||||||
|
import { url } from '@/config';
|
||||||
|
import { entities } from 'foundkey-js';
|
||||||
|
|
||||||
export function pleaseLogin(path?: string) {
|
export function pleaseLoginOrPage(path?: string) {
|
||||||
if ($i) return;
|
if ($i) return;
|
||||||
|
|
||||||
popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {
|
popup(defineAsyncComponent(() => import('@/components/signin-dialog.vue')), {
|
||||||
|
@ -19,3 +21,19 @@ export function pleaseLogin(path?: string) {
|
||||||
|
|
||||||
if (!path) throw new Error('signin required');
|
if (!path) throw new Error('signin required');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pleaseLoginOrRemote(remoteUrl: string) {
|
||||||
|
if ($i) return;
|
||||||
|
|
||||||
|
popup(defineAsyncComponent(() => import('@/components/remote-interact.vue')), {
|
||||||
|
remoteUrl,
|
||||||
|
}, {}, 'closed');
|
||||||
|
|
||||||
|
throw new Error('signin required');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function urlForNote(note: entities.Note): string {
|
||||||
|
return note.url
|
||||||
|
?? note.uri
|
||||||
|
?? `${url}/notes/${note.id}`;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue