initial design work for the project docs pages

Signed-off-by: MiniDigger <admin@benndorf.dev>
This commit is contained in:
MiniDigger 2021-08-17 11:06:31 +02:00
parent fa95259216
commit ce3ca1bd45
23 changed files with 527 additions and 283 deletions

View File

@ -1,6 +1,7 @@
@import '~vuetify/src/styles/styles.sass';
@import 'utils';
@import "markdown";
@import "theme/base";
.text-transform-unset {
text-transform: unset;

View File

@ -0,0 +1,16 @@
@import '~vuetify/src/styles/styles.sass';
.v-divider {
border: 1px solid rgba(255, 255, 255, 0.15) !important;
}
.v-card,
.v-sheet.v-list {
background-color: #272727 !important;
}
.v-btn.v-btn--has-bg {
background-color: #1E1E1E !important;
}
a {
text-decoration: none;
}

View File

@ -4,3 +4,11 @@
// $font-size-root: 20px;
// !!!!---No imports in this file---!!!!
//@import '~vue-cli-plugin-vuetify-preset-reply/preset/variables.scss';
@import '~vuetify/src/styles/styles.sass';
$container-max-widths: (
'md': 1000px,
'lg': 1500px,
'xl': 1680px
)

View File

@ -19,8 +19,10 @@ export default class Announcement extends Vue {
<style lang="scss" scoped>
.v-alert {
text-align: center;
margin-bottom: 1rem;
flex: 0 0 100%;
max-width: 100%;
min-height: 43px;
margin: 0;
border-radius: 0;
}
</style>

View File

@ -101,4 +101,8 @@ img {
a {
margin: 0 3px;
}
footer {
margin-top: 12px;
}
</style>

View File

@ -1,10 +1,10 @@
<template>
<v-app-bar fixed app>
<v-app-bar height="100px">
<v-menu bottom offset-y open-on-hover transition="slide-y-transition" close-delay="100">
<template #activator="{ on, attrs }">
<v-btn text x-large class="align-self-center px-1" v-bind="attrs" :ripple="false" v-on="on">
<NuxtLink class="float-left" to="/" exact>
<v-img height="55" width="220" src="https://papermc.io/images/logo-marker.svg" alt="Paper logo" />
<v-img height="60" width="220" src="https://papermc.io/images/logo-marker.svg" alt="Paper logo" />
</NuxtLink>
<v-icon>mdi-chevron-down</v-icon>
@ -244,9 +244,7 @@ export default class Header extends HangarComponent {
.v-badge--bordered.header-badge .v-badge__badge::after {
border-color: #272727 !important;
}
</style>
<style lang="scss">
.avatar-button {
//border: 2px white solid;
.v-image {
margin: 20px;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div>
<v-sheet :color="color" class="darken-1" rounded elevation="2">
<v-sheet :color="color" rounded elevation="2">
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="!$fetchState.pending" class="page-rendered" :class="innerClass" v-html="renderedMarkdown" />
<v-row v-else no-gutters justify="center">

View File

@ -1,11 +1,11 @@
<template>
<div class="markdown-editor">
<div v-show="isEditing && !preview" class="ml-4">
<div v-show="isEditing && !preview">
<!-- TODO validations for pageContent min/max length from validations state -->
<v-textarea v-model="rawEdited" outlined :rows="rawEdited.split(/\r\n|\r|\n/g).length + 3" :rules="rules" />
</div>
<Markdown v-show="!isEditing" :raw="raw" class="ml-4" />
<Markdown v-if="preview" :raw="rawEdited" class="ml-4" inner-class="pl-5" />
<Markdown v-show="!isEditing" :raw="raw" />
<Markdown v-if="preview" :raw="rawEdited" inner-class="pl-5" />
<v-btn v-show="!isEditing" class="page-btn edit-btn info" fab absolute icon x-small @click="isEditing = true">
<v-icon>mdi-pencil</v-icon>
</v-btn>
@ -38,7 +38,7 @@
<v-btn
v-show="isEditing && cancellable"
class="page-btn cancel-btn warning red darken-2"
class="page-btn cancel-btn warning red darken-1"
fab
absolute
icon
@ -118,23 +118,23 @@ export default class MarkdownEditor extends Vue {
<style lang="scss" scoped>
.page-btn {
left: 0;
left: -20px;
&.edit-btn,
&.save-btn {
top: -16px;
top: 5px;
}
&.preview-btn {
top: 22px;
top: 45px;
}
&.delete-btn {
top: 62px;
top: 78px;
}
&.cancel-btn {
top: 102px;
top: 118px;
}
}

View File

@ -3,9 +3,9 @@
<template #activator="{ on: dialogOn, attrs }">
<v-tooltip :disabled="!project.userActions.flagged" bottom>
<template #activator="{ on: tooltipOn }">
<div v-on="tooltipOn">
<div class="flag-modal-trigger" v-on="tooltipOn">
<!-- wrap in div so tooltip shows up when the button is disabled -->
<v-btn v-bind="attrs" color="warning" :disabled="project.userActions.flagged" :class="activatorClass" v-on="dialogOn">
<v-btn v-bind="attrs" small color="warning" :disabled="project.userActions.flagged" :class="activatorClass" v-on="dialogOn">
<v-icon>mdi-flag</v-icon>
{{ $t('project.actions.flag') }}
</v-btn>

View File

@ -1,8 +1,8 @@
<template>
<div class="button-group d-inline-block">
<div class="button-group">
<v-menu v-if="platformSelection" offset-y>
<template #activator="{ on, attrs }">
<v-btn style="min-width: 32px" class="px-2" color="primary darken-1" dark v-bind="attrs" elevation="0" v-on="on">
<v-btn style="min-width: 32px" class="px-2" color="primary darken-1" height="52px" dark v-bind="attrs" elevation="0" v-on="on">
<v-icon v-text="`$vuetify.icons.${selectedPlatform.toLowerCase()}`" />
</v-btn>
</template>
@ -18,7 +18,7 @@
</v-list-item>
</v-list-item-group>
</v-list> </v-menu
><v-btn color="primary" :small="small" :loading="loading" elevation="0" @click="checkAndDownload">
><v-btn color="primary" :small="small" :loading="loading" elevation="0" height="52px" @click="checkAndDownload">
<v-icon>mdi-download-outline</v-icon>
{{ external ? $t('version.page.downloadExternal') : $t('version.page.download') }} </v-btn
><HangarModal
@ -38,7 +38,16 @@
<em>{{ $t('version.page.confirmation.disclaimer') }}</em> </HangarModal
><v-tooltip v-if="copyButton" v-model="copySuccessful" bottom>
<template #activator="{ attrs }">
<v-btn style="min-width: 32px" class="px-2" color="primary lighten-1" :small="small" v-bind="attrs" elevation="0" @click="copyDownloadUrl">
<v-btn
style="min-width: 32px"
class="px-2"
color="primary lighten-1"
height="52px"
:small="small"
v-bind="attrs"
elevation="0"
@click="copyDownloadUrl"
>
<v-icon>mdi-content-copy</v-icon>
</v-btn>
</template>

View File

@ -0,0 +1,117 @@
<template>
<v-tabs>
<v-tab v-for="tab in tabs" :key="tab.title" :exact="!!tab.exact" :to="tab.external ? '/linkout?remoteUrl=' + tab.link : tab.link" nuxt>
<v-icon left>
{{ tab.icon }}
</v-icon>
{{ tab.title }}
<v-icon v-if="tab.external" small class="mb-1 ml-1" color="primary"> mdi-open-in-new</v-icon>
</v-tab>
</v-tabs>
</template>
<script lang="ts">
import { Component } from 'nuxt-property-decorator';
import { TranslateResult } from 'vue-i18n';
import { HangarProjectMixin } from '~/components/mixins';
import VisibilityChangerModal from '~/components/modals/VisibilityChangerModal.vue';
import Markdown from '~/components/markdown/Markdown.vue';
interface Tab {
title: string | TranslateResult;
icon: string;
link: string;
external: boolean;
exact?: boolean;
}
@Component({
components: { Markdown, VisibilityChangerModal },
})
export default class ProjectTabs extends HangarProjectMixin {
get tabs(): Tab[] {
const tabs = [] as Tab[];
tabs.push({ title: this.$t('project.tabs.docs'), icon: 'mdi-book', link: this.slug, external: false, exact: true });
tabs.push({
title: this.$t('project.tabs.versions'),
icon: 'mdi-download',
link: this.slug + '/versions',
external: false,
});
if ((this.project.settings.forumSync && this.project.postId) || this.$perms.canEditSubjectSettings) {
tabs.push({
title: this.$t('project.tabs.discuss'),
icon: 'mdi-account-group',
link: this.slug + '/discuss',
external: false,
});
}
if (this.$perms.canEditSubjectSettings) {
tabs.push({
title: this.$t('project.tabs.settings'),
icon: 'mdi-cog',
link: this.slug + '/settings',
external: false,
});
}
if (this.project.settings.homepage) {
tabs.push({
title: this.$t('project.tabs.homepage'),
icon: 'mdi-home',
link: this.project.settings.homepage,
external: true,
});
}
if (this.project.settings.issues) {
tabs.push({
title: this.$t('project.tabs.issues'),
icon: 'mdi-bug',
link: this.project.settings.issues,
external: true,
});
}
if (this.project.settings.source) {
tabs.push({
title: this.$t('project.tabs.source'),
icon: 'mdi-code-tags',
link: this.project.settings.source,
external: true,
});
}
if (this.project.settings.support) {
tabs.push({
title: this.$t('project.tabs.support'),
icon: 'mdi-chat-question',
link: this.project.settings.support,
external: true,
});
}
return tabs;
}
get slug(): string {
return `/${this.project.namespace.owner}/${this.project.namespace.slug}`;
}
}
</script>
<style lang="scss">
.v-tabs {
margin-top: 12px;
}
.v-tabs-bar {
background-color: #272727 !important;
a:not(.v-tab--active) {
& .v-icon--left {
color: white !important;
}
color: white !important;
}
}
.v-slide-group__prev--disabled,
.v-slide-group__next--disabled {
display: none !important;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="user-avatar-wrap">
<NuxtLink :to="url">
<img :title="username" :src="src" :alt="username" :class="'user-avatar ' + clazz" @error="errored = true" />
</NuxtLink>

View File

@ -1,12 +1,13 @@
<template>
<v-app>
<Header />
<v-main>
<v-container>
<div class="announcements">
<template v-if="announcements">
<Announcement v-for="(announcement, idx) in announcements" :key="idx" :announcement="announcement" />
</template>
</div>
<Header />
<v-main>
<v-container id="main" fluid>
<DonationResult />
<nuxt />
</v-container>
@ -42,3 +43,12 @@ export default class DefaultLayout extends Vue {
}
}
</script>
<style lang="scss">
#main {
padding: 0;
}
.v-main {
padding-bottom: 12px;
}
</style>

View File

@ -90,7 +90,7 @@ const msgs: LocaleMessageObject = {
noWatchers: 'There are no watchers on this project yet 😢',
members: 'Members',
category: {
info: 'Category: {0}',
info: 'Category',
admin_tools: 'Admin Tools',
chat: 'Chat',
dev_tools: 'Developer Tools',
@ -212,11 +212,11 @@ const msgs: LocaleMessageObject = {
sendForApproval: 'Send for approval',
info: {
title: 'Information',
publishDate: 'Published on {0}',
views: '0 views | {0} view | {0} views',
totalDownloads: '0 total downloads | {0} total download | {0} total downloads',
stars: '0 stars | {0} star | {0} stars',
watchers: '0 watchers | {0} watcher | {0} watchers'
publishDate: 'Published on',
views: 'Views | View | Views',
totalDownloads: 'Total downloads | Total download | Total downloads',
stars: 'Stars | Star | Stars',
watchers: 'Watchers | Watcher | Watchers'
},
promotedVersions: 'Promoted Versions',
license: {

View File

@ -10,7 +10,11 @@
"lint": "eslint --ext \".js,.vue,.ts\" .",
"prepare": "cd .. && husky install frontend/.husky"
},
"eslintIgnore": [".gitignore", "locales/*", "sw.js"],
"eslintIgnore": [
".gitignore",
"locales/*",
"sw.js"
],
"lint-staged": {
"*.{ts,js,vue}": "eslint"
},
@ -28,6 +32,8 @@
"filesize": "6.3.0",
"jwt-decode": "3.1.2",
"lodash-es": "4.17.21",
"lru-cache": "6.0.0",
"minify-css-string": "1.0.0",
"nuxt": "2.15.7",
"nuxt-i18n": "Machine-Maker/i18n-module#fix/mixing-mem-leak",
"nuxt-property-decorator": "2.9.1",
@ -47,6 +53,7 @@
"@types/chartist": "0.11.0",
"@types/diff-match-patch": "1.0.32",
"@types/lodash-es": "4.17.4",
"@types/lru-cache": "5.1.1",
"@types/swagger-ui-dist": "3.30.0",
"eslint": "7.29.0",
"eslint-config-prettier": "8.3.0",

View File

@ -22,8 +22,10 @@
<Markdown v-if="project.lastVisibilityChangeComment" :raw="project.lastVisibilityChangeComment" />
</v-alert>
</template>
<div class="project-header">
<v-container>
<v-row>
<v-col cols="1">
<v-col cols="auto" align-self="center">
<UserAvatar
:username="project.name"
:href="'/' + project.namespace.owner + '/' + project.namespace.slug"
@ -31,7 +33,7 @@
clazz="user-avatar-md"
></UserAvatar>
</v-col>
<v-col cols="auto">
<v-col cols="auto" align-self="center">
<h1 class="d-inline">
<NuxtLink :to="'/' + project.namespace.owner">
{{ project.namespace.owner }}
@ -41,84 +43,28 @@
{{ project.name }}
</NuxtLink>
</h1>
<div>
<v-subheader>{{ project.description }}</v-subheader>
</div>
</v-col>
<v-spacer />
<v-col v-if="isLoggedIn" cols="3">
<v-row no-gutters justify="end">
<v-tooltip v-if="!$util.isCurrentUser(project.owner.id)" bottom>
<template #activator="{ on }">
<v-btn icon @click="toggleStar" v-on="on">
<v-icon v-if="project.userActions.starred" color="amber"> mdi-star </v-icon>
<v-icon v-else color="amber"> mdi-star-outline </v-icon>
</v-btn>
</template>
<span v-if="project.userActions.starred">{{ $t('project.actions.unstar') }}</span>
<span v-else>{{ $t('project.actions.star') }}</span>
</v-tooltip>
<v-tooltip v-if="!$util.isCurrentUser(project.owner.id)" bottom>
<template #activator="{ on }">
<v-btn icon @click="toggleWatch" v-on="on">
<v-icon v-if="project.userActions.watching"> mdi-eye-off </v-icon>
<v-icon v-else> mdi-eye </v-icon>
</v-btn>
</template>
<span v-if="project.userActions.watching">{{ $t('project.actions.unwatch') }}</span>
<span v-else>{{ $t('project.actions.watch') }}</span>
</v-tooltip>
<FlagModal v-if="isLoggedIn && !$util.isCurrentUser(project.owner.id)" :project="project" activator-class="ml-1" />
<v-menu v-if="$perms.isStaff" bottom offset-y open-on-hover>
<template #activator="{ on, attrs }">
<v-btn v-bind="attrs" class="ml-1" color="info" v-on="on">
{{ $t('project.actions.adminActions') }}
</v-btn>
</template>
<v-list>
<v-list-item :to="slug + '/flags'" nuxt>
<v-list-item-title>
{{ $t('project.actions.flagHistory', [project.info.flagCount]) }}
</v-list-item-title>
</v-list-item>
<v-list-item :to="slug + '/notes'" nuxt>
<v-list-item-title>
{{ $t('project.actions.staffNotes', [project.info.noteCount]) }}
</v-list-item-title>
</v-list-item>
<v-list-item :to="'/admin/log/?projectFilter=' + slug" nuxt>
<v-list-item-title>
{{ $t('project.actions.userActionLogs') }}
</v-list-item-title>
</v-list-item>
<v-list-item :href="$util.forumUrl(project.namespace.owner)">
<v-list-item-title>
{{ $t('project.actions.forum') }}
<v-icon>mdi-open-in-new</v-icon>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-row>
<v-col cols="auto" align-self="center">
<DownloadButton
v-if="project.recommendedVersions && Object.keys(project.recommendedVersions).length > 0"
:project="project"
:platform-selection="true"
:small="false"
/>
</v-col>
<ProjectTabs :project="project"></ProjectTabs>
</v-row>
<v-row>
<v-tabs>
<v-tab v-for="tab in tabs" :key="tab.title" :exact="!!tab.exact" :to="tab.external ? '/linkout?remoteUrl=' + tab.link : tab.link" nuxt>
<v-icon left>
{{ tab.icon }}
</v-icon>
{{ tab.title }}
<v-icon v-if="tab.external" small class="mb-1 ml-1" color="primary"> mdi-open-in-new </v-icon>
</v-tab>
</v-tabs>
</v-row>
</v-container>
</div>
<v-container>
<NuxtChild class="mt-5" :project="project">
<v-tab-item>
{{ $route.name }}
</v-tab-item>
</NuxtChild>
</v-container>
</div>
</template>
@ -127,24 +73,17 @@ import { Component } from 'nuxt-property-decorator';
import { Context } from '@nuxt/types';
import { HangarProject } from 'hangar-internal';
import { NavigationGuardNext, Route } from 'vue-router';
import { TranslateResult } from 'vue-i18n';
import { Markdown } from '~/components/markdown';
import FlagModal from '~/components/modals/projects/FlagModal.vue';
import { UserAvatar } from '~/components/users';
import { Visibility } from '~/types/enums';
import { HangarComponent } from '~/components/mixins';
interface Tab {
title: string | TranslateResult;
icon: string;
link: string;
external: boolean;
exact?: boolean;
}
import ProjectTabs from '~/components/projects/ProjectTabs.vue';
import DownloadButton from '~/components/projects/DownloadButton.vue';
@Component({
components: {
FlagModal,
DownloadButton,
ProjectTabs,
Markdown,
UserAvatar,
},
@ -171,32 +110,6 @@ export default class ProjectPage extends HangarComponent {
return { project };
}
get tabs(): Tab[] {
const tabs = [] as Tab[];
tabs.push({ title: this.$t('project.tabs.docs'), icon: 'mdi-book', link: this.slug, external: false, exact: true });
tabs.push({ title: this.$t('project.tabs.versions'), icon: 'mdi-download', link: this.slug + '/versions', external: false });
if ((this.project.settings.forumSync && this.project.postId) || this.$perms.canEditSubjectSettings) {
tabs.push({ title: this.$t('project.tabs.discuss'), icon: 'mdi-account-group', link: this.slug + '/discuss', external: false });
}
if (this.$perms.canEditSubjectSettings) {
tabs.push({ title: this.$t('project.tabs.settings'), icon: 'mdi-cog', link: this.slug + '/settings', external: false });
}
if (this.project.settings.homepage) {
tabs.push({ title: this.$t('project.tabs.homepage'), icon: 'mdi-home', link: this.project.settings.homepage, external: true });
}
if (this.project.settings.issues) {
tabs.push({ title: this.$t('project.tabs.issues'), icon: 'mdi-bug', link: this.project.settings.issues, external: true });
}
if (this.project.settings.source) {
tabs.push({ title: this.$t('project.tabs.source'), icon: 'mdi-code-tags', link: this.project.settings.source, external: true });
}
if (this.project.settings.support) {
tabs.push({ title: this.$t('project.tabs.support'), icon: 'mdi-chat-question', link: this.project.settings.support, external: true });
}
return tabs;
}
get isPublic(): Boolean {
return this.project.visibility === Visibility.PUBLIC;
}
@ -213,24 +126,6 @@ export default class ProjectPage extends HangarComponent {
return `/${this.project.namespace.owner}/${this.project.namespace.slug}`;
}
toggleStar() {
this.$api
.requestInternal<void>(`projects/project/${this.project.id}/star/${!this.project.userActions.starred}`, true, 'post')
.then(() => {
this.project.userActions.starred = !this.project.userActions.starred;
})
.catch((err) => this.$util.handleRequestError(err, 'project.error.star'));
}
toggleWatch() {
this.$api
.requestInternal(`projects/project/${this.project.id}/watch/${!this.project.userActions.watching}`, true, 'post')
.then(() => {
this.project.userActions.watching = !this.project.userActions.watching;
})
.catch((err) => this.$util.handleRequestError(err, 'project.error.watch'));
}
sendForApproval() {
this.loading.approval = true;
this.$api
@ -254,3 +149,39 @@ export default class ProjectPage extends HangarComponent {
}
}
</script>
<style lang="scss">
div.project-header {
background-color: #1d1d1d;
min-height: 125px;
.container {
padding: 15px 0 0 0;
.row {
margin: 0;
}
}
.col:first-child {
margin-left: 50px;
padding: 0;
}
.user-avatar-wrap,
.user-avatar {
height: 95px;
width: 95px;
}
a {
text-decoration: none;
color: white;
font-weight: normal;
}
.v-subheader {
padding: 0;
height: 18px;
}
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<v-row>
<v-col v-if="page.contents" cols="12" md="8">
<v-col v-if="page.contents" cols="12" md="8" class="main-page-content">
<v-divider />
<MarkdownEditor
v-if="$perms.canEditPage"
ref="editor"
@ -18,17 +19,48 @@
<v-row>
<v-col cols="12">
<v-card>
<v-card-title v-text="$t('project.info.title')" />
<v-card-text>
<DownloadButton
v-if="project.recommendedVersions && Object.keys(project.recommendedVersions).length > 0"
:project="project"
:platform-selection="true"
:small="false"
/>
<div v-if="project.settings.donation.enable" style="margin-top: 5px">
<v-card-title>
<v-col cols="auto">
{{ $t('project.info.title') }}
</v-col>
<v-col v-if="isLoggedIn && !$util.isCurrentUser(project.owner.id)" cols="auto">
<FlagModal :project="project" activator-class="ml-1" />
</v-col>
<v-col v-if="$perms.isStaff" cols="auto">
<v-menu bottom offset-y open-on-hover>
<template #activator="{ on, attrs }">
<v-btn v-bind="attrs" small class="ml-1" color="info" v-on="on">
{{ $t('project.actions.adminActions') }}
</v-btn>
</template>
<v-list>
<v-list-item :to="slug + '/flags'" nuxt>
<v-list-item-title>
{{ $t('project.actions.flagHistory', [project.info.flagCount]) }}
</v-list-item-title>
</v-list-item>
<v-list-item :to="slug + '/notes'" nuxt>
<v-list-item-title>
{{ $t('project.actions.staffNotes', [project.info.noteCount]) }}
</v-list-item-title>
</v-list-item>
<v-list-item :to="'/admin/log/?projectFilter=' + slug" nuxt>
<v-list-item-title>
{{ $t('project.actions.userActionLogs') }}
</v-list-item-title>
</v-list-item>
<v-list-item :href="$util.forumUrl(project.namespace.owner)">
<v-list-item-title>
{{ $t('project.actions.forum') }}
<v-icon>mdi-open-in-new</v-icon>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<v-col cols="auto">
<DonationModal
v-if="project.settings.donation.enable"
:donation-email="project.settings.donation.email"
:default-amount="project.settings.donation.defaultAmount"
:one-time-amounts="project.settings.donation.oneTimeAmounts"
@ -39,35 +71,63 @@
>
<template #activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on">
<v-icon left> mdi-currency-usd </v-icon>
<v-icon left> mdi-currency-usd</v-icon>
{{ $t('general.donate') }}
</v-btn>
</template>
</DonationModal>
</div>
</DonationModal></v-col
>
<v-col cols="auto">
<v-btn v-if="!$util.isCurrentUser(project.owner.id)" @click="toggleStar">
<v-icon v-if="project.userActions.starred" left color="amber"> mdi-star</v-icon>
<v-icon v-else lef> mdi-star-outline</v-icon>
<span v-if="project.userActions.starred">{{ $t('project.actions.unstar') }}</span>
<span v-else>{{ $t('project.actions.star') }}</span>
</v-btn>
</v-col>
<v-col cols="auto">
<v-btn v-if="!$util.isCurrentUser(project.owner.id)" @click="toggleWatch">
<v-icon v-if="project.userActions.watching" left> mdi-eye-off</v-icon>
<v-icon v-else left> mdi-eye</v-icon>
<span v-if="project.userActions.watching">{{ $t('project.actions.unwatch') }}</span>
<span v-else>{{ $t('project.actions.watch') }}</span>
</v-btn>
</v-col>
</v-card-title>
<v-card-text>
<v-divider class="mb-5" />
<div class="project-info">
<p>{{ $t('project.category.info', [$store.state.projectCategories.get(project.category).title]) }}</p>
<p>{{ $t('project.info.publishDate', [$util.prettyDate(project.createdAt)]) }}</p>
<p v-if="project">
<span id="view-count">{{ $tc('project.info.views', project.stats.views, [project.stats.views]) }}</span
>,&nbsp;<span id="download-count"
>{{ $tc('project.info.totalDownloads', project.stats.downloads, [project.stats.downloads]) }}
</span>
</p>
<p v-if="project">
<v-btn :to="`${$route.params.slug}/stars`" nuxt small>
{{ $tc('project.info.stars', project.stats.stars, [project.stats.stars]) }}
</v-btn>
<v-btn :to="`${$route.params.slug}/watchers`" nuxt small>
{{ $tc('project.info.watchers', project.stats.watchers, [project.stats.watchers]) }}
</v-btn>
</p>
<div class="float-left">
<p>{{ $t('project.category.info') }}</p>
<p>{{ $t('project.info.publishDate') }}</p>
<p>{{ $tc('project.info.views', project.stats.views) }}</p>
<p>{{ $tc('project.info.totalDownloads', project.stats.downloads) }}</p>
<NuxtLink :to="`${$route.params.slug}/stars`" nuxt small class="d-block">
{{ $tc('project.info.stars', project.stats.stars) }}
</NuxtLink>
<NuxtLink :to="`${$route.params.slug}/watchers`" nuxt small class="d-block">
{{ $tc('project.info.watchers', project.stats.watchers) }}
</NuxtLink>
<p v-if="project && project.settings.license && project.settings.license.name">
{{ $t('project.license.link') }}
</p>
</div>
<div class="float-right">
<p>{{ $store.state.projectCategories.get(project.category).title }}</p>
<p>{{ $util.prettyDate(project.createdAt) }}</p>
<p>{{ project.stats.views }}</p>
<p>{{ project.stats.downloads }}</p>
<p>{{ project.stats.stars }}</p>
<p>{{ project.stats.watchers }}</p>
<p v-if="project && project.settings.license && project.settings.license.name">
<a ref="noopener" :href="project.settings.license.url" target="_blank">{{ project.settings.license.name }}</a>
</p>
</div>
</div>
</v-card-text>
</v-card>
</v-col>
@ -112,9 +172,19 @@ import DonationModal from '~/components/donation/DonationModal.vue';
import { Markdown, MarkdownEditor } from '~/components/markdown';
import { DocPageMixin } from '~/components/mixins';
import { DownloadButton, MemberList, ProjectPageList } from '~/components/projects';
import FlagModal from '~/components/modals/projects/FlagModal.vue';
@Component({
components: { DownloadButton, ProjectPageList, Markdown, MemberList, DonationModal, MarkdownEditor, Tag },
components: {
FlagModal,
DownloadButton,
ProjectPageList,
Markdown,
MemberList,
DonationModal,
MarkdownEditor,
Tag,
},
})
export default class DocsPage extends DocPageMixin {
roles!: Role[];
@ -123,6 +193,28 @@ export default class DocsPage extends DocPageMixin {
return process.env.publicHost;
}
toggleStar() {
this.$api
.requestInternal<void>(`projects/project/${this.project.id}/star/${!this.project.userActions.starred}`, true, 'post')
.then(() => {
this.project.userActions.starred = !this.project.userActions.starred;
})
.catch((err) => this.$util.handleRequestError(err, 'project.error.star'));
}
toggleWatch() {
this.$api
.requestInternal(`projects/project/${this.project.id}/watch/${!this.project.userActions.watching}`, true, 'post')
.then(() => {
this.project.userActions.watching = !this.project.userActions.watching;
})
.catch((err) => this.$util.handleRequestError(err, 'project.error.watch'));
}
get slug(): string {
return `/${this.project.namespace.owner}/${this.project.namespace.slug}`;
}
async asyncData({ $api, params, $util }: Context) {
const page = await $api.requestInternal<ProjectPage>(`pages/page/${params.author}/${params.slug}`, false).catch<any>($util.handlePageRequestError);
const roles = await $api.requestInternal('data/projectRoles', false).catch($util.handlePageRequestError);
@ -131,8 +223,22 @@ export default class DocsPage extends DocPageMixin {
}
</script>
<style lang="scss" scoped>
.project-info p {
<style lang="scss">
.project-info {
height: 170px; // no idea why I have to set a height...
p {
margin-bottom: 3px;
}
.float-right p {
text-align: right;
}
}
.flag-modal-trigger {
display: inline;
}
.main-page-content {
padding-left: 25px;
}
</style>

View File

@ -1,7 +1,6 @@
<template>
<div>
<v-row>
<v-col v-if="page.contents" cols="12" md="9">
<v-col v-if="page.contents" cols="12" md="9" class="main-page-content">
<MarkdownEditor
v-if="$perms.canEditPage"
ref="editor"
@ -17,7 +16,6 @@
<ProjectPageList :project="project" />
</v-col>
</v-row>
</div>
</template>
<script lang="ts">

View File

@ -0,0 +1,26 @@
import LRUCache from 'lru-cache';
// @ts-ignore
import minifyTheme from 'minify-css-string';
import { ThemeOptions, VuetifyThemeCache, VuetifyThemeVariant } from 'vuetify/types/services/theme';
const themeCache = new LRUCache({
max: 10,
maxAge: 1000 * 60 * 60, // 1 hour
}) as VuetifyThemeCache;
export default function buildTheme(darkTheme: boolean, dark: Partial<VuetifyThemeVariant>, light: Partial<VuetifyThemeVariant>): ThemeOptions {
return {
default: darkTheme ? 'dark' : 'light',
dark: darkTheme,
disable: false,
options: {
customProperties: true,
themeCache,
minifyTheme,
},
themes: {
dark,
light,
},
};
}

View File

@ -0,0 +1,15 @@
import colors from 'vuetify/es5/util/colors';
import { VuetifyThemeVariant } from 'vuetify/types/services/theme';
export default {
anchor: colors.blue.lighten3,
primary: colors.blue.darken2,
background: '#111827',
box: '#212121',
accent: '#272727',
secondary: colors.amber.darken3,
info: colors.lightBlue.base,
warning: colors.orange.darken3,
error: colors.deepOrange.accent4,
success: colors.lightGreen.darken2,
} as Partial<VuetifyThemeVariant>;

View File

@ -0,0 +1,13 @@
import colors from 'vuetify/es5/util/colors';
import { VuetifyThemeVariant } from 'vuetify/types/services/theme';
export default {
anchor: colors.blue.lighten3,
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3,
} as Partial<VuetifyThemeVariant>;

View File

@ -1,6 +1,8 @@
import colors from 'vuetify/es5/util/colors';
import { VuetifyPreset } from 'vuetify';
import { Paper, Velocity, Waterfall } from '~/components/logos';
import buildTheme from '~/plugins/themes/base';
import defaultDark from '~/plugins/themes/default_dark';
import defaultLight from '~/plugins/themes/default_light';
export default {
icons: {
@ -17,34 +19,5 @@ export default {
},
},
},
theme: {
default: 'dark',
dark: true,
disable: false,
options: {
customProperties: true,
},
themes: {
dark: {
anchor: colors.blue.lighten3,
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.lightBlue.base,
warning: colors.orange.darken3,
error: colors.deepOrange.accent4,
success: colors.lightGreen.darken2,
},
light: {
anchor: colors.blue.lighten3,
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3,
},
},
},
theme: buildTheme(true, defaultDark, defaultLight),
} as Partial<VuetifyPreset>;

View File

@ -1834,6 +1834,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
"@types/lru-cache@5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef"
integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==
"@types/mime@^1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
@ -6673,6 +6678,13 @@ lowlight@^1.17.0:
fault "^1.0.0"
highlight.js "~10.6.0"
lru-cache@6.0.0, lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
lru-cache@^4.1.2:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@ -6688,13 +6700,6 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
lru-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
@ -6910,6 +6915,11 @@ mimic-fn@^3.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74"
integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==
minify-css-string@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minify-css-string/-/minify-css-string-1.0.0.tgz#201bd949271e19f6e0af0a1dc0ccc583de47c630"
integrity sha1-IBvZSSceGfbgrwodwMzFg95HxjA=
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"