2019-05-27 09:48:58 +00:00
import React from 'react' ;
2022-12-29 04:21:47 +00:00
import ReactDom from 'react-dom' ;
2023-09-13 00:40:50 +00:00
import PropTypes from 'prop-types' ;
2022-12-29 04:21:47 +00:00
import { navigate } from '@gatsbyjs/reach-router' ;
2024-10-21 03:29:17 +00:00
import dayjs from 'dayjs' ;
2019-05-27 09:48:58 +00:00
import { Utils } from './utils/utils' ;
2019-12-05 07:45:16 +00:00
import { gettext , siteRoot , mediaUrl , logoPath , logoWidth , logoHeight , siteTitle } from './utils/constants' ;
2019-05-27 09:48:58 +00:00
import { seafileAPI } from './utils/seafile-api' ;
import Loading from './components/loading' ;
2020-03-11 09:26:34 +00:00
import Paginator from './components/paginator' ;
2019-05-27 09:48:58 +00:00
import ModalPortal from './components/modal-portal' ;
import CommonToolbar from './components/toolbar/common-toolbar' ;
import CommitDetails from './components/dialog/commit-details' ;
import UpdateRepoCommitLabels from './components/dialog/edit-repo-commit-labels' ;
import './css/toolbar.css' ;
import './css/search.css' ;
import './css/repo-history.css' ;
2020-11-02 05:56:35 +00:00
const {
2019-05-27 09:48:58 +00:00
repoID ,
repoName ,
userPerm ,
showLabel
} = window . app . pageOptions ;
class RepoHistory extends React . Component {
constructor ( props ) {
super ( props ) ;
this . state = {
isLoading : true ,
errorMsg : '' ,
2020-03-11 09:26:34 +00:00
currentPage : 1 ,
2024-10-16 09:05:32 +00:00
perPage : 100 ,
2020-03-11 09:26:34 +00:00
hasNextPage : false ,
items : [ ]
2019-05-27 09:48:58 +00:00
} ;
}
componentDidMount ( ) {
2020-03-11 09:26:34 +00:00
let urlParams = ( new URL ( window . location ) ) . searchParams ;
const {
currentPage , perPage
} = this . state ;
this . setState ( {
perPage : parseInt ( urlParams . get ( 'per_page' ) || perPage ) ,
currentPage : parseInt ( urlParams . get ( 'page' ) || currentPage )
} , ( ) => {
this . getItems ( this . state . currentPage ) ;
2020-11-02 05:56:35 +00:00
} ) ;
2019-05-27 09:48:58 +00:00
}
getItems = ( page ) => {
seafileAPI . getRepoHistory ( repoID , page , this . state . perPage ) . then ( ( res ) => {
this . setState ( {
isLoading : false ,
2020-03-11 09:26:34 +00:00
currentPage : page ,
2019-05-27 09:48:58 +00:00
items : res . data . data ,
2020-03-11 09:26:34 +00:00
hasNextPage : res . data . more
2019-05-27 09:48:58 +00:00
} ) ;
} ) . catch ( ( error ) => {
2019-12-05 07:45:16 +00:00
this . setState ( {
isLoading : false ,
errorMsg : Utils . getErrorMsg ( error , true ) // true: show login tip if 403
2020-11-02 05:56:35 +00:00
} ) ;
2019-05-27 09:48:58 +00:00
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
2020-03-11 09:26:34 +00:00
resetPerPage = ( perPage ) => {
this . setState ( {
perPage : perPage
} , ( ) => {
this . getItems ( 1 ) ;
2020-11-02 05:56:35 +00:00
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
onSearchedClick = ( selectedItem ) => {
if ( selectedItem . is _dir === true ) {
let url = siteRoot + 'library/' + selectedItem . repo _id + '/' + selectedItem . repo _name + selectedItem . path ;
2024-11-20 12:37:46 +00:00
navigate ( url , { replace : true } ) ;
2019-05-27 09:48:58 +00:00
} else {
let url = siteRoot + 'lib/' + selectedItem . repo _id + '/file' + Utils . encodePath ( selectedItem . path ) ;
let newWindow = window . open ( 'about:blank' ) ;
newWindow . location . href = url ;
}
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
2019-05-29 08:26:25 +00:00
goBack = ( e ) => {
e . preventDefault ( ) ;
window . history . back ( ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-29 08:26:25 +00:00
2019-05-27 09:48:58 +00:00
render ( ) {
2024-03-07 07:37:09 +00:00
let title = gettext ( '{placeholder} Modification History' ) ;
title = title . replace ( '{placeholder}' , '<span class="op-target text-truncate mx-1">' + Utils . HTMLescape ( repoName ) + '</span>' ) ;
2019-05-27 09:48:58 +00:00
return (
< React . Fragment >
< div className = "h-100 d-flex flex-column" >
< div className = "top-header d-flex justify-content-between" >
< a href = { siteRoot } >
< img src = { mediaUrl + logoPath } height = { logoHeight } width = { logoWidth } title = { siteTitle } alt = "logo" / >
< / a >
< CommonToolbar onSearchedClick = { this . onSearchedClick } / >
< / d i v >
2019-05-29 08:26:25 +00:00
< div className = "flex-auto container-fluid pt-4 pb-6 o-auto" >
2019-05-27 09:48:58 +00:00
< div className = "row" >
< div className = "col-md-10 offset-md-1" >
2024-07-18 03:58:42 +00:00
< h2 dangerouslySetInnerHTML = { { _ _html : title } } className = "d-flex text-nowrap" > < / h 2 >
2021-09-26 09:28:36 +00:00
< a href = "#" className = "go-back" title = { gettext ( 'Back' ) } onClick = { this . goBack } role = "button" aria - label = { gettext ( 'Back' ) } >
2024-06-28 00:39:44 +00:00
< span className = "sf3-font sf3-font-down rotate-90 d-inline-block" > < / s p a n >
2019-05-29 08:26:25 +00:00
< / a >
2019-05-27 09:48:58 +00:00
{ userPerm == 'rw' && < p className = "tip" > { gettext ( 'Tip: a snapshot will be generated after modification, which records the library state after the modification.' ) } < / p > }
2020-11-02 05:56:35 +00:00
< Content
2020-03-11 09:26:34 +00:00
isLoading = { this . state . isLoading }
errorMsg = { this . state . errorMsg }
items = { this . state . items }
currentPage = { this . state . currentPage }
hasNextPage = { this . state . hasNextPage }
curPerPage = { this . state . perPage }
resetPerPage = { this . resetPerPage }
getListByPage = { this . getItems }
2019-05-27 09:48:58 +00:00
/ >
< / d i v >
< / d i v >
< / d i v >
< / d i v >
< / R e a c t . F r a g m e n t >
) ;
}
}
class Content extends React . Component {
constructor ( props ) {
super ( props ) ;
this . theadData = showLabel ? [
2024-07-18 03:58:42 +00:00
{ width : '43%' , text : gettext ( 'Description' ) } ,
{ width : '12%' , text : gettext ( 'Time' ) } ,
{ width : '9%' , text : gettext ( 'Modifier' ) } ,
{ width : '12%' , text : ` ${ gettext ( 'Device' ) } / ${ gettext ( 'Version' ) } ` } ,
{ width : '12%' , text : gettext ( 'Labels' ) } ,
{ width : '12%' , text : '' }
2019-05-27 09:48:58 +00:00
] : [
2024-07-18 03:58:42 +00:00
{ width : '43%' , text : gettext ( 'Description' ) } ,
{ width : '15%' , text : gettext ( 'Time' ) } ,
{ width : '15%' , text : gettext ( 'Modifier' ) } ,
{ width : '15%' , text : ` ${ gettext ( 'Device' ) } / ${ gettext ( 'Version' ) } ` } ,
{ width : '12%' , text : '' }
2019-05-27 09:48:58 +00:00
] ;
2020-11-02 05:56:35 +00:00
}
2019-05-27 09:48:58 +00:00
2020-03-11 09:26:34 +00:00
getPreviousPage = ( ) => {
this . props . getListByPage ( this . props . currentPage - 1 ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
2020-03-11 09:26:34 +00:00
getNextPage = ( ) => {
this . props . getListByPage ( this . props . currentPage + 1 ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
render ( ) {
2020-03-11 09:26:34 +00:00
const {
isLoading , errorMsg , items ,
2020-11-02 05:56:35 +00:00
curPerPage , currentPage , hasNextPage
2020-03-11 09:26:34 +00:00
} = this . props ;
2019-05-27 09:48:58 +00:00
if ( isLoading ) {
return < Loading / > ;
}
if ( errorMsg ) {
return < p className = "error mt-6 text-center" > { errorMsg } < / p > ;
}
return (
< React . Fragment >
< table className = "table-hover" >
< thead >
< tr >
{ this . theadData . map ( ( item , index ) => {
return < th key = { index } width = { item . width } > { item . text } < / t h > ;
} ) }
< / t r >
< / t h e a d >
< tbody >
{ items . map ( ( item , index ) => {
2020-03-11 09:26:34 +00:00
item . isFirstCommit = ( currentPage == 1 ) && ( index == 0 ) ;
item . showDetails = hasNextPage || ( index != items . length - 1 ) ;
2019-05-27 09:48:58 +00:00
return < Item key = { index } item = { item } / > ;
} ) }
< / t b o d y >
< / t a b l e >
2020-03-11 09:26:34 +00:00
< Paginator
gotoPreviousPage = { this . getPreviousPage }
gotoNextPage = { this . getNextPage }
currentPage = { currentPage }
hasNextPage = { hasNextPage }
curPerPage = { curPerPage }
resetPerPage = { this . props . resetPerPage }
2020-11-02 05:56:35 +00:00
/ >
2019-05-27 09:48:58 +00:00
< / R e a c t . F r a g m e n t >
) ;
}
}
2023-09-13 00:40:50 +00:00
Content . propTypes = {
isLoading : PropTypes . bool . isRequired ,
errorMsg : PropTypes . string . isRequired ,
items : PropTypes . array . isRequired ,
currentPage : PropTypes . number . isRequired ,
hasNextPage : PropTypes . bool . isRequired ,
curPerPage : PropTypes . number . isRequired ,
resetPerPage : PropTypes . func . isRequired ,
getListByPage : PropTypes . func . isRequired ,
} ;
2019-05-27 09:48:58 +00:00
class Item extends React . Component {
constructor ( props ) {
super ( props ) ;
this . state = {
2020-11-02 05:56:35 +00:00
labels : this . props . item . tags ,
2019-05-27 09:48:58 +00:00
isIconShown : false ,
isCommitLabelUpdateDialogOpen : false ,
isCommitDetailsDialogOpen : false
} ;
}
handleMouseOver = ( ) => {
2024-07-18 03:58:42 +00:00
this . setState ( { isIconShown : true } ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
handleMouseOut = ( ) => {
2024-07-18 03:58:42 +00:00
this . setState ( { isIconShown : false } ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
showCommitDetails = ( e ) => {
e . preventDefault ( ) ;
this . setState ( {
isCommitDetailsDialogOpen : ! this . state . isCommitDetailsDialogOpen
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
toggleCommitDetailsDialog = ( ) => {
this . setState ( {
isCommitDetailsDialogOpen : ! this . state . isCommitDetailsDialogOpen
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
2021-09-26 09:28:36 +00:00
editLabel = ( e ) => {
e . preventDefault ( ) ;
2019-05-27 09:48:58 +00:00
this . setState ( {
isCommitLabelUpdateDialogOpen : ! this . state . isCommitLabelUpdateDialogOpen
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
toggleLabelEditDialog = ( ) => {
this . setState ( {
isCommitLabelUpdateDialogOpen : ! this . state . isCommitLabelUpdateDialogOpen
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
updateLabels = ( labels ) => {
this . setState ( {
labels : labels
} ) ;
2023-09-13 00:40:50 +00:00
} ;
2019-05-27 09:48:58 +00:00
render ( ) {
const item = this . props . item ;
const { isIconShown , isCommitLabelUpdateDialogOpen , isCommitDetailsDialogOpen , labels } = this . state ;
let name = '' ;
if ( item . email ) {
if ( ! item . second _parent _id ) {
name = < a href = { ` ${ siteRoot } profile/ ${ encodeURIComponent ( item . email ) } / ` } > { item . name } < / a > ;
} else {
name = gettext ( 'None' ) ;
}
} else {
name = gettext ( 'Unknown' ) ;
}
return (
< React . Fragment >
2021-09-26 09:28:36 +00:00
< tr onMouseOver = { this . handleMouseOver } onMouseOut = { this . handleMouseOut } onFocus = { this . handleMouseOver } >
2019-05-27 09:48:58 +00:00
< td >
{ item . description }
{ item . showDetails &&
2021-09-26 09:28:36 +00:00
< a href = "#" className = "details" onClick = { this . showCommitDetails } role = "button" > { gettext ( 'Details' ) } < / a >
2019-05-27 09:48:58 +00:00
}
< / t d >
2024-10-21 03:29:17 +00:00
< td title = { dayjs ( item . time ) . format ( 'dddd, MMMM D, YYYY h:mm:ss A' ) } > { dayjs ( item . time ) . format ( 'YYYY-MM-DD' ) } < / t d >
2019-05-27 09:48:58 +00:00
< td > { name } < / t d >
< td >
{ item . client _version ? ` ${ item . device _name } / ${ item . client _version } ` : 'API / --' }
< / t d >
2020-11-02 05:56:35 +00:00
{ showLabel &&
2019-05-27 09:48:58 +00:00
< td >
{ labels . map ( ( item , index ) => {
return < span key = { index } className = "commit-label" > { item } < / s p a n > ;
} ) }
{ userPerm == 'rw' &&
2024-07-18 03:58:42 +00:00
< a href = "#" role = "button" className = { ` attr-action-icon sf3-font sf3-font-rename ${ isIconShown ? '' : 'invisible' } ` } title = { gettext ( 'Edit' ) } aria - label = { gettext ( 'Edit' ) } onClick = { this . editLabel } > < / a >
2019-05-27 09:48:58 +00:00
}
< / t d >
}
< td >
{ userPerm == 'rw' && (
item . isFirstCommit ?
2024-07-18 03:58:42 +00:00
< span className = { isIconShown ? '' : 'invisible' } > { gettext ( 'Current Version' ) } < / s p a n > :
< a href = { ` ${ siteRoot } repo/ ${ repoID } /snapshot/?commit_id= ${ item . commit _id } ` } className = { isIconShown ? '' : 'invisible' } > { gettext ( 'View Snapshot' ) } < / a >
2019-05-27 09:48:58 +00:00
) }
< / t d >
< / t r >
{ isCommitDetailsDialogOpen &&
< ModalPortal >
< CommitDetails
repoID = { repoID }
commitID = { item . commit _id }
commitTime = { item . time }
toggleDialog = { this . toggleCommitDetailsDialog }
/ >
< / M o d a l P o r t a l >
}
{ isCommitLabelUpdateDialogOpen &&
< ModalPortal >
< UpdateRepoCommitLabels
repoID = { repoID }
commitID = { item . commit _id }
commitLabels = { labels }
updateCommitLabels = { this . updateLabels }
toggleDialog = { this . toggleLabelEditDialog }
/ >
< / M o d a l P o r t a l >
}
< / R e a c t . F r a g m e n t >
) ;
}
}
2023-09-13 00:40:50 +00:00
Item . propTypes = {
item : PropTypes . object . isRequired ,
} ;
2022-12-29 04:21:47 +00:00
ReactDom . render ( < RepoHistory / > , document . getElementById ( 'wrapper' ) ) ;