feat(client): add rss-marquee widget
This commit is contained in:
		
							parent
							
								
									bbdc52a7ea
								
							
						
					
					
						commit
						6ba888f476
					
				
					 7 changed files with 141 additions and 8 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -1246,6 +1246,7 @@ _widgets: | |||
|   trends: "トレンド" | ||||
|   clock: "時計" | ||||
|   rss: "RSSリーダー" | ||||
|   rssMarquee: "RSSリーダー(マーキー)" | ||||
|   activity: "アクティビティ" | ||||
|   photos: "フォト" | ||||
|   digitalClock: "デジタル時計" | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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', | ||||
|  |  | |||
							
								
								
									
										115
									
								
								packages/client/src/widgets/rss-marquee.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								packages/client/src/widgets/rss-marquee.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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== | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue