[wip] standalone frontend stuff

This commit is contained in:
Laura Hausmann 2023-12-04 20:29:59 +01:00
parent 7f58107311
commit 0b2e9511e2
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
124 changed files with 2779 additions and 128 deletions

49
.pnp.cjs generated
View file

@ -3239,6 +3239,30 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
["@rollup/plugin-replace", [\
["npm:5.0.5", {\
"packageLocation": "./.yarn/cache/@rollup-plugin-replace-npm-5.0.5-42f61be6e8-bcf106346f.zip/node_modules/@rollup/plugin-replace/",\
"packageDependencies": [\
["@rollup/plugin-replace", "npm:5.0.5"]\
],\
"linkType": "SOFT"\
}],\
["virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:5.0.5", {\
"packageLocation": "./.yarn/__virtual__/@rollup-plugin-replace-virtual-bb6943b0cc/0/cache/@rollup-plugin-replace-npm-5.0.5-42f61be6e8-bcf106346f.zip/node_modules/@rollup/plugin-replace/",\
"packageDependencies": [\
["@rollup/plugin-replace", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:5.0.5"],\
["@rollup/pluginutils", "virtual:bb6943b0cca2c422f7a7afd65919f64a13f4e8693f7906299796ebe01591897f1a7622cbd9bd4db684f8c6acc1f55e4a4d48e34b460bcd763b5121a2b479f420#npm:5.1.0"],\
["@types/rollup", null],\
["magic-string", "npm:0.30.5"],\
["rollup", "npm:3.26.2"]\
],\
"packagePeers": [\
"@types/rollup",\
"rollup"\
],\
"linkType": "HARD"\
}]\
]],\
["@rollup/pluginutils", [\
["npm:3.1.0", {\
"packageLocation": "./.yarn/cache/@rollup-pluginutils-npm-3.1.0-b44b222e7d-3b69f02893.zip/node_modules/@rollup/pluginutils/",\
@ -3256,6 +3280,29 @@ const RAW_RUNTIME_STATE =
],\
"linkType": "HARD"\
}],\
["npm:5.1.0", {\
"packageLocation": "./.yarn/cache/@rollup-pluginutils-npm-5.1.0-6939820ef8-abb15eaec5.zip/node_modules/@rollup/pluginutils/",\
"packageDependencies": [\
["@rollup/pluginutils", "npm:5.1.0"]\
],\
"linkType": "SOFT"\
}],\
["virtual:bb6943b0cca2c422f7a7afd65919f64a13f4e8693f7906299796ebe01591897f1a7622cbd9bd4db684f8c6acc1f55e4a4d48e34b460bcd763b5121a2b479f420#npm:5.1.0", {\
"packageLocation": "./.yarn/__virtual__/@rollup-pluginutils-virtual-2c209a0117/0/cache/@rollup-pluginutils-npm-5.1.0-6939820ef8-abb15eaec5.zip/node_modules/@rollup/pluginutils/",\
"packageDependencies": [\
["@rollup/pluginutils", "virtual:bb6943b0cca2c422f7a7afd65919f64a13f4e8693f7906299796ebe01591897f1a7622cbd9bd4db684f8c6acc1f55e4a4d48e34b460bcd763b5121a2b479f420#npm:5.1.0"],\
["@types/estree", "npm:1.0.1"],\
["@types/rollup", null],\
["estree-walker", "npm:2.0.2"],\
["picomatch", "npm:2.3.1"],\
["rollup", "npm:3.26.2"]\
],\
"packagePeers": [\
"@types/rollup",\
"rollup"\
],\
"linkType": "HARD"\
}],\
["virtual:cf1428485ac6d7495e234c120f3000a61db869813c88397f285b8f9691f2488035a4fa06e3d208d181d90d55925c550cd96d3b139f098a04a0ef06802bcb7775#npm:3.1.0", {\
"packageLocation": "./.yarn/__virtual__/@rollup-pluginutils-virtual-228ea72560/0/cache/@rollup-pluginutils-npm-3.1.0-b44b222e7d-3b69f02893.zip/node_modules/@rollup/pluginutils/",\
"packageDependencies": [\
@ -8537,6 +8584,7 @@ const RAW_RUNTIME_STATE =
["@phosphor-icons/web", "npm:2.0.3"],\
["@rollup/plugin-alias", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:3.1.9"],\
["@rollup/plugin-json", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:4.1.0"],\
["@rollup/plugin-replace", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:5.0.5"],\
["@rollup/pluginutils", "npm:4.2.1"],\
["@syuilo/aiscript", "npm:0.11.1"],\
["@types/escape-regexp", "npm:0.0.1"],\
@ -8592,6 +8640,7 @@ const RAW_RUNTIME_STATE =
["prismjs", "npm:1.29.0"],\
["punycode", "npm:2.3.0"],\
["querystring", "npm:0.2.1"],\
["reconnecting-websocket", "npm:4.4.0"],\
["rndstr", "npm:1.0.0"],\
["rollup", "npm:3.26.2"],\
["s-age", "npm:1.1.2"],\

BIN
.yarn/cache/@rollup-plugin-replace-npm-5.0.5-42f61be6e8-bcf106346f.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
.yarn/cache/@rollup-pluginutils-npm-5.1.0-6939820ef8-abb15eaec5.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

View file

@ -17,6 +17,7 @@
"@phosphor-icons/web": "^2.0.3",
"@rollup/plugin-alias": "3.1.9",
"@rollup/plugin-json": "4.1.0",
"@rollup/plugin-replace": "^5.0.5",
"@rollup/pluginutils": "^4.2.1",
"@syuilo/aiscript": "0.11.1",
"@types/escape-regexp": "0.0.1",
@ -57,7 +58,6 @@
"focus-trap": "^7.5.2",
"focus-trap-vue": "^4.0.2",
"gsap": "^3.12.2",
"iceshrimp-js": "workspace:*",
"idb-keyval": "6.2.1",
"insert-text-at-cursor": "0.3.0",
"json5": "2.2.3",
@ -103,6 +103,7 @@
},
"dependencies": {
"@babel/core": "^7.23.2",
"eslint": "^8.45.0"
"eslint": "^8.45.0",
"reconnecting-websocket": "^4.4.0"
}
}

View file

@ -1,5 +1,5 @@
import { defineAsyncComponent, reactive } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import { i18n } from "./i18n";
import { del, get, set } from "@/scripts/idb-proxy";
import { apiUrl } from "@/config";

View file

@ -41,7 +41,7 @@
<script setup lang="ts">
import { ref } from "vue";
import type * as Misskey from "iceshrimp-js";
import type * as Misskey from "@/iceshrimp-js";
import XWindow from "@/components/MkWindow.vue";
import MkTextarea from "@/components/form/textarea.vue";
import MkButton from "@/components/MkButton.vue";

View file

@ -102,7 +102,7 @@ import { defaultStore } from "@/store";
import { addSkinTone, emojilist } from "@/scripts/emojilist";
import { instance } from "@/instance";
import { i18n } from "@/i18n";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js";
interface EmojiDef {
emoji: string;

View file

@ -63,7 +63,7 @@
</template>
<script lang="ts" setup>
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js";
import { i18n } from "@/i18n";
import { acct } from "@/filters/user";
import { $i } from "@/account";

View file

@ -37,7 +37,7 @@
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import Cropper from "cropperjs";
import tinycolor from "tinycolor2";
import XModalWindow from "@/components/MkModalWindow.vue";

View file

@ -15,7 +15,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import { length } from "stringz";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { concat } from "@/scripts/array";
import { i18n } from "@/i18n";
import {defaultStore} from "@/store";

View file

@ -39,7 +39,7 @@
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import copyToClipboard from "@/scripts/copy-to-clipboard";
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
import bytes from "@/filters/bytes";

View file

@ -38,7 +38,7 @@
<script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";

View file

@ -15,7 +15,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import * as os from "@/os";
import { i18n } from "@/i18n";

View file

@ -140,7 +140,7 @@ import {
ref,
watch,
} from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import MkButton from "./MkButton.vue";
import XNavFolder from "@/components/MkDrive.navFolder.vue";
import XFolder from "@/components/MkDrive.folder.vue";

View file

@ -41,7 +41,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import type * as Misskey from "iceshrimp-js";
import type * as Misskey from "@/iceshrimp-js";
import ImgWithBlurhash from "@/components/MkImgWithBlurhash.vue";
const props = defineProps<{

View file

@ -37,7 +37,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import XDrive from "@/components/MkDrive.vue";
import XModalWindow from "@/components/MkModalWindow.vue";
import number from "@/filters/number";

View file

@ -15,7 +15,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import XDrive from "@/components/MkDrive.vue";
import XWindow from "@/components/MkWindow.vue";
import { i18n } from "@/i18n";

View file

@ -164,7 +164,7 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import XSection from "@/components/MkEmojiPicker.section.vue";
import {
emojilist,

View file

@ -8,7 +8,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import * as os from "@/os";
const meta = ref<Misskey.entities.DetailedInstanceMetadata>();

View file

@ -53,7 +53,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js";
import MkPagination from "@/components/MkPagination.vue";
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
import bytes from "@/filters/bytes";

View file

@ -62,7 +62,7 @@
<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted } from "vue";
import type * as Misskey from "iceshrimp-js";
import type * as Misskey from "@/iceshrimp-js";
import * as os from "@/os";
import { stream } from "@/stream";
import { i18n } from "@/i18n";

View file

@ -27,7 +27,7 @@
<script lang="ts" setup>
import {} from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import bytes from "@/filters/bytes";
import number from "@/filters/number";
import MkModal from "@/components/MkModal.vue";

View file

@ -24,7 +24,7 @@
</template>
<script lang="ts" setup>
import * as iceshrimp from "iceshrimp-js";
import * as iceshrimp from "@/iceshrimp-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";

View file

@ -60,7 +60,7 @@ import MkInput from "@/components/form/input.vue";
import XModalWindow from "@/components/MkModalWindow.vue";
import * as os from "@/os";
import { i18n } from "@/i18n";
import { Instance } from "iceshrimp-js/built/entities";
import { Instance } from "@/iceshrimp-js/entities";
const emit = defineEmits<{
(ev: "ok", selected: Instance): void;

View file

@ -90,7 +90,7 @@
import { watch, ref, computed } from "vue";
import VuePlyr from "vue-plyr";
import "vue-plyr/dist/vue-plyr.css";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import ImgWithBlurhash from "@/components/MkImgWithBlurhash.vue";
import { defaultStore } from "@/store";

View file

@ -58,7 +58,7 @@
<script lang="ts" setup>
import { onMounted } from "vue";
import VuePlyr from "vue-plyr";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { ColdDeviceStorage } from "@/store";
import "vue-plyr/dist/vue-plyr.css";
import { i18n } from "@/i18n";

View file

@ -29,7 +29,7 @@
<script lang="ts" setup>
import { onMounted, ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import PhotoSwipeLightbox from "photoswipe/lightbox";
import PhotoSwipe from "photoswipe";
import "photoswipe/style.css";

View file

@ -260,7 +260,7 @@
import { computed, inject, onMounted, onUnmounted, reactive, ref } from "vue";
import * as mfm from "mfm-js";
import type { Ref } from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import MkNoteSub from "@/components/MkNoteSub.vue";
import MkSubNoteContent from "./MkSubNoteContent.vue";
import XNoteHeader from "@/components/MkNoteHeader.vue";

View file

@ -164,7 +164,7 @@ import {
reactive,
ref,
} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkTab from "@/components/MkTab.vue";
import MkNote from "@/components/MkNote.vue";
import MkNoteSub from "@/components/MkNoteSub.vue";
@ -185,7 +185,7 @@ import { getNoteMenu } from "@/scripts/get-note-menu";
import { useNoteCapture } from "@/scripts/use-note-capture";
import { deepClone } from "@/scripts/clone";
import { stream } from "@/stream";
import { NoteUpdatedEvent } from "iceshrimp-js/src/streaming.types";
import { NoteUpdatedEvent } from "@/iceshrimp-js/streaming.types";
import appear from "@/directives/appear";
const props = defineProps<{

View file

@ -48,7 +48,7 @@
<script lang="ts" setup>
import {} from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { defaultStore, noteViewInterruptors } from "@/store";
import MkVisibility from "@/components/MkVisibility.vue";
import MkInstanceTicker from "@/components/MkInstanceTicker.vue";

View file

@ -12,7 +12,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import XNoteHeader from "@/components/MkNoteHeader.vue";
import MkSubNoteContent from "@/components/MkSubNoteContent.vue";

View file

@ -181,7 +181,7 @@
<script lang="ts" setup>
import { inject, ref } from "vue";
import type { Ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import XNoteHeader from "@/components/MkNoteHeader.vue";
import MkSubNoteContent from "@/components/MkSubNoteContent.vue";
import XReactionsViewer from "@/components/MkReactionsViewer.vue";

View file

@ -274,7 +274,7 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, watch } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import XReactionIcon from "@/components/MkReactionIcon.vue";
import MkFollowButton from "@/components/MkFollowButton.vue";
import XReactionTooltip from "@/components/MkReactionTooltip.vue";

View file

@ -41,7 +41,7 @@
<script lang="ts" setup>
import {} from "vue";
import { notificationTypes } from "iceshrimp-js";
import { notificationTypes } from "@/iceshrimp-js";
import MkSwitch from "./form/switch.vue";
import MkInfo from "./MkInfo.vue";
import MkButton from "./MkButton.vue";

View file

@ -54,7 +54,7 @@ import {
computed,
ref,
} from "vue";
import { notificationTypes } from "iceshrimp-js";
import { notificationTypes } from "@/iceshrimp-js";
import MkPagination, { Paging } from "@/components/MkPagination.vue";
import XNotification from "@/components/MkNotification.vue";
import XList from "@/components/MkDateSeparatedList.vue";

View file

@ -72,7 +72,7 @@ import {
ref,
watch,
} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import * as os from "@/os";
import {
onScrollTop,

View file

@ -53,7 +53,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import { sum } from "@/scripts/array";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";

View file

@ -235,11 +235,11 @@
<script lang="ts" setup>
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from "vue";
import * as mfm from "mfm-js";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import insertTextAtCursor from "insert-text-at-cursor";
import { length } from "stringz";
import { toASCII } from "punycode/";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
import { throttle } from "throttle-debounce";
import XNoteSimple from "@/components/MkNoteSimple.vue";
import XNotePreview from "@/components/MkNotePreview.vue";

View file

@ -20,7 +20,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkModal from "@/components/MkModal.vue";
import MkPostForm from "@/components/MkPostForm.vue";

View file

@ -11,7 +11,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { Note } from "iceshrimp-js/built/entities";
import type { Note } from "@/iceshrimp-js/entities";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";
import { $i } from "@/account";

View file

@ -36,7 +36,7 @@
<script lang="ts" setup>
import { onMounted, watch } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkReactionIcon from "@/components/MkReactionIcon.vue";
import MkUserCardMini from "@/components/MkUserCardMini.vue";
import { i18n } from "@/i18n";

View file

@ -22,7 +22,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import XDetails from "@/components/MkReactionsViewer.details.vue";
import XReactionIcon from "@/components/MkReactionIcon.vue";
import * as os from "@/os";

View file

@ -18,7 +18,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import { $i } from "@/account";
import XReaction from "@/components/MkReactionsViewer.reaction.vue";

View file

@ -22,7 +22,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import Ripple from "@/components/MkRipple.vue";
import XDetails from "@/components/MkUsersTooltip.vue";
import { pleaseLogin } from "@/scripts/please-login";

View file

@ -4,7 +4,7 @@ import MkInput from "@/components/form/input.vue";
import * as os from "@/os.js";
import XSearchFilterDialog from "@/components/MkSearchFilterDialog.vue";
import { onActivated, onMounted, onUnmounted, ref, toRefs } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
const props = defineProps<{
query: string;

View file

@ -38,7 +38,7 @@
</template>
<script lang="ts" setup>
import type { Note } from "iceshrimp-js/built/entities";
import type { Note } from "@/iceshrimp-js/entities";
import Ripple from "@/components/MkRipple.vue";
import { pleaseLogin } from "@/scripts/please-login";
import * as os from "@/os";

View file

@ -42,7 +42,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import type { Note } from "iceshrimp-js/built/entities";
import type { Note } from "@/iceshrimp-js/entities";
import Ripple from "@/components/MkRipple.vue";
import XDetails from "@/components/MkUsersTooltip.vue";
import { pleaseLogin } from "@/scripts/please-login";

View file

@ -202,7 +202,7 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import * as mfm from "mfm-js";
import * as os from "@/os";
import XNoteSimple from "@/components/MkNoteSimple.vue";

View file

@ -44,7 +44,7 @@
<script lang="ts" setup>
import {} from "vue";
import { permissions as kinds } from "iceshrimp-js";
import { permissions as kinds } from "@/iceshrimp-js";
import MkInput from "./form/input.vue";
import MkSwitch from "./form/switch.vue";
import MkButton from "./MkButton.vue";

View file

@ -24,7 +24,7 @@
</template>
<script lang="ts" setup>
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os";
import { acct, userPage } from "@/filters/user";

View file

@ -86,7 +86,7 @@
</template>
<script lang="ts" setup>
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkFollowButton from "@/components/MkFollowButton.vue";
import XShowMoreButton from "@/components/MkShowMoreButton.vue";
import MkNumber from "@/components/MkNumber.vue";

View file

@ -4,7 +4,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import { i18n } from "@/i18n";
const props = defineProps<{

View file

@ -30,8 +30,8 @@
<script lang="ts" setup>
import { onMounted } from "vue";
import MkUserInfo from "@/components/MkUserInfo.vue";
import * as Acct from "iceshrimp-js/built/acct";
import type * as misskey from "iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import type * as misskey from "@/iceshrimp-js";
import * as os from "@/os";
const props = defineProps<{

View file

@ -89,7 +89,7 @@
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkInput from "@/components/form/input.vue";
import FormSplit from "@/components/form/split.vue";
import XModalWindow from "@/components/MkModalWindow.vue";

View file

@ -85,7 +85,7 @@
<script lang="ts" setup>
import { nextTick, onMounted } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkInput from "@/components/form/input.vue";
import FormSplit from "@/components/form/split.vue";
import XModalWindow from "@/components/MkModalWindow.vue";

View file

@ -126,7 +126,7 @@
<script lang="ts" setup>
import { nextTick, watch } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkModal from "@/components/MkModal.vue";
import { i18n } from "@/i18n";

View file

@ -10,7 +10,7 @@
</template>
<script lang="ts" setup>
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { toUnicode } from "punycode/";
import { host as hostRaw } from "@/config";

View file

@ -37,7 +37,7 @@
<script lang="ts" setup>
import { onMounted, watch } from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
import { acct, userPage } from "@/filters/user";

View file

@ -24,7 +24,7 @@
<script lang="ts" setup>
import { computed, ref, watch } from "vue";
import type { CustomEmoji } from "iceshrimp-js/built/entities";
import type { CustomEmoji } from "@/iceshrimp-js/entities.js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { char2filePath } from "@/scripts/twemoji-base";
import { defaultStore } from "@/store";

View file

@ -10,7 +10,7 @@
<script lang="ts" setup>
import {} from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
const props = withDefaults(
defineProps<{

View file

@ -18,11 +18,15 @@ export const apiUrl = `${url}/api`;
export const wsUrl = `${url
.replace("http://", "ws://")
.replace("https://", "wss://")}/streaming`;
export const lang = localStorage.getItem("lang");
export const lang = localStorage.getItem("lang") ?? 'en-US';
export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem("locale"));
export let locale = JSON.parse(localStorage.getItem("locale"));
export const version = _VERSION_;
export const instanceName = siteName === "Iceshrimp" ? host : siteName;
export let searchEngine = 'https://duckduckgo.com/?q=';
export const ui = localStorage.getItem("ui");
export const debug = localStorage.getItem("debug") === "true";
export function updateLocale(newLocale: any): void {
locale = newLocale;
}

View file

@ -1,9 +1,10 @@
import * as misskey from "iceshrimp-js";
import * as Acct from "iceshrimp-js/built/acct";
import * as misskey from "@/iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import { url } from "@/config";
import { ParsedAcct } from "@/iceshrimp-js/acct.js";
export const acct = (user: misskey.Acct) => {
return Acct.toString(user);
export const acct = (user: ParsedAcct) => {
return new Acct(user.username, user.host).toString();
};
export const userName = (user: misskey.entities.User) => {

View file

@ -2,7 +2,7 @@ import { markRaw } from "vue";
import { locale } from "@/config";
import { I18n } from "@/scripts/i18n";
export const i18n = markRaw(new I18n(locale));
export let i18n = markRaw(new I18n(locale));
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module "@vue/runtime-core" {
@ -11,3 +11,7 @@ declare module "@vue/runtime-core" {
$ts: typeof i18n["locale"];
}
}
export function updateI18n(newLocale: any) {
i18n.ts = newLocale;
}

View file

@ -0,0 +1,32 @@
export type ParsedAcct = {
username: string;
host: string | null;
}
export class Acct {
public username: string;
public host: string | null;
constructor(username: string, host: string | null) {
this.username = username;
this.host = host;
}
public static parse(acct: string): ParsedAcct {
if (acct.startsWith("@")) acct = acct.slice(1);
const split = acct.split("@", 2);
return { username: split[0], host: split[1] ?? null };
}
public static fromParsed(parsed: ParsedAcct) {
return new this(parsed.username, parsed.host);
}
public static toString(parsed: ParsedAcct) {
return this.fromParsed(parsed).toString();
}
public toString(): string {
return this.host == null ? this.username : `${this.username}@${this.host}`;
}
}

View file

@ -0,0 +1,127 @@
import { Endpoints } from "./api.types.js";
const MK_API_ERROR = Symbol();
export type APIError = {
id: string;
code: string;
message: string;
kind: "client" | "server";
info: Record<string, any>;
};
export function isAPIError(reason: any): reason is APIError {
return reason[MK_API_ERROR] === true;
}
export type FetchLike = (
input: string,
init?: {
method?: string;
body?: string;
credentials?: RequestCredentials;
cache?: RequestCache;
},
) => Promise<{
status: number;
json(): Promise<any>;
}>;
type IsNeverType<T> = [T] extends [never] ? true : false;
type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
type IsCaseMatched<
E extends keyof Endpoints,
P extends Endpoints[E]["req"],
C extends number,
> = IsNeverType<
StrictExtract<Endpoints[E]["res"]["$switch"]["$cases"][C], [P, any]>
> extends false
? true
: false;
type GetCaseResult<
E extends keyof Endpoints,
P extends Endpoints[E]["req"],
C extends number,
> = StrictExtract<Endpoints[E]["res"]["$switch"]["$cases"][C], [P, any]>[1];
export class APIClient {
public origin: string;
public credential: string | null | undefined;
public fetch: FetchLike;
constructor(opts: {
origin: APIClient["origin"];
credential?: APIClient["credential"];
fetch?: APIClient["fetch"] | null | undefined;
}) {
this.origin = opts.origin;
this.credential = opts.credential;
// ネイティブ関数をそのまま変数に代入して使おうとするとChromiumではIllegal invocationエラーが発生するため、
// 環境で実装されているfetchを使う場合は無名関数でラップして使用する
this.fetch = opts.fetch || ((...args) => fetch(...args));
}
public request<E extends keyof Endpoints, P extends Endpoints[E]["req"]>(
endpoint: E,
params: P = {} as P,
credential?: string | null | undefined,
): Promise<
Endpoints[E]["res"] extends {
$switch: { $cases: [any, any][]; $default: any };
}
? IsCaseMatched<E, P, 0> extends true
? GetCaseResult<E, P, 0>
: IsCaseMatched<E, P, 1> extends true
? GetCaseResult<E, P, 1>
: IsCaseMatched<E, P, 2> extends true
? GetCaseResult<E, P, 2>
: IsCaseMatched<E, P, 3> extends true
? GetCaseResult<E, P, 3>
: IsCaseMatched<E, P, 4> extends true
? GetCaseResult<E, P, 4>
: IsCaseMatched<E, P, 5> extends true
? GetCaseResult<E, P, 5>
: IsCaseMatched<E, P, 6> extends true
? GetCaseResult<E, P, 6>
: IsCaseMatched<E, P, 7> extends true
? GetCaseResult<E, P, 7>
: IsCaseMatched<E, P, 8> extends true
? GetCaseResult<E, P, 8>
: IsCaseMatched<E, P, 9> extends true
? GetCaseResult<E, P, 9>
: Endpoints[E]["res"]["$switch"]["$default"]
: Endpoints[E]["res"]
> {
const promise = new Promise((resolve, reject) => {
this.fetch(`${this.origin}/api/${endpoint}`, {
method: "POST",
body: JSON.stringify({
...params,
i: credential !== undefined ? credential : this.credential,
}),
credentials: "omit",
cache: "no-cache",
})
.then(async (res) => {
const body = res.status === 204 ? null : await res.json();
if (res.status === 200) {
resolve(body);
} else if (res.status === 204) {
resolve(null);
} else {
reject({
[MK_API_ERROR]: true,
...body.error,
});
}
})
.catch(reject);
});
return promise as any;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
export const notificationTypes = [
"follow",
"mention",
"reply",
"renote",
"quote",
"reaction",
"pollVote",
"pollEnded",
"receiveFollowRequest",
"followRequestAccepted",
"groupInvited",
"app",
] as const;
export const noteVisibilities = [
"public",
"home",
"followers",
"specified",
] as const;
export const ffVisibility = ["public", "followers", "private"] as const;
export const permissions = [
"read:account",
"write:account",
"read:blocks",
"write:blocks",
"read:drive",
"write:drive",
"read:favorites",
"write:favorites",
"read:following",
"write:following",
"read:messaging",
"write:messaging",
"read:mutes",
"write:mutes",
"write:notes",
"read:notifications",
"write:notifications",
"read:reactions",
"write:reactions",
"write:votes",
"read:pages",
"write:pages",
"write:page-likes",
"read:page-likes",
"read:user-groups",
"write:user-groups",
"read:channels",
"write:channels",
"read:gallery",
"write:gallery",
"read:gallery-likes",
"write:gallery-likes",
];

View file

@ -0,0 +1,494 @@
export type ID = string;
export type DateString = string;
type TODO = Record<string, any>;
// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように
export type User = UserLite | UserDetailed;
export type UserLite = {
id: ID;
username: string;
host: string | null;
name: string;
onlineStatus: "online" | "active" | "offline" | "unknown";
avatarUrl: string;
avatarBlurhash: string;
alsoKnownAs: string[];
movedToUri: any;
emojis: {
name: string;
url: string;
}[];
instance?: {
name: Instance["name"];
softwareName: Instance["softwareName"];
softwareVersion: Instance["softwareVersion"];
iconUrl: Instance["iconUrl"];
faviconUrl: Instance["faviconUrl"];
themeColor: Instance["themeColor"];
};
};
export type UserDetailed = UserLite & {
bannerBlurhash: string | null;
bannerColor: string | null;
bannerUrl: string | null;
birthday: string | null;
createdAt: DateString;
description: string | null;
ffVisibility: "public" | "followers" | "private";
fields: {
name: string;
value: string;
verified?: boolean;
}[];
followersCount: number;
followingCount: number;
hasPendingFollowRequestFromYou: boolean;
hasPendingFollowRequestToYou: boolean;
isAdmin: boolean;
isBlocked: boolean;
isBlocking: boolean;
isBot: boolean;
isCat: boolean;
isFollowed: boolean;
isFollowing: boolean;
isLocked: boolean;
isModerator: boolean;
isMuted: boolean;
isRenoteMuted: boolean;
isSilenced: boolean;
isSuspended: boolean;
lang: string | null;
lastFetchedAt?: DateString;
location: string | null;
notesCount: number;
pinnedNoteIds: ID[];
pinnedNotes: Note[];
pinnedPage: Page | null;
pinnedPageId: string | null;
publicReactions: boolean;
securityKeys: boolean;
twoFactorEnabled: boolean;
updatedAt: DateString | null;
uri: string | null;
url: string | null;
};
export type UserGroup = TODO;
export type UserList = {
id: ID;
createdAt: DateString;
name: string;
userIds: User["id"][];
};
export type MeDetailed = UserDetailed & {
avatarId: DriveFile["id"];
bannerId: DriveFile["id"];
autoAcceptFollowed: boolean;
alwaysMarkNsfw: boolean;
carefulBot: boolean;
emailNotificationTypes: string[];
hasPendingReceivedFollowRequest: boolean;
hasUnreadAnnouncement: boolean;
hasUnreadAntenna: boolean;
hasUnreadChannel: boolean;
hasUnreadMentions: boolean;
hasUnreadMessagingMessage: boolean;
hasUnreadNotification: boolean;
hasUnreadSpecifiedNotes: boolean;
hideOnlineStatus: boolean;
injectFeaturedNote: boolean;
integrations: Record<string, any>;
isDeleted: boolean;
isExplorable: boolean;
mutedWords: string[][];
mutingNotificationTypes: string[];
noCrawle: boolean;
preventAiLearning: boolean;
receiveAnnouncementEmail: boolean;
usePasswordLessLogin: boolean;
[other: string]: any;
};
export type DriveFile = {
id: ID;
createdAt: DateString;
isSensitive: boolean;
name: string;
thumbnailUrl: string;
url: string;
type: string;
size: number;
md5: string;
blurhash: string;
comment: string | null;
properties: Record<string, any>;
};
export type DriveFolder = TODO;
export type GalleryPost = TODO;
export type Note = {
id: ID;
createdAt: DateString;
text: string | null;
cw: string | null;
user: User;
userId: User["id"];
reply?: Note;
replyId: Note["id"];
renote?: Note;
renoteId: Note["id"];
files: DriveFile[];
fileIds: DriveFile["id"][];
visibility: "public" | "home" | "followers" | "specified";
visibleUserIds?: User["id"][];
localOnly?: boolean;
channel?: Channel["id"];
myReaction?: string;
isRenoted?: boolean;
reactions: Record<string, number>;
renoteCount: number;
repliesCount: number;
poll?: {
expiresAt: DateString | null;
multiple: boolean;
choices: {
isVoted: boolean;
text: string;
votes: number;
}[];
};
emojis: {
name: string;
url: string;
}[];
uri?: string;
url?: string;
updatedAt?: DateString;
isHidden?: boolean;
};
export type NoteReaction = {
id: ID;
createdAt: DateString;
user: UserLite;
type: string;
};
export type Notification = {
id: ID;
createdAt: DateString;
isRead: boolean;
} & (
| {
type: "reaction";
reaction: string;
user: User;
userId: User["id"];
note: Note;
}
| {
type: "reply";
user: User;
userId: User["id"];
note: Note;
}
| {
type: "renote";
user: User;
userId: User["id"];
note: Note;
}
| {
type: "quote";
user: User;
userId: User["id"];
note: Note;
}
| {
type: "mention";
user: User;
userId: User["id"];
note: Note;
}
| {
type: "pollVote";
user: User;
userId: User["id"];
note: Note;
}
| {
type: "follow";
user: User;
userId: User["id"];
}
| {
type: "followRequestAccepted";
user: User;
userId: User["id"];
}
| {
type: "receiveFollowRequest";
user: User;
userId: User["id"];
}
| {
type: "groupInvited";
invitation: UserGroup;
user: User;
userId: User["id"];
}
| {
type: "app";
header?: string | null;
body: string;
icon?: string | null;
}
);
export type MessagingMessage = {
id: ID;
createdAt: DateString;
file: DriveFile | null;
fileId: DriveFile["id"] | null;
isRead: boolean;
reads: User["id"][];
text: string | null;
user: User;
userId: User["id"];
recipient?: User | null;
recipientId: User["id"] | null;
group?: UserGroup | null;
groupId: UserGroup["id"] | null;
};
export type CustomEmoji = {
id: string;
name: string;
url: string;
category: string;
aliases: string[];
};
export type LiteInstanceMetadata = {
maintainerName: string | null;
maintainerEmail: string | null;
version: string;
name: string | null;
uri: string;
description: string | null;
tosUrl: string | null;
disableRegistration: boolean;
disableLocalTimeline: boolean;
disableRecommendedTimeline: boolean;
disableGlobalTimeline: boolean;
driveCapacityPerLocalUserMb: number;
driveCapacityPerRemoteUserMb: number;
enableHcaptcha: boolean;
hcaptchaSiteKey: string | null;
enableRecaptcha: boolean;
recaptchaSiteKey: string | null;
swPublickey: string | null;
maxNoteTextLength: number;
enableEmail: boolean;
enableGithubIntegration: boolean;
enableDiscordIntegration: boolean;
searchEngine: string;
emojis: CustomEmoji[];
images: {
error: string;
notFound: string;
info: string;
};
};
export type DetailedInstanceMetadata = LiteInstanceMetadata & {
features: Record<string, any>;
};
export type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata;
export type ServerInfo = {
machine: string;
cpu: {
model: string;
cores: number;
};
mem: {
total: number;
};
fs: {
total: number;
used: number;
};
};
export type Stats = {
notesCount: number;
originalNotesCount: number;
usersCount: number;
originalUsersCount: number;
instances: number;
driveUsageLocal: number;
driveUsageRemote: number;
};
export type Page = {
id: ID;
createdAt: DateString;
updatedAt: DateString;
userId: User["id"];
user: User;
content: Record<string, any>[];
variables: Record<string, any>[];
title: string;
name: string;
summary: string | null;
hideTitleWhenPinned: boolean;
alignCenter: boolean;
font: string;
script: string;
eyeCatchingImageId: DriveFile["id"] | null;
eyeCatchingImage: DriveFile | null;
attachedFiles: any;
likedCount: number;
isLiked?: boolean;
};
export type PageEvent = {
pageId: Page["id"];
event: string;
var: any;
userId: User["id"];
user: User;
};
export type Announcement = {
id: ID;
createdAt: DateString;
updatedAt: DateString | null;
text: string;
title: string;
imageUrl: string | null;
isRead?: boolean;
};
export type Antenna = {
id: ID;
createdAt: DateString;
name: string;
keywords: string[][]; // TODO
excludeKeywords: string[][]; // TODO
src: "home" | "all" | "users" | "list" | "group" | "instances";
userListId: ID | null; // TODO
userGroupId: ID | null; // TODO
users: string[]; // TODO
instances: string[];
caseSensitive: boolean;
notify: boolean;
withReplies: boolean;
withFile: boolean;
hasUnreadNote: boolean;
};
export type App = TODO;
export type AuthSession = {
id: ID;
app: App;
token: string;
};
export type Clip = TODO;
export type NoteFavorite = {
id: ID;
createdAt: DateString;
noteId: Note["id"];
note: Note;
};
export type FollowRequest = {
id: ID;
follower: User;
followee: User;
};
export type Channel = {
id: ID;
// TODO
};
export type Following = {
id: ID;
createdAt: DateString;
followerId: User["id"];
followeeId: User["id"];
};
export type FollowingFolloweePopulated = Following & {
followee: UserDetailed;
};
export type FollowingFollowerPopulated = Following & {
follower: UserDetailed;
};
export type Blocking = {
id: ID;
createdAt: DateString;
blockeeId: User["id"];
blockee: UserDetailed;
};
export type Instance = {
id: ID;
caughtAt: DateString;
host: string;
usersCount: number;
notesCount: number;
followingCount: number;
followersCount: number;
driveUsage: number;
driveFiles: number;
latestRequestSentAt: DateString | null;
latestStatus: number | null;
latestRequestReceivedAt: DateString | null;
lastCommunicatedAt: DateString;
isNotResponding: boolean;
isSuspended: boolean;
softwareName: string | null;
softwareVersion: string | null;
openRegistrations: boolean | null;
name: string | null;
description: string | null;
maintainerName: string | null;
maintainerEmail: string | null;
iconUrl: string | null;
faviconUrl: string | null;
themeColor: string | null;
infoUpdatedAt: DateString | null;
};
export type Signin = {
id: ID;
createdAt: DateString;
ip: string;
headers: Record<string, any>;
success: boolean;
};
export type UserSorting =
| "+follower"
| "-follower"
| "+createdAt"
| "-createdAt"
| "+updatedAt"
| "-updatedAt";
export type OriginType = "combined" | "local" | "remote";

View file

@ -0,0 +1,21 @@
import { Endpoints } from "./api.types.js";
import Stream, { Connection } from "./streaming.js";
import { Channels } from "./streaming.types.js";
import { Acct } from "./acct.js";
import * as consts from "./consts.js";
export type { Endpoints, Channels }
export { Acct, Stream, Connection as ChannelConnection };
export const permissions = consts.permissions;
export const notificationTypes = consts.notificationTypes;
export const noteVisibilities = consts.noteVisibilities;
export const ffVisibility = consts.ffVisibility;
// api extractor not supported yet
//export * as api from './api';
//export * as entities from './entities';
import * as api from "./api.js";
import * as entities from "./entities.js";
export { api, entities };

View file

@ -0,0 +1,391 @@
import { EventEmitter } from "eventemitter3";
import ReconnectingWebsocket from "reconnecting-websocket";
import { BroadcastEvents, Channels } from "./streaming.types.js";
function autobind(instance: any): void {
const prototype = Object.getPrototypeOf(instance);
const propertyNames = Object.getOwnPropertyNames(prototype);
for (const key of propertyNames) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
if (typeof descriptor?.value === "function" && key !== "constructor") {
Object.defineProperty(instance, key, {
value: instance[key].bind(instance),
enumerable: descriptor.enumerable,
configurable: descriptor.configurable,
writable: descriptor.writable,
});
}
}
}
export function urlQuery(
obj: Record<string, string | number | boolean | undefined>,
): string {
const params = Object.entries(obj)
.filter(([, v]) => (Array.isArray(v) ? v.length : v !== undefined))
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.reduce(
(a, [k, v]) => ((a[k] = v!), a),
{} as Record<string, string | number | boolean>,
);
return Object.entries(params)
.map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
.join("&");
}
type AnyOf<T extends Record<any, any>> = T[keyof T];
type StreamEvents = {
_connected_: void;
_disconnected_: void;
} & BroadcastEvents;
/**
* Misskey stream connection
*/
export default class Stream extends EventEmitter<StreamEvents> {
private stream: ReconnectingWebsocket;
public state: "initializing" | "reconnecting" | "connected" = "initializing";
private sharedConnectionPools: Pool[] = [];
private sharedConnections: SharedConnection[] = [];
private nonSharedConnections: NonSharedConnection[] = [];
private idCounter = 0;
constructor(
origin: string,
user: { token: string } | null,
options?: {
WebSocket?: any;
},
) {
super();
autobind(this);
options = options || {};
const query = urlQuery({
i: user?.token,
// To prevent cache of an HTML such as error screen
_t: Date.now(),
});
const wsOrigin = origin
.replace("http://", "ws://")
.replace("https://", "wss://");
this.stream = new ReconnectingWebsocket(
`${wsOrigin}/streaming?${query}`,
"",
{
minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91
WebSocket: options.WebSocket,
},
);
this.stream.addEventListener("open", this.onOpen);
this.stream.addEventListener("close", this.onClose);
this.stream.addEventListener("message", this.onMessage);
}
private genId(): string {
return (++this.idCounter).toString();
}
public useChannel<C extends keyof Channels>(
channel: C,
params?: Channels[C]["params"],
name?: string,
): Connection<Channels[C]> {
if (params) {
return this.connectToChannel(channel, params);
} else {
return this.useSharedConnection(channel, name);
}
}
private useSharedConnection<C extends keyof Channels>(
channel: C,
name?: string,
): SharedConnection<Channels[C]> {
let pool = this.sharedConnectionPools.find((p) => p.channel === channel);
if (pool == null) {
pool = new Pool(this, channel, this.genId());
this.sharedConnectionPools.push(pool);
}
const connection = new SharedConnection(this, channel, pool, name);
this.sharedConnections.push(connection);
return connection;
}
public removeSharedConnection(connection: SharedConnection): void {
this.sharedConnections = this.sharedConnections.filter(
(c) => c !== connection,
);
}
public removeSharedConnectionPool(pool: Pool): void {
this.sharedConnectionPools = this.sharedConnectionPools.filter(
(p) => p !== pool,
);
}
private connectToChannel<C extends keyof Channels>(
channel: C,
params: Channels[C]["params"],
): NonSharedConnection<Channels[C]> {
const connection = new NonSharedConnection(
this,
channel,
this.genId(),
params,
);
this.nonSharedConnections.push(connection);
return connection;
}
public disconnectToChannel(connection: NonSharedConnection): void {
this.nonSharedConnections = this.nonSharedConnections.filter(
(c) => c !== connection,
);
}
/**
* Callback of when open connection
*/
private onOpen(): void {
const isReconnect = this.state === "reconnecting";
this.state = "connected";
this.emit("_connected_");
// チャンネル再接続
if (isReconnect) {
for (const p of this.sharedConnectionPools) p.connect();
for (const c of this.nonSharedConnections) c.connect();
}
}
/**
* Callback of when close connection
*/
private onClose(): void {
if (this.state === "connected") {
this.state = "reconnecting";
this.emit("_disconnected_");
}
}
/**
* Callback of when received a message from connection
*/
private onMessage(message: { data: string }): void {
const { type, body } = JSON.parse(message.data);
if (type === "channel") {
const id = body.id;
let connections: Connection[];
connections = this.sharedConnections.filter((c) => c.id === id);
if (connections.length === 0) {
const found = this.nonSharedConnections.find((c) => c.id === id);
if (found) {
connections = [found];
}
}
for (const c of connections) {
c.emit(body.type, body.body);
c.inCount++;
}
} else {
this.emit(type, body);
}
}
/**
* Send a message to connection
*/
public send(typeOrPayload: any, payload?: any): void {
const data =
payload === undefined
? typeOrPayload
: {
type: typeOrPayload,
body: payload,
};
this.stream.send(JSON.stringify(data));
}
/**
* Close this connection
*/
public close(): void {
this.stream.close();
}
}
// TODO: これらのクラスを Stream クラスの内部クラスにすれば余計なメンバをpublicにしないで済むかも
// もしくは @internal を使う? https://www.typescriptlang.org/tsconfig#stripInternal
class Pool {
public channel: string;
public id: string;
protected stream: Stream;
public users = 0;
private disposeTimerId: any;
private isConnected = false;
constructor(stream: Stream, channel: string, id: string) {
this.channel = channel;
this.stream = stream;
this.id = id;
this.stream.on("_disconnected_", this.onStreamDisconnected);
}
private onStreamDisconnected(): void {
this.isConnected = false;
}
public inc(): void {
if (this.users === 0 && !this.isConnected) {
this.connect();
}
this.users++;
// タイマー解除
if (this.disposeTimerId) {
clearTimeout(this.disposeTimerId);
this.disposeTimerId = null;
}
}
public dec(): void {
this.users--;
// そのコネクションの利用者が誰もいなくなったら
if (this.users === 0) {
// また直ぐに再利用される可能性があるので、一定時間待ち、
// 新たな利用者が現れなければコネクションを切断する
this.disposeTimerId = setTimeout(() => {
this.disconnect();
}, 3000);
}
}
public connect(): void {
if (this.isConnected) return;
this.isConnected = true;
this.stream.send("connect", {
channel: this.channel,
id: this.id,
});
}
private disconnect(): void {
this.stream.off("_disconnected_", this.onStreamDisconnected);
this.stream.send("disconnect", { id: this.id });
this.stream.removeSharedConnectionPool(this);
}
}
export abstract class Connection<
Channel extends AnyOf<Channels> = any,
> extends EventEmitter<Channel["events"]> {
public channel: string;
protected stream: Stream;
public abstract id: string;
public name?: string; // for debug
public inCount = 0; // for debug
public outCount = 0; // for debug
constructor(stream: Stream, channel: string, name?: string) {
super();
this.stream = stream;
this.channel = channel;
this.name = name;
}
public send<T extends keyof Channel["receives"]>(
type: T,
body: Channel["receives"][T],
): void {
this.stream.send("ch", {
id: this.id,
type: type,
body: body,
});
this.outCount++;
}
public abstract dispose(): void;
}
class SharedConnection<
Channel extends AnyOf<Channels> = any,
> extends Connection<Channel> {
private pool: Pool;
public get id(): string {
return this.pool.id;
}
constructor(stream: Stream, channel: string, pool: Pool, name?: string) {
super(stream, channel, name);
this.pool = pool;
this.pool.inc();
}
public dispose(): void {
this.pool.dec();
this.removeAllListeners();
this.stream.removeSharedConnection(this);
}
}
class NonSharedConnection<
Channel extends AnyOf<Channels> = any,
> extends Connection<Channel> {
public id: string;
protected params: Channel["params"];
constructor(
stream: Stream,
channel: string,
id: string,
params: Channel["params"],
) {
super(stream, channel);
this.params = params;
this.id = id;
this.connect();
}
public connect(): void {
this.stream.send("connect", {
channel: this.channel,
id: this.id,
params: this.params,
});
}
public dispose(): void {
this.removeAllListeners();
this.stream.send("disconnect", { id: this.id });
this.stream.disconnectToChannel(this);
}
}

View file

@ -0,0 +1,197 @@
import type {
Antenna,
CustomEmoji,
DriveFile,
MeDetailed,
MessagingMessage,
Note,
Notification,
PageEvent,
User,
UserGroup,
} from "./entities.js";
type FIXME = any;
export type Channels = {
main: {
params: null;
events: {
notification: (payload: Notification) => void;
mention: (payload: Note) => void;
reply: (payload: Note) => void;
renote: (payload: Note) => void;
follow: (payload: User) => void; // 自分が他人をフォローしたとき
followed: (payload: User) => void; // 他人が自分をフォローしたとき
unfollow: (payload: User) => void; // 自分が他人をフォロー解除したとき
meUpdated: (payload: MeDetailed) => void;
pageEvent: (payload: PageEvent) => void;
urlUploadFinished: (payload: { marker: string; file: DriveFile }) => void;
readAllNotifications: () => void;
unreadNotification: (payload: Notification) => void;
unreadMention: (payload: Note["id"]) => void;
readAllUnreadMentions: () => void;
unreadSpecifiedNote: (payload: Note["id"]) => void;
readAllUnreadSpecifiedNotes: () => void;
readAllMessagingMessages: () => void;
messagingMessage: (payload: MessagingMessage) => void;
unreadMessagingMessage: (payload: MessagingMessage) => void;
readAllAntennas: () => void;
unreadAntenna: (payload: Antenna) => void;
readAllAnnouncements: () => void;
readAllChannels: () => void;
unreadChannel: (payload: Note["id"]) => void;
myTokenRegenerated: () => void;
reversiNoInvites: () => void;
reversiInvited: (payload: FIXME) => void;
signin: (payload: FIXME) => void;
registryUpdated: (payload: {
scope?: string[];
key: string;
value: any | null;
}) => void;
driveFileCreated: (payload: DriveFile) => void;
readAntenna: (payload: Antenna) => void;
};
receives: null;
};
homeTimeline: {
params: null;
events: {
note: (payload: Note) => void;
};
receives: null;
};
localTimeline: {
params: null;
events: {
note: (payload: Note) => void;
};
receives: null;
};
hybridTimeline: {
params: null;
events: {
note: (payload: Note) => void;
};
receives: null;
};
recommendedTimeline: {
params: null;
events: {
note: (payload: Note) => void;
};
receives: null;
};
globalTimeline: {
params: null;
events: {
note: (payload: Note) => void;
};
receives: null;
};
antenna: {
params: {
antennaId: Antenna["id"];
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
messaging: {
params: {
otherparty?: User["id"] | null;
group?: UserGroup["id"] | null;
};
events: {
message: (payload: MessagingMessage) => void;
deleted: (payload: MessagingMessage["id"]) => void;
read: (payload: MessagingMessage["id"][]) => void;
typers: (payload: User[]) => void;
};
receives: {
read: {
id: MessagingMessage["id"];
};
};
};
serverStats: {
params: null;
events: {
stats: (payload: FIXME) => void;
};
receives: {
requestLog: {
id: string | number;
length: number;
};
};
};
queueStats: {
params: null;
events: {
stats: (payload: FIXME) => void;
};
receives: {
requestLog: {
id: string | number;
length: number;
};
};
};
};
export type NoteUpdatedEvent =
| {
id: Note["id"];
type: "reacted";
body: {
reaction: string;
userId: User["id"];
};
}
| {
id: Note["id"];
type: "unreacted";
body: {
reaction: string;
userId: User["id"];
};
}
| {
id: Note["id"];
type: "deleted";
body: {
deletedAt: string;
};
}
| {
id: Note["id"];
type: "pollVoted";
body: {
choice: number;
userId: User["id"];
};
}
| {
id: Note["id"];
type: "replied";
body: {
id: Note["id"];
};
}
| {
id: Note["id"];
type: "updated";
body: {
updatedAt: string;
};
};
export type BroadcastEvents = {
noteUpdated: (payload: NoteUpdatedEvent) => void;
emojiAdded: (payload: {
emoji: CustomEmoji;
}) => void;
};

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>[dev] Iceshrimp</title>
<link rel="stylesheet" href="./style.scss" />
</head>
<body>
<div id="app"></div>
<script> let exports = {}; </script>
<script type="module" src="./init.ts"></script>
</body>
</html>

View file

@ -32,10 +32,10 @@ import { compareVersions } from "compare-versions";
import widgets from "@/widgets";
import directives from "@/directives";
import components from "@/components";
import { version, ui, lang, setHost, setSearchEngine } from "@/config";
import { version, ui, lang, setHost, setSearchEngine, updateLocale } from "@/config";
import { applyTheme } from "@/scripts/theme";
import { isDeviceDarkmode } from "@/scripts/is-device-darkmode";
import { i18n } from "@/i18n";
import { i18n, updateI18n } from "@/i18n";
import { confirm, alert, post, popup, toast, api } from "@/os";
import { stream } from "@/stream";
import * as sound from "@/scripts/sound";
@ -96,6 +96,23 @@ function checkForSplash() {
});
}
//#region Detect language & fetch translations
const localeVersion = localStorage.getItem('localeVersion');
const localeOutdated = (localeVersion == null || localeVersion !== version);
if (localeOutdated) {
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
if (res.status === 200) {
const newLocale = await res.text();
const parsedNewLocale = JSON.parse(newLocale);
localStorage.setItem('locale', newLocale);
localStorage.setItem('localeVersion', version);
updateLocale(parsedNewLocale);
updateI18n(parsedNewLocale);
}
}
//#endregion
// タッチデバイスでCSSの:hoverを機能させる
document.addEventListener("touchend", () => {}, { passive: true });
@ -363,6 +380,10 @@ function checkForSplash() {
JSON.parse(instance.defaultDarkTheme),
);
defaultStore.set("themeInitial", false);
} else if (defaultStore.state.darkMode) {
applyTheme(darkTheme.value);
} else {
applyTheme(lightTheme.value);
}
});

View file

@ -1,6 +1,6 @@
import { computed, reactive } from "vue";
import { api } from "./os";
import type * as Misskey from "iceshrimp-js";
import type * as Misskey from "@/iceshrimp-js";
// TODO: 他のタブと永続化されたstateを同期

View file

@ -3,7 +3,7 @@
import { Component, markRaw, Ref, ref, defineAsyncComponent } from "vue";
import { EventEmitter } from "eventemitter3";
import insertTextAtCursor from "insert-text-at-cursor";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import { apiUrl, url } from "@/config";
import MkPostFormDialog from "@/components/MkPostFormDialog.vue";
import MkWaitingDialog from "@/components/MkWaitingDialog.vue";

View file

@ -36,7 +36,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkButton from "@/components/MkButton.vue";
import { version } from "@/config";
import * as os from "@/os";

View file

@ -81,7 +81,7 @@
<script lang="ts" setup>
import { computed, defineAsyncComponent } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue";

View file

@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkMiniChart from "@/components/MkMiniChart.vue";
import * as os from "@/os";
import { acct } from "@/filters/user";

View file

@ -46,7 +46,7 @@
<script lang="ts" setup>
import { ref } from "vue";
import JSON5 from "json5";
import { Endpoints } from "iceshrimp-js";
import { Endpoints } from "@/iceshrimp-js";
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
import MkTextarea from "@/components/form/textarea.vue";

View file

@ -29,7 +29,7 @@
<script lang="ts" setup>
import { computed, watch, provide } from "vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import XNotes from "@/components/MkNotes.vue";
import { $i } from "@/account";
import { i18n } from "@/i18n";

View file

@ -4,7 +4,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
import * as os from "@/os";
import { mainRouter } from "@/router";
import { i18n } from "@/i18n";

View file

@ -340,7 +340,7 @@
import { watch } from "vue";
import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue";
import type * as iceshrimp from "iceshrimp-js";
import type * as iceshrimp from "@/iceshrimp-js";
import MkChart from "@/components/MkChart.vue";
import MkObjectView from "@/components/MkObjectView.vue";
import FormLink from "@/components/form/link.vue";

View file

@ -89,7 +89,7 @@
<script lang="ts" setup>
import { ref, markRaw, onMounted, onUnmounted, watch } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue";
import MkButton from "@/components/MkButton.vue";

View file

@ -56,7 +56,7 @@
<script lang="ts" setup>
import { onMounted, watch } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Misskey from "@/iceshrimp-js";
import autosize from "autosize";
//import insertTextAtCursor from 'insert-text-at-cursor';
import { throttle } from "throttle-debounce";

View file

@ -84,7 +84,7 @@
<script lang="ts" setup>
import {} from "vue";
import * as mfm from "mfm-js";
import type * as Misskey from "iceshrimp-js";
import type * as Misskey from "@/iceshrimp-js";
import XMediaList from "@/components/MkMediaList.vue";
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
import MkUrlPreview from "@/components/MkUrlPreview.vue";

View file

@ -98,8 +98,8 @@
<script lang="ts" setup>
import { computed, watch, onMounted, nextTick, onBeforeUnmount } from "vue";
import * as Misskey from "iceshrimp-js";
import * as Acct from "iceshrimp-js/built/acct";
import * as Misskey from "@/iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import XMessage from "./messaging-room.message.vue";
import XForm from "./messaging-room.form.vue";
import XList from "@/components/MkDateSeparatedList.vue";

View file

@ -115,7 +115,7 @@
<script lang="ts" setup>
import { watch } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js"
import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue";
import MkTextarea from "@/components/form/textarea.vue";

View file

@ -39,7 +39,7 @@
<script lang="ts" setup>
import { computed, defineComponent, watch } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import XNoteDetailed from "@/components/MkNoteDetailed.vue";
import XNotes from "@/components/MkNotes.vue";
import MkRemoteCaution from "@/components/MkRemoteCaution.vue";

View file

@ -54,7 +54,7 @@
import { computed, ref, watch } from "vue";
import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue";
import { notificationTypes } from "iceshrimp-js";
import { notificationTypes } from "@/iceshrimp-js";
import XNotifications from "@/components/MkNotifications.vue";
import XNotes from "@/components/MkNotes.vue";
import * as os from "@/os";

View file

@ -378,6 +378,7 @@ watch(swipeOnDesktop, () => {
watch(lang, () => {
localStorage.setItem("lang", lang.value as string);
localStorage.removeItem("locale");
localStorage.removeItem('localeVersion');
});
watch(fontSize, () => {

View file

@ -59,7 +59,7 @@ import * as os from "@/os";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import { $i } from "@/account";
import { toString } from "iceshrimp-js/built/acct";
import { Acct } from "@/iceshrimp-js";
let moveToAccount = $ref("");
let accountAlias = $ref([""]);
@ -71,7 +71,7 @@ async function init() {
const aka = await os.api("users/show", { userIds: $i.alsoKnownAs });
accountAlias =
aka && aka.length > 0
? aka.map((user) => `@${toString(user)}`)
? aka.map((user) => `@${Acct.toString(user)}`)
: [""];
} else {
accountAlias = [""];

View file

@ -50,7 +50,7 @@
<script lang="ts" setup>
import { defineAsyncComponent } from "vue";
import { notificationTypes } from "iceshrimp-js";
import { notificationTypes } from "@/iceshrimp-js";
import FormButton from "@/components/MkButton.vue";
import FormLink from "@/components/form/link.vue";
import FormSection from "@/components/form/section.vue";

View file

@ -33,9 +33,9 @@
// SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
import {} from "vue";
import { noteVisibilities } from "iceshrimp-js";
import * as Acct from "iceshrimp-js/built/acct";
import * as Misskey from "iceshrimp-js";
import { noteVisibilities } from "@/iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import * as Misskey from "@/iceshrimp-js";
import MkButton from "@/components/MkButton.vue";
import XPostForm from "@/components/MkPostForm.vue";
import * as os from "@/os";

View file

@ -340,7 +340,7 @@
<script lang="ts" setup>
import { computed, watch } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkChart from "@/components/MkChart.vue";
import MkObjectView from "@/components/MkObjectView.vue";
import FormTextarea from "@/components/form/textarea.vue";

View file

@ -25,7 +25,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkPagination from "@/components/MkPagination.vue";
const props = defineProps<{

View file

@ -24,7 +24,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkUserInfo from "@/components/MkUserInfo.vue";
import MkPagination from "@/components/MkPagination.vue";

View file

@ -24,8 +24,8 @@ import {
onUnmounted,
watch,
} from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import * as misskey from "iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import * as misskey from "@/iceshrimp-js";
import XFollowList from "./follow-list.vue";
import * as os from "@/os";
import { definePageMetadata } from "@/scripts/page-metadata";

View file

@ -24,8 +24,8 @@ import {
onUnmounted,
watch,
} from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import * as misskey from "iceshrimp-js";
import { Acct } from "@/iceshrimp-js"
import * as misskey from "@/iceshrimp-js";
import XFollowList from "./follow-list.vue";
import * as os from "@/os";
import { definePageMetadata } from "@/scripts/page-metadata";

View file

@ -15,7 +15,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import * as misskey from "iceshrimp-js";
import * as misskey from "@/iceshrimp-js";
import MkGalleryPostPreview from "@/components/MkGalleryPostPreview.vue";
import MkPagination from "@/components/MkPagination.vue";

View file

@ -365,7 +365,7 @@ import { defineAsyncComponent, onMounted, onUnmounted } from "vue";
import calcAge from "s-age";
import cityTimezones from "city-timezones";
import XUserTimeline from "./index.timeline.vue";
import type * as misskey from "iceshrimp-js";
import type * as misskey from "@/iceshrimp-js";
import XNote from "@/components/MkNote.vue";
import MkFollowButton from "@/components/MkFollowButton.vue";
import MkRemoteCaution from "@/components/MkRemoteCaution.vue";

Some files were not shown because too many files have changed in this diff Show more