Fixed zoomIn/Out slider in editBook + now you can view books offline. click on a book on your home page

This commit is contained in:
Sagi Dayan 2020-06-04 19:07:34 -04:00
parent c75afd74f9
commit 3364adbe31
14 changed files with 219 additions and 24 deletions

View file

@ -0,0 +1,33 @@
'use strict'
/** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */
class BookCallPageAuth {
/**
* @param {object} ctx
* @param {Request} ctx.request
* @param {Function} next
*/
async handle(ctx, next) {
const {request, auth, response, book, call} = ctx;
// call next to advance the request
const user = auth.user;
if (book.user_id) {
// Belongs to a user. Check if the book user has a connection with this
// user
if (book.user_id === user.id) {
await next();
} else if (call.parent_id === user.id || call.guest_id === user.id) {
await next();
} else {
response.status(403);
response.send({code: 403, message: 'Book is private'});
}
} else {
await next();
}
}
}
module.exports = BookCallPageAuth

View file

@ -2,8 +2,7 @@
/** @typedef {import('@adonisjs/framework/src/Request')} Request */ /** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */ /** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */ /** @typedef {import('@adonisjs/framework/src/View')} View */
const Book = use('App/Models/Book');
const UserChildUtils = use('App/Utils/UserChildUtils');
class BookPageAuth { class BookPageAuth {
/** /**
* @param {object} ctx * @param {object} ctx
@ -11,7 +10,7 @@ class BookPageAuth {
* @param {Function} next * @param {Function} next
*/ */
async handle(ctx, next) { async handle(ctx, next) {
const {request, auth, response, book, call} = ctx; const {request, auth, response, book} = ctx;
// call next to advance the request // call next to advance the request
const user = auth.user; const user = auth.user;
if (book.user_id) { if (book.user_id) {
@ -19,8 +18,6 @@ class BookPageAuth {
// user // user
if (book.user_id === user.id) { if (book.user_id === user.id) {
await next(); await next();
} else if (call.parent_id === user.id || call.guest_id === user.id) {
await next();
} else { } else {
response.status(403); response.status(403);
response.send({code: 403, message: 'Book is private'}); response.send({code: 403, message: 'Book is private'});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -8118,9 +8118,6 @@ video {
border-radius: 8px; border-radius: 8px;
color: white; } color: white; }
.book-thumb.page-preview {
flex-basis: unset; }
.book-stitch-preview-left::before { .book-stitch-preview-left::before {
content: ''; content: '';
height: 100%; height: 100%;

View file

@ -505,7 +505,7 @@ video{
} }
} }
.book-thumb.page-preview{ .book-thumb.page-preview{
flex-basis: unset; // flex-basis: unset;
} }
.book-stitch-preview-left{ .book-stitch-preview-left{
&::before{ &::before{

View file

@ -15,6 +15,7 @@ import Settings from "../views/settings.vue";
import Call from "../views/call.vue"; import Call from "../views/call.vue";
import ChildProfile from "../views/child_profile.vue"; import ChildProfile from "../views/child_profile.vue";
import EditBook from "../views/edit_book.vue"; import EditBook from "../views/edit_book.vue";
import BookOfflineViewer from "../views/BookOfflineViewer.vue";
// Call Views // Call Views
import CallLobby from "../views/call_views/Lobby.vue"; import CallLobby from "../views/call_views/Lobby.vue";
@ -35,6 +36,10 @@ const routes: RouteConfig[] = [
path: "/create/book", path: "/create/book",
component: EditBook component: EditBook
}, },
{
path: "/book/:id",
component: BookOfflineViewer
},
{ {
path: "/call/:id", path: "/call/:id",
component: Call, component: Call,

View file

@ -0,0 +1,137 @@
<template>
<div class="is-fullwidth is-fullheight-container p-l-lg p-r-lg">
<Loading v-if="loading" />
<div :class="`is-fullheight-container ${flipbookRef ? '' : 'is-transparent'}`" v-else>
<div class="book-view m-sm m-r-md">
<div
class="go-left m-r-sm"
style="display: inline-block; align-items: center; position: absolute; left:0px; top:0px"
>
<button
class="button book-flip-buttons"
:disabled="!canFlipLeft"
@click="onLeftClicked()"
>
<i class="fa fa-fw fa-arrow-left"></i>
</button>
</div>
<flipbook
class="flipbook"
:pages="pages"
:forwardDirection="book.ltr ? 'right': 'left'"
:zooms="null"
:enabled="true"
@on-mounted="bookMounted()"
ref="flipbook"
v-slot="flipbook"
>
<!-- @flip-left-start="onFlip('left')" -->
<!-- @flip-right-start="onFlip('right')" -->
<div class="page-progress has-text-centered m-b-none">
<p>Page {{ flipbook.page }} of {{ flipbook.numPages }}</p>
</div>
</flipbook>
<div
class="go-right m-l-sm"
style="display: inline-block; align-items: center; position: absolute; right:0px; top:0px"
>
<button
class="button book-flip-buttons"
:disabled="!canFlipRight"
@click="onRightClicked()"
>
<i class="fa fa-fw fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { mapGetters, mapActions } from "vuex";
import Flipbook from "../components/flipbook/flipbook.cjs.js";
import Loading from "../../shared/components/Loading/Loading.vue";
export default {
name: "BookOfflineViewer",
components: {
Flipbook,
Loading
},
created() {
const bookId = Number(this.$route.params.id);
if (!this.user || !bookId) {
this.$router.replace({ path: `/` });
}
this.user.books.forEach(b => {
if (this.book) return;
if (b.id === bookId) {
console.log("Found Book");
this.book = b;
}
});
if (!this.book) {
this.notify({ message: "Book Not Found!", level: "danger" });
this.$router.replace({ path: `/` });
} else {
// create pages
// /u/books/:bookId/page/:pageNumber
const pages = [null];
for (let i = 1; i < this.book.pages + 1; i++) {
pages.push(`/u/books/${bookId}/page/${i}`);
}
this.pages = pages;
this.loading = false;
}
},
methods: {
bookMounted() {
console.log("Book Mounted!");
if (this.$refs.flipbook) {
console.log("Found!");
this.flipbookRef = true;
// this.$refs.flipbook.onResize();
// console.log("resized");
} else {
console.log("Still Null!!");
}
},
onLeftClicked() {
this.$refs.flipbook.flipLeft();
},
onRightClicked() {
this.$refs.flipbook.flipRight();
},
...mapActions(["notify"])
},
computed: {
canFlipLeft() {
return this.flipbookRef && this.$refs.flipbook.canFlipLeft;
},
canFlipRight() {
return this.flipbookRef && this.$refs.flipbook.canFlipRight;
},
...mapGetters(["user"])
},
data() {
return {
loading: true,
book: <IBook>null,
pages: <string[]>[],
flipbookRef: null
};
}
};
interface IBook {
id: number;
pages: number;
user_id?: number;
title: string;
ltr: boolean;
created_at: string;
}
</script>

View file

@ -199,7 +199,7 @@
<div class="book-text"> <div class="book-text">
<div>Previouse Page</div> <div>Previouse Page</div>
</div> </div>
<div class="book-cover"> <div class="book-cover is-flex">
<img <img
:src="pages[currentPage - 1] ? pages[currentPage - 1].base64: ''" :src="pages[currentPage - 1] ? pages[currentPage - 1].base64: ''"
/> />
@ -245,30 +245,33 @@
<input <input
type="range" type="range"
min="0" min="0"
max="1000" :max="DEFAULT_ZOOM*2"
v-model="pageZoom" v-model="pageZoom"
:disabled="!pages[currentPage].imageLoaded" :disabled="!pages[currentPage].imageLoaded"
ref="zoomRangeSlider"
@mouseup="blurZoomSlider()"
/> />
<div class="is-flex is-justify-between"> <div class="is-flex is-justify-between">
<button <button
type="button" type="button"
class="button is-rounded is-outlined is-small" class="button is-rounded is-outlined is-small"
:disabled="!pages[currentPage].imageLoaded" :disabled="!pages[currentPage].imageLoaded || pageZoom <= 0"
@click="pageZoom-=2" @click="zoom(false)"
> >
<i class="fa fa-fw fa-minus"></i> <i class="fa fa-fw fa-minus"></i>
</button> </button>
<button <button
type="button" type="button"
class="button is-rounded is-outlined is-small" class="button is-rounded is-outlined is-small"
:disabled="!pages[currentPage].imageLoaded" :disabled="!pages[currentPage].imageLoaded || pageZoom >= DEFAULT_ZOOM * 2"
@click="pageZoom+=2" @click="zoom(true)"
> >
<i class="fa fa-fw fa-plus"></i> <i class="fa fa-fw fa-plus"></i>
</button> </button>
</div> </div>
</div> </div>
<div class="rotations is-flex is-justify-between m-b-lg"> <div class="rotations is-flex is-justify-between m-b-lg">
<label class="label">Rotate Image</label>
<button <button
class="button" class="button"
@click="onRotateClicked(false)" @click="onRotateClicked(false)"
@ -382,7 +385,7 @@ import Services from "../../services";
const DEFAULT_PAGE_WIDTH = 350; const DEFAULT_PAGE_WIDTH = 350;
const DEFAULT_PAGE_HEIGHT = 350; const DEFAULT_PAGE_HEIGHT = 350;
const DEFAULT_ZOOM = 500; const DEFAULT_ZOOM = 250;
const MIME_TYPE = "image/jpeg"; const MIME_TYPE = "image/jpeg";
const COMPRESSION_RATE = 0.4; const COMPRESSION_RATE = 0.4;
export default { export default {
@ -448,6 +451,7 @@ export default {
const page = this.pages[this.currentPage]; const page = this.pages[this.currentPage];
console.log(newVal, oldVal); console.log(newVal, oldVal);
const delta = Math.abs(newVal - oldVal); const delta = Math.abs(newVal - oldVal);
if (!page.croppa.zoomOut) return;
if (newVal < oldVal) { if (newVal < oldVal) {
//zoomOut //zoomOut
for (let i = delta; i > 0; i--) page.croppa.zoomOut(); for (let i = delta; i > 0; i--) page.croppa.zoomOut();
@ -657,6 +661,16 @@ export default {
onRightClicked() { onRightClicked() {
this.$refs.flipbook.flipRight(); this.$refs.flipbook.flipRight();
return true; return true;
},
zoom(zoomIn: boolean) {
const amount = zoomIn ? 2 : -2;
this.pageZoom = Number(this.pageZoom) + amount;
},
blurZoomSlider() {
const slider: HTMLInputElement = this.$refs.zoomRangeSlider;
if (slider) {
slider.blur();
}
} }
}, },
computed: { computed: {
@ -703,7 +717,8 @@ export default {
uploading: false, uploading: false,
errors: {}, errors: {},
DEFAULT_PAGE_WIDTH, DEFAULT_PAGE_WIDTH,
DEFAULT_PAGE_HEIGHT DEFAULT_PAGE_HEIGHT,
DEFAULT_ZOOM
}; };
} }
}; };

View file

@ -139,7 +139,12 @@
<i class="fa fa-fw fa-book"></i> My Books <i class="fa fa-fw fa-book"></i> My Books
</h2> </h2>
<div class="is-flex m-b-md is-justify-centered has-wrap"> <div class="is-flex m-b-md is-justify-centered has-wrap">
<div class="book-thumb m-l-md" v-for="book in user.books" :key="book.id"> <div
class="book-thumb enabled m-l-md"
v-for="book in user.books"
:key="book.id"
@click="goToBook(book)"
>
<div class="book-cover"> <div class="book-cover">
<figure class="image is-2by3 m-a"> <figure class="image is-2by3 m-a">
<img :src="`/u/books/${book.id}/thumbnail`" /> <img :src="`/u/books/${book.id}/thumbnail`" />
@ -246,6 +251,9 @@ export default {
goChildProfile(connection) { goChildProfile(connection) {
this.$router.push({ path: `/child/${connection.id}` }); this.$router.push({ path: `/child/${connection.id}` });
}, },
goToBook(book) {
this.$router.push({ path: `/book/${book.id}` });
},
async onChildCreated(child) { async onChildCreated(child) {
this.loading = true; this.loading = true;
await this.getUser(); await this.getUser();

View file

@ -42,6 +42,7 @@ const globalMiddleware =
auth: 'Adonis/Middleware/Auth', auth: 'Adonis/Middleware/Auth',
guest: 'Adonis/Middleware/AllowGuestOnly', guest: 'Adonis/Middleware/AllowGuestOnly',
adminAuth: 'App/Middleware/AdminAuth', adminAuth: 'App/Middleware/AdminAuth',
BookCallPageAuth: 'App/Middleware/BookCallPageAuth',
BookPageAuth: 'App/Middleware/BookPageAuth', BookPageAuth: 'App/Middleware/BookPageAuth',
BookContext: 'App/Middleware/BookContext', BookContext: 'App/Middleware/BookContext',
CallContext: 'App/Middleware/CallContext', CallContext: 'App/Middleware/CallContext',

View file

@ -61,7 +61,9 @@ Route
.get( .get(
'/u/call/:callId/books/:bookId/page/:pageNumber', '/u/call/:callId/books/:bookId/page/:pageNumber',
'BookApiController.getPage') 'BookApiController.getPage')
.middleware(['auth', 'BookContext', 'CallContext', 'BookPageAuth']); .middleware(['auth', 'BookContext', 'CallContext', 'BookCallPageAuth']);
Route.get('/u/books/:bookId/page/:pageNumber', 'BookApiController.getPage')
.middleware(['auth', 'BookContext', 'BookPageAuth']);
/** /**
* Public book thumbnail * Public book thumbnail
*/ */