diff --git a/CHANGELOG.md b/CHANGELOG.md index 063ebf5251abbe8abb4abeb84a6a13a0db4fb0a4..405c92bb3a9c2e01d1d727c1aa893f134458115d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ You should also include the user name that made the change. - Client: Improve control panel @syuilo - Client: Show warning in control panel when there is an unresolved abuse report @syuilo - Client: Add instance-cloud widget @syuilo +- Client: Add rss-marquee widget @syuilo - Make possible to delete an account by admin @syuilo - Improve player detection in URL preview @mei23 - Add Badge Image to Push Notification #8012 @tamaina diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 743640725860a4cea0b7f9c4c010f9b41dffacc9..1f52c2c2590786d7a00167ccea2b5f49a7789df6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1246,6 +1246,7 @@ _widgets: trends: "トレンド" clock: "時計" rss: "RSSリーダー" + rssMarquee: "RSSリーダー(マーã‚ー)" activity: "アクティビティ" photos: "フォト" digitalClock: "デジタル時計" diff --git a/packages/client/package.json b/packages/client/package.json index 56467fc84206bd093f1c0f8b44a98676154bed68..54ee7dee53c9a692414cfc0648c1b4159a69e9dc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -76,6 +76,7 @@ "vanilla-tilt": "1.7.2", "vite": "3.0.0-beta.5", "vue": "3.2.37", + "vue-marquee-text-component": "2.0.1", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "4.0.1", "websocket": "1.0.34", diff --git a/packages/client/src/widgets/index.ts b/packages/client/src/widgets/index.ts index feda16c91dd133eed6151a5d94f0e350d30bdb0c..ed65f52913e9c2843f484547375df55fed186a37 100644 --- a/packages/client/src/widgets/index.ts +++ b/packages/client/src/widgets/index.ts @@ -6,6 +6,7 @@ export default function(app: App) { app.component('MkwTimeline', defineAsyncComponent(() => import('./timeline.vue'))); app.component('MkwCalendar', defineAsyncComponent(() => import('./calendar.vue'))); app.component('MkwRss', defineAsyncComponent(() => import('./rss.vue'))); + app.component('MkwRssMarquee', defineAsyncComponent(() => import('./rss-marquee.vue'))); app.component('MkwTrends', defineAsyncComponent(() => import('./trends.vue'))); app.component('MkwClock', defineAsyncComponent(() => import('./clock.vue'))); app.component('MkwActivity', defineAsyncComponent(() => import('./activity.vue'))); @@ -29,13 +30,14 @@ export const widgets = [ 'timeline', 'calendar', 'rss', + 'rssMarquee', 'trends', 'clock', 'activity', 'photos', 'digitalClock', 'federation', - 'instance-cloud', + 'instanceCloud', 'postForm', 'slideshow', 'serverMetric', diff --git a/packages/client/src/widgets/rss-marquee.vue b/packages/client/src/widgets/rss-marquee.vue new file mode 100644 index 0000000000000000000000000000000000000000..e23f916d3a58568af64c01f91638a80fe369eab2 --- /dev/null +++ b/packages/client/src/widgets/rss-marquee.vue @@ -0,0 +1,115 @@ +<template> +<MkContainer :naked="widgetProps.transparent" :show-header="widgetProps.showHeader" class="mkw-rss-marquee"> + <template #header><i class="fas fa-rss-square"></i>RSS</template> + <template #func><button class="_button" @click="configure"><i class="fas fa-cog"></i></button></template> + + <div class="ekmkgxbk"> + <MkLoading v-if="fetching"/> + <div v-else class="feed"> + <MarqueeText :duration="widgetProps.speed" :reverse="widgetProps.reverse"> + <a v-for="item in items" class="item" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a> + </MarqueeText> + </div> + </div> +</MkContainer> +</template> + +<script lang="ts" setup> +import { onMounted, onUnmounted, ref, watch } from 'vue'; +import MarqueeText from 'vue-marquee-text-component'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { GetFormResultType } from '@/scripts/form'; +import * as os from '@/os'; +import MkContainer from '@/components/ui/container.vue'; +import { useInterval } from '@/scripts/use-interval'; + +const name = 'rssMarquee'; + +const widgetPropsDef = { + url: { + type: 'string' as const, + default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', + }, + showHeader: { + type: 'boolean' as const, + default: false, + }, + transparent: { + type: 'boolean' as const, + default: false, + }, + speed: { + type: 'radio' as const, + default: 70, + options: [{ + value: 170, label: 'very slow', + }, { + value: 100, label: 'slow', + }, { + value: 70, label: 'medium', + }, { + value: 40, label: 'fast', + }, { + value: 20, label: 'very fast', + }], + }, + reverse: { + type: 'boolean' as const, + default: false, + }, +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +// ç¾æ™‚点ã§ã¯vueã®åˆ¶é™ã«ã‚ˆã‚Šimportã—ãŸtypeをジェãƒãƒªãƒƒã‚¯ã«æ¸¡ã›ãªã„ +//const props = defineProps<WidgetComponentProps<WidgetProps>>(); +//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); +const props = defineProps<{ widget?: Widget<WidgetProps>; }>(); +const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>(); + +const { widgetProps, configure } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +const items = ref([]); +const fetching = ref(true); + +const tick = () => { + fetch(`https://api.rss2json.com/v1/api.json?rss_url=${widgetProps.url}`, {}).then(res => { + res.json().then(feed => { + items.value = feed.items; + fetching.value = false; + }); + }); +}; + +watch(() => widgetProps.url, tick); + +useInterval(tick, 60000, { + immediate: true, + afterMounted: true, +}); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> + +<style lang="scss" scoped> +.ekmkgxbk { + > .feed { + padding: 0; + font-size: 0.9em; + + ::v-deep(.item) { + display: inline-block; + color: var(--fg); + margin: 12px 3em 12px 0; + } + } +} +</style> diff --git a/packages/client/src/widgets/rss.vue b/packages/client/src/widgets/rss.vue index e5da291a8dd15719f2820f0f24a606ec0c315eee..ea896478a3cbfc1b7618f20cb0cfe5fb87918c0f 100644 --- a/packages/client/src/widgets/rss.vue +++ b/packages/client/src/widgets/rss.vue @@ -6,7 +6,7 @@ <div class="ekmkgxbj"> <MkLoading v-if="fetching"/> <div v-else class="feed"> - <a v-for="item in items" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a> + <a v-for="item in items" class="item" :href="item.link" rel="nofollow noopener" target="_blank" :title="item.title">{{ item.title }}</a> </div> </div> </MkContainer> @@ -23,14 +23,14 @@ import { useInterval } from '@/scripts/use-interval'; const name = 'rss'; const widgetPropsDef = { - showHeader: { - type: 'boolean' as const, - default: true, - }, url: { type: 'string' as const, default: 'http://feeds.afpbb.com/rss/afpbb/afpbbnews', }, + showHeader: { + type: 'boolean' as const, + default: true, + }, }; type WidgetProps = GetFormResultType<typeof widgetPropsDef>; @@ -79,7 +79,7 @@ defineExpose<WidgetComponentExpose>({ padding: 0; font-size: 0.9em; - > a { + > .item { display: block; padding: 8px 16px; color: var(--fg); diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index 7bbbe08d377c218e402d664a0b91b5c4644fb54a..58df1576f6852dd9ec6875a6146ddeb12c33ff1e 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -1221,6 +1221,11 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" +core-js@^3.18.0: + version "3.23.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.3.tgz#3b977612b15da6da0c9cc4aec487e8d24f371112" + integrity sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q== + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4252,12 +4257,20 @@ vue-eslint-parser@^9.0.1: lodash "^4.17.21" semver "^7.3.6" +vue-marquee-text-component@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vue-marquee-text-component/-/vue-marquee-text-component-2.0.1.tgz#62691df195f755471fa9bdc9b1969f836a922b9a" + integrity sha512-dbeRwDY5neOJcWZrDFU2tJMhPSsxN25ZpNYeZdt0jkseg1MbyGKzrfEH9nrCFZRkEfqhxG+ukyzwVwR9US5sTQ== + dependencies: + core-js "^3.18.0" + vue "^3.2.19" + vue-prism-editor@2.0.0-alpha.2: version "2.0.0-alpha.2" resolved "https://registry.yarnpkg.com/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz#aa53a88efaaed628027cbb282c2b1d37fc7c5c69" integrity sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w== -vue@3.2.37: +vue@3.2.37, vue@^3.2.19: version "3.2.37" resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e" integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==