index.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import React, {
  2. useEffect, useState, useRef, useCallback,
  3. } from 'react';
  4. import queryString from 'query-string';
  5. import Icon from '../Icon';
  6. import Drawer from '../Drawer';
  7. import Typography from '../Typography';
  8. import Item from '../AnnotationItem';
  9. import {
  10. AnnotationType, ViewportType, ScrollStateType, PositionType,
  11. } from '../../constants/type';
  12. import { downloadFileWithUri, watchScroll } from '../../helpers/utility';
  13. import { getAnnotationText } from '../../helpers/annotation';
  14. import { Separator } from '../../global/otherStyled';
  15. import {
  16. Container, Head, Body, IconWrapper,
  17. } from '../../global/sidebarStyled';
  18. type Props = {
  19. isActive?: boolean;
  20. close: () => void;
  21. annotations: AnnotationType[];
  22. viewport: ViewportType;
  23. scale: number;
  24. pdf: any;
  25. };
  26. const AnnotationsList: React.FunctionComponent<Props> = ({
  27. isActive = false,
  28. close,
  29. annotations,
  30. viewport,
  31. scale,
  32. pdf,
  33. }: Props) => {
  34. const [renderQueue, setQueue] = useState<AnnotationType[]>([]);
  35. const containerRef = useRef<HTMLDivElement>(null);
  36. const innerRef = useRef<HTMLDivElement>(null);
  37. const handleExport = (): void => {
  38. const parsed = queryString.parse(window.location.search);
  39. const uri = `/api/v1/output.xfdf?f=${parsed.token}`;
  40. downloadFileWithUri('output.xfdf', uri);
  41. };
  42. const getText = async (page: number, position: PositionType[]): Promise<any> => {
  43. const text = await getAnnotationText({
  44. pdf,
  45. viewport,
  46. scale,
  47. page,
  48. coords: position,
  49. });
  50. return text;
  51. };
  52. const scrollUpdate = useCallback((state: ScrollStateType): void => {
  53. const innerHeight = innerRef.current?.offsetHeight || 0;
  54. const wrapperHeight = containerRef.current?.offsetHeight || 0;
  55. if (
  56. wrapperHeight + state.lastY >= innerHeight
  57. && renderQueue.length !== annotations.length
  58. ) {
  59. const start = renderQueue.length;
  60. const end = renderQueue.length + 10;
  61. const newQueue = [...renderQueue, ...annotations.slice(start, end)];
  62. setQueue(newQueue);
  63. }
  64. }, [renderQueue, annotations]);
  65. useEffect(() => {
  66. const state = watchScroll(containerRef.current, scrollUpdate);
  67. return (): void => {
  68. state.subscriber.unsubscribe();
  69. };
  70. }, [scrollUpdate]);
  71. useEffect(() => {
  72. if (isActive) {
  73. setQueue(annotations.slice(0, 10));
  74. }
  75. }, [isActive]);
  76. return (
  77. <Drawer anchor="right" open={isActive}>
  78. <Container>
  79. <Head>
  80. <IconWrapper>
  81. <Icon glyph="right-back" onClick={close} />
  82. </IconWrapper>
  83. <Separator />
  84. <IconWrapper>
  85. <Icon glyph="sort" />
  86. </IconWrapper>
  87. <IconWrapper onClick={handleExport}>
  88. <Icon glyph="annotation-export" />
  89. </IconWrapper>
  90. <IconWrapper>
  91. <Icon glyph="import" />
  92. </IconWrapper>
  93. </Head>
  94. <Body ref={containerRef}>
  95. <div ref={innerRef}>
  96. <Typography light align="left">
  97. {`${annotations.length} Annotations`}
  98. </Typography>
  99. {isActive && renderQueue.map((ele, index) => {
  100. const key = `annot_item_${index}`;
  101. const {
  102. obj_type,
  103. obj_attr: {
  104. page,
  105. bdcolor,
  106. position,
  107. transparency,
  108. },
  109. } = ele;
  110. const actualPage = page + 1;
  111. const prevPage = index > 0 ? annotations[index - 1].obj_attr.page + 1 : -1;
  112. return (
  113. <Item
  114. key={key}
  115. type={obj_type}
  116. page={actualPage}
  117. bdcolor={bdcolor}
  118. transparency={transparency}
  119. getText={(): Promise<any> => getText(actualPage, position)}
  120. showPageNum={actualPage !== prevPage}
  121. />
  122. );
  123. })}
  124. </div>
  125. </Body>
  126. </Container>
  127. </Drawer>
  128. );
  129. };
  130. export default AnnotationsList;