123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- <template>
- <div class="profile-carousel-component">
- <template v-if="showSplash">
- <SplashScreen />
- </template>
- <template v-else>
- <template v-if="emptyFeed">
- <div class="bg-dark d-flex justify-content-center align-items-center w-100 h-100">
- <div>
- <h2 class="text-light">Oops! This account hasn't posted yet or is private.</h2>
- <a href="/" class="font-weight-bold text-muted">Go back home</a>
- </div>
- </div>
- </template>
- <template v-else>
- <FullscreenCarousel
- :feed="feed"
- :withLinks="withLinks"
- :withOverlay="withOverlay"
- :autoPlay="autoPlay"
- :autoPlayInterval="autoPlayInterval"
- :canLoadMore="hasMoreData"
- @load-more="loadMoreData"
- />
- </template>
- </template>
- </div>
- </template>
- <script>
- import SplashScreen from './SplashScreen.vue';
- import FullscreenCarousel from './FullscreenCarousel.vue'
- export default {
- props: ['profile-id'],
- components: {
- SplashScreen,
- FullscreenCarousel
- },
- data() {
- return {
- showSplash: true,
- profile: {},
- feed: [],
- emptyFeed: false,
- hasMoreData: false,
- withLinks: true,
- withOverlay: true,
- autoPlay: false,
- autoPlayInterval: 5000,
- maxId: null
- }
- },
- mounted() {
- const url = new URL(window.location.href);
- const params = url.searchParams;
- if(params.has('linkless') == true) {
- this.withLinks = false;
- }
- if(params.has('clean') == true) {
- this.withOverlay = false;
- }
- if(params.has('interval') == true) {
- const val = parseInt(params.get('interval'));
- const valid = this.validateIntegerRange(val, { min: 1000, max: 30000 })
- if(valid) {
- this.autoPlayInterval = val;
- }
- }
- if(params.has('autoplay') == true) {
- this.autoPlay = true;
- }
- this.init();
- },
- methods: {
- async init() {
- await axios.get(`/api/pixelfed/v1/accounts/${this.profileId}/statuses?media_type=photo&limit=10`)
- .then(res => {
- if(!res || !res.data || !res.data.length) {
- this.emptyFeed = true;
- return;
- }
- this.maxId = this.arrayMinId(res.data);
- const posts = res.data.flatMap(post =>
- post.media_attachments.filter(media => {
- return ['image/jpeg','image/png', 'image/jpg', 'image/webp'].includes(media.mime)
- }).map(media => ({
- media_url: media.url,
- id: post.id,
- caption: post.content_text,
- created_at: post.created_at,
- url: post.url,
- account: {
- username: post.account.username,
- url: post.account.url
- }
- }))
- );
- this.feed = posts;
- this.hasMoreData = res.data.length === 10;
- setTimeout(() => {
- this.showSplash = false;
- }, 3000);
- })
- },
- async fetchMore() {
- await axios.get(`/api/pixelfed/v1/accounts/${this.profileId}/statuses?media_type=photo&limit=10&max_id=${this.maxId}`)
- .then(res => {
- this.maxId = this.arrayMinId(res.data);
- const posts = res.data.flatMap(post =>
- post.media_attachments.filter(media => {
- return ['image/jpeg','image/png', 'image/jpg', 'image/webp'].includes(media.mime)
- }).map(media => ({
- media_url: media.url,
- id: post.id,
- caption: post.content_text,
- created_at: post.created_at,
- url: post.url,
- account: {
- username: post.account.username,
- url: post.account.url
- }
- }))
- );
- this.feed.push(...posts);
- this.hasMoreData = res.data.length === 10;
- })
- },
- arrayMinId(arr) {
- if (arr.length === 0) return null;
- let smallest = BigInt(arr[0].id);
- for (let i = 1; i < arr.length; i++) {
- const current = BigInt(arr[i].id);
- if (current < smallest) {
- smallest = current;
- }
- }
- return smallest.toString();
- },
- loadMoreData() {
- this.fetchMore();
- },
- validateIntegerRange(value, options = {}) {
- if (typeof value !== 'number' || !Number.isInteger(value)) {
- return false;
- }
- const {
- min = Number.MIN_SAFE_INTEGER,
- max = Number.MAX_SAFE_INTEGER,
- inclusiveMin = true,
- inclusiveMax = true
- } = options;
- if (min !== undefined && !Number.isInteger(min)) {
- return false;
- }
- if (max !== undefined && !Number.isInteger(max)) {
- return false;
- }
- if (min > max) {
- return false;
- }
- const aboveMin = inclusiveMin ? value >= min : value > min;
- const belowMax = inclusiveMax ? value <= max : value < max;
- return aboveMin && belowMax;
- }
- }
- }
- </script>
- <style type="text/css">
- .profile-carousel-component {
- display: block;
- width: 100dvw;
- height: 100dvh;
- z-index: 2;
- background: #000;
- }
- </style>
|