mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #5044 from overleaf/hb-roll-out-react-file-view
Roll out react file view to all users GitOrigin-RevId: 714246e8de39d1f686c3c07ff298d2c236cbdb8b
This commit is contained in:
parent
f7900b474b
commit
ab736aecc6
8 changed files with 2 additions and 317 deletions
|
@ -872,10 +872,6 @@ const ProjectController = {
|
||||||
'new_navigation_ui',
|
'new_navigation_ui',
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
showNewFileViewUI: shouldDisplayFeature(
|
|
||||||
'new_file_view',
|
|
||||||
user.alphaProgram || user.betaProgram
|
|
||||||
),
|
|
||||||
showNewPdfPreview: shouldDisplayFeature('new_pdf_preview', false),
|
showNewPdfPreview: shouldDisplayFeature('new_pdf_preview', false),
|
||||||
showSymbolPalette: shouldDisplayFeature(
|
showSymbolPalette: shouldDisplayFeature(
|
||||||
'symbol_palette',
|
'symbol_palette',
|
||||||
|
|
|
@ -109,10 +109,8 @@ block content
|
||||||
.ui-layout-center
|
.ui-layout-center
|
||||||
include ./editor/editor
|
include ./editor/editor
|
||||||
|
|
||||||
if showNewFileViewUI
|
include ./editor/file-view
|
||||||
include ./editor/file-view
|
|
||||||
else
|
|
||||||
include ./editor/binary-file
|
|
||||||
include ./editor/history
|
include ./editor/history
|
||||||
|
|
||||||
if !isRestrictedTokenMember
|
if !isRestrictedTokenMember
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
div.binary-file-header
|
|
||||||
// Linked Files: URL
|
|
||||||
div(ng-if="openFile.linkedFileData.provider == 'url'")
|
|
||||||
p
|
|
||||||
i.fa.fa-fw.fa-external-link-square.fa-rotate-180.linked-file-icon
|
|
||||||
| Imported from
|
|
||||||
|
|
|
||||||
a(ng-href='{{openFile.linkedFileData.url}}') {{ displayUrl(openFile.linkedFileData.url) }}
|
|
||||||
|
|
|
||||||
| at {{ openFile.created | formatDate:'h:mm a' }} {{ openFile.created | relativeDate }}
|
|
||||||
|
|
||||||
// Linked Files: Project File
|
|
||||||
div(ng-if="openFile.linkedFileData.provider == 'project_file'")
|
|
||||||
p
|
|
||||||
i.fa.fa-fw.fa-external-link-square.fa-rotate-180.linked-file-icon
|
|
||||||
| Imported from
|
|
||||||
|
|
|
||||||
a(ng-if='!openFile.linkedFileData.v1_source_doc_id'
|
|
||||||
ng-href='/project/{{openFile.linkedFileData.source_project_id}}' target="_blank")
|
|
||||||
| Another project
|
|
||||||
span(ng-if='openFile.linkedFileData.v1_source_doc_id')
|
|
||||||
| Another project
|
|
||||||
| /{{ openFile.linkedFileData.source_entity_path.slice(1) }},
|
|
||||||
|
|
|
||||||
| at {{ openFile.created | formatDate:'h:mm a' }} {{ openFile.created | relativeDate }}
|
|
||||||
|
|
||||||
// Linked Files: Project Output File
|
|
||||||
div(ng-if="openFile.linkedFileData.provider == 'project_output_file'")
|
|
||||||
p
|
|
||||||
i.fa.fa-fw.fa-external-link-square.fa-rotate-180.linked-file-icon
|
|
||||||
| Imported from the output of
|
|
||||||
|
|
|
||||||
a(ng-if='!openFile.linkedFileData.v1_source_doc_id'
|
|
||||||
ng-href='/project/{{openFile.linkedFileData.source_project_id}}' target="_blank")
|
|
||||||
| Another project
|
|
||||||
span(ng-if='openFile.linkedFileData.v1_source_doc_id')
|
|
||||||
| Another project
|
|
||||||
| : {{ openFile.linkedFileData.source_output_file_path }},
|
|
||||||
|
|
|
||||||
| at {{ openFile.created | formatDate:'h:mm a' }} {{ openFile.created | relativeDate }}
|
|
||||||
|
|
||||||
!= moduleIncludes("binaryFile:linkedFileInfo", locals)
|
|
||||||
|
|
||||||
// Bottom Controls
|
|
||||||
span(ng-if="openFile.linkedFileData.provider")
|
|
||||||
button.btn.btn-success(
|
|
||||||
href, ng-click="refreshFile(openFile)",
|
|
||||||
ng-disabled="refreshing"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-refresh(ng-class={'fa-spin': refreshing})
|
|
||||||
|
|
|
||||||
span(ng-show="!refreshing") Refresh
|
|
||||||
span(ng-show="refreshing") Refreshing…
|
|
||||||
|
|
|
||||||
a.btn.btn-info(
|
|
||||||
ng-href="/project/{{ project_id }}/file/{{ openFile.id }}"
|
|
||||||
)
|
|
||||||
i.fa.fa-fw.fa-download
|
|
||||||
|
|
|
||||||
| #{translate("download")}
|
|
||||||
|
|
||||||
// Refresh Error
|
|
||||||
div(ng-if="refreshError").row
|
|
||||||
br
|
|
||||||
.alert.alert-danger.col-md-6.col-md-offset-3
|
|
||||||
| Error: {{ refreshError}}
|
|
||||||
!= moduleIncludes("binaryFile:linkedFileRefreshError", locals)
|
|
|
@ -1,41 +0,0 @@
|
||||||
div.binary-file.full-size(
|
|
||||||
ng-controller="BinaryFileController"
|
|
||||||
ng-show="ui.view == 'file'"
|
|
||||||
ng-if="openFile"
|
|
||||||
)
|
|
||||||
|
|
||||||
include ./binary-file-header
|
|
||||||
|
|
||||||
img(
|
|
||||||
ng-show="!failedLoad"
|
|
||||||
ng-src="/project/{{ project_id }}/file/{{ openFile.id }}"
|
|
||||||
ng-if="isImageFile()"
|
|
||||||
ng-class="{'img-preview': !imgLoaded}"
|
|
||||||
onerror="sl_binaryFilePreviewError()"
|
|
||||||
onabort="sl_binaryFilePreviewError()"
|
|
||||||
onload="sl_binaryFilePreviewLoaded()"
|
|
||||||
)
|
|
||||||
|
|
||||||
img(
|
|
||||||
ng-show="!failedLoad"
|
|
||||||
ng-src="/project/{{ project_id }}/file/{{ openFile.id }}?format=png"
|
|
||||||
ng-if="isPreviewableFile()"
|
|
||||||
ng-class="{'img-preview': !imgLoaded}"
|
|
||||||
onerror="sl_binaryFilePreviewError()"
|
|
||||||
onabort="sl_binaryFilePreviewError()"
|
|
||||||
onload="sl_binaryFilePreviewLoaded()"
|
|
||||||
)
|
|
||||||
|
|
||||||
div(ng-if="isTextFile() && !textPreview.error")
|
|
||||||
div.text-loading(ng-show="textPreview.loading && !textPreview.error")
|
|
||||||
| #{translate('loading')}…
|
|
||||||
div.text-preview(ng-show="textPreview.data && !textPreview.loading && !textPreview.error")
|
|
||||||
div.scroll-container
|
|
||||||
p
|
|
||||||
| {{ textPreview.data }}
|
|
||||||
p(ng-show="textPreview.shouldShowDots")
|
|
||||||
| …
|
|
||||||
|
|
||||||
p.no-preview(
|
|
||||||
ng-if="failedLoad || textPreview.error || isUnpreviewableFile()"
|
|
||||||
) #{translate("no_preview_available")}
|
|
|
@ -9,7 +9,6 @@ import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
|
||||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||||
import useAbortController from '../../../shared/hooks/use-abort-controller'
|
import useAbortController from '../../../shared/hooks/use-abort-controller'
|
||||||
import BetaBadge from '../../../shared/components/beta-badge'
|
|
||||||
const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo')
|
const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo')
|
||||||
const tprLinkedFileRefreshError = importOverleafModules(
|
const tprLinkedFileRefreshError = importOverleafModules(
|
||||||
'tprLinkedFileRefreshError'
|
'tprLinkedFileRefreshError'
|
||||||
|
@ -106,12 +105,6 @@ export default function FileViewHeader({ file, storeReferencesKeys }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BetaBadge
|
|
||||||
tooltip={{
|
|
||||||
id: 'file-view-beta-tooltip',
|
|
||||||
text: t('beta_badge_tooltip', { feature: 'file views' }),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{file.linkedFileData && fileInfo}
|
{file.linkedFileData && fileInfo}
|
||||||
{file.linkedFileData &&
|
{file.linkedFileData &&
|
||||||
tprLinkedFileInfo.map(({ import: { LinkedFileInfo }, path }) => (
|
tprLinkedFileInfo.map(({ import: { LinkedFileInfo }, path }) => (
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
* DS102: Remove unnecessary code created because of implicit returns
|
* DS102: Remove unnecessary code created because of implicit returns
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
*/
|
*/
|
||||||
import './controllers/BinaryFileController'
|
|
||||||
let BinaryFilesManager
|
let BinaryFilesManager
|
||||||
|
|
||||||
export default BinaryFilesManager = class BinaryFilesManager {
|
export default BinaryFilesManager = class BinaryFilesManager {
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
import App from '../../../base'
|
|
||||||
|
|
||||||
export default App.controller(
|
|
||||||
'BinaryFileController',
|
|
||||||
function ($scope, $rootScope, $http, $timeout, $element, ide, waitFor) {
|
|
||||||
const MAX_FILE_SIZE = 2 * 1024 * 1024
|
|
||||||
const MAX_URL_LENGTH = 60
|
|
||||||
const FRONT_OF_URL_LENGTH = 35
|
|
||||||
const FILLER = '...'
|
|
||||||
const TAIL_OF_URL_LENGTH =
|
|
||||||
MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length
|
|
||||||
|
|
||||||
const textExtensions = window.ExposedSettings.textExtensions
|
|
||||||
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif']
|
|
||||||
const previewableExtensions = []
|
|
||||||
|
|
||||||
const extension = file => file.name.split('.').pop().toLowerCase()
|
|
||||||
|
|
||||||
$scope.isTextFile = () =>
|
|
||||||
textExtensions.indexOf(extension($scope.openFile)) > -1
|
|
||||||
$scope.isImageFile = () =>
|
|
||||||
imageExtensions.indexOf(extension($scope.openFile)) > -1
|
|
||||||
$scope.isPreviewableFile = () =>
|
|
||||||
previewableExtensions.indexOf(extension($scope.openFile)) > -1
|
|
||||||
$scope.isUnpreviewableFile = () =>
|
|
||||||
!$scope.isTextFile() &&
|
|
||||||
!$scope.isImageFile() &&
|
|
||||||
!$scope.isPreviewableFile()
|
|
||||||
|
|
||||||
$scope.textPreview = {
|
|
||||||
loading: false,
|
|
||||||
shouldShowDots: false,
|
|
||||||
error: false,
|
|
||||||
data: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.refreshing = false
|
|
||||||
$scope.refreshError = null
|
|
||||||
|
|
||||||
$scope.displayUrl = function (url) {
|
|
||||||
if (url == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (url.length > MAX_URL_LENGTH) {
|
|
||||||
const front = url.slice(0, FRONT_OF_URL_LENGTH)
|
|
||||||
const tail = url.slice(url.length - TAIL_OF_URL_LENGTH)
|
|
||||||
return front + FILLER + tail
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.refreshFile = function (file) {
|
|
||||||
$scope.refreshing = true
|
|
||||||
$scope.refreshError = null
|
|
||||||
window.expectingLinkedFileRefreshedSocketFor = file.name
|
|
||||||
ide.fileTreeManager
|
|
||||||
.refreshLinkedFile(file)
|
|
||||||
.then(function (response) {
|
|
||||||
const { data } = response
|
|
||||||
const newFileId = data.new_file_id
|
|
||||||
$timeout(
|
|
||||||
() =>
|
|
||||||
waitFor(() => ide.fileTreeManager.findEntityById(newFileId), 5000)
|
|
||||||
.then(newFile => {
|
|
||||||
ide.binaryFilesManager.openFile(newFile)
|
|
||||||
window.expectingLinkedFileRefreshedSocketFor = null
|
|
||||||
})
|
|
||||||
.catch(err => console.warn(err)),
|
|
||||||
|
|
||||||
0
|
|
||||||
)
|
|
||||||
$scope.refreshError = null
|
|
||||||
})
|
|
||||||
.catch(response => ($scope.refreshError = response.data))
|
|
||||||
.finally(() => {
|
|
||||||
$scope.refreshing = false
|
|
||||||
const provider = file.linkedFileData.provider
|
|
||||||
if (
|
|
||||||
provider === 'mendeley' ||
|
|
||||||
provider === 'zotero' ||
|
|
||||||
file.name.match(/^.*\.bib$/)
|
|
||||||
) {
|
|
||||||
ide.$scope.$emit('references:should-reindex', {})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback fired when the `img` tag fails to load,
|
|
||||||
// `failedLoad` used to show the "No Preview" message
|
|
||||||
$scope.failedLoad = false
|
|
||||||
window.sl_binaryFilePreviewError = () => {
|
|
||||||
$scope.failedLoad = true
|
|
||||||
$scope.$apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback fired when the `img` tag is done loading,
|
|
||||||
// `imgLoaded` is used to show the spinner gif while loading
|
|
||||||
$scope.imgLoaded = false
|
|
||||||
window.sl_binaryFilePreviewLoaded = () => {
|
|
||||||
$scope.imgLoaded = true
|
|
||||||
$scope.$apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($scope.isTextFile()) {
|
|
||||||
loadTextFilePreview()
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTextFilePreview() {
|
|
||||||
const url = `/project/${window.project_id}/file/${$scope.openFile.id}`
|
|
||||||
let truncated = false
|
|
||||||
displayPreviewLoading()
|
|
||||||
getFileSize(url)
|
|
||||||
.then(fileSize => {
|
|
||||||
const opts = {}
|
|
||||||
if (fileSize > MAX_FILE_SIZE) {
|
|
||||||
truncated = true
|
|
||||||
opts.maxSize = MAX_FILE_SIZE
|
|
||||||
}
|
|
||||||
return getFileContents(url, opts)
|
|
||||||
})
|
|
||||||
.then(contents => {
|
|
||||||
const displayedContents = truncated
|
|
||||||
? truncateFileContents(contents)
|
|
||||||
: contents
|
|
||||||
displayPreview(displayedContents, truncated)
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err)
|
|
||||||
displayPreviewError()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileSize(url) {
|
|
||||||
return $http.head(url).then(response => {
|
|
||||||
const size = parseInt(response.headers('Content-Length'), 10)
|
|
||||||
if (isNaN(size)) {
|
|
||||||
throw new Error('Could not parse Content-Length header')
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileContents(url, opts = {}) {
|
|
||||||
const { maxSize } = opts
|
|
||||||
if (maxSize != null) {
|
|
||||||
url += `?range=0-${maxSize}`
|
|
||||||
}
|
|
||||||
return $http
|
|
||||||
.get(url, {
|
|
||||||
transformResponse: null, // Don't parse JSON
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
return response.data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncateFileContents(contents) {
|
|
||||||
return contents.replace(/\n.*$/, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayPreviewLoading() {
|
|
||||||
$scope.textPreview.data = null
|
|
||||||
$scope.textPreview.loading = true
|
|
||||||
$scope.textPreview.shouldShowDots = false
|
|
||||||
$scope.$apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayPreview(contents, truncated) {
|
|
||||||
$scope.textPreview.error = false
|
|
||||||
$scope.textPreview.data = contents
|
|
||||||
$scope.textPreview.shouldShowDots = truncated
|
|
||||||
$timeout(setPreviewHeight, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayPreviewError() {
|
|
||||||
$scope.textPreview.error = true
|
|
||||||
$scope.textPreview.loading = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPreviewHeight() {
|
|
||||||
const $preview = $element.find('.text-preview .scroll-container')
|
|
||||||
const $header = $element.find('.binary-file-header')
|
|
||||||
const maxHeight = $element.height() - $header.height() - 14 // borders + margin
|
|
||||||
$preview.css({ 'max-height': maxHeight })
|
|
||||||
// Don't show the preview until we've set the height, otherwise we jump around
|
|
||||||
$scope.textPreview.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -44,8 +44,4 @@
|
||||||
.linked-file-icon {
|
.linked-file-icon {
|
||||||
color: @blue;
|
color: @blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.beta-badge {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue