(favorite) save favorite repos

This commit is contained in:
2021-03-19 23:01:31 +01:00
parent 2faabb6c0e
commit 5fcf3c9df5
11 changed files with 308 additions and 44 deletions

26
src/components/GoBack.vue Normal file
View File

@@ -0,0 +1,26 @@
<template>
<button class="button is-primary go-back" @click="back">
<img src="@/assets/icons/left-arrow.svg" alt="back" />
</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useRouter } from 'vue-router'
export default defineComponent({
name: 'GoBack',
setup() {
const { go } = useRouter()
return {
back: () => go(-1)
}
}
})
</script>
<style lang="scss" scoped>
.go-back {
}
</style>

View File

@@ -1,26 +1,56 @@
<template>
<div class="welcome-world">
<h1 class="title is-1">About "Lite notes"</h1>
<ol>
<li>
Take notes your favorite
<router-link :to="{ name: 'TextEditor' }">text editor</router-link>
</li>
<li>
Push to GitHub
</li>
<li>
Read it here
</li>
<li>
Share it with an URL
</li>
</ol>
<router-link :to="{ name: 'RepoList' }" v-if="isLogged"
>go to repos</router-link
>
<div class="columns">
<div class="column">
<h3 class="title is-3">Lite Note</h3>
<h4 class="subtitle is-4">Get started</h4>
<ol>
<li>
Take notes your favorite
<router-link :to="{ name: 'TextEditor' }">text editor</router-link>
</li>
<li>
Push to GitHub
</li>
<li>
Read it here
</li>
<li>
Share it with an URL
</li>
</ol>
</div>
<div class="column">
<p>
<router-link :to="{ name: 'RepoList' }" v-if="isLogged"
>Manage your repos</router-link
>
</p>
<section v-if="savedFavoriteRepos.length">
<h4 class="subtitle is-4">
</h4>
<ul>
<li
v-for="favoriteRepo in savedFavoriteRepos"
:key="favoriteRepo.id"
>
<router-link
:to="{
name: 'Home',
params: {
user: username,
repo: favoriteRepo.name
}
}"
>
{{ favoriteRepo.name }}
</router-link>
</li>
</ul>
</section>
</div>
</div>
<form @submit.prevent>
<div class="columns is-centered is-vcentered to-user-repo">
@@ -90,13 +120,15 @@
import { defineComponent } from 'vue'
import { useForm } from '@/hooks/useForm.hook'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
import { useFavoriteRepos } from '@/modules/repo/hooks/useFavoriteRepos.hook'
export default defineComponent({
name: 'WelcomeWord',
setup() {
const { isLogged } = useGitHubLogin()
const { isLogged, username } = useGitHubLogin()
const { savedFavoriteRepos } = useFavoriteRepos()
return { ...useForm(), isLogged }
return { ...useForm(), isLogged, username, savedFavoriteRepos }
}
})
</script>
@@ -107,6 +139,11 @@ export default defineComponent({
margin: auto;
display: flex;
flex-direction: column;
h3,
h4 {
text-align: center;
}
}
footer {

View File

@@ -1,3 +1,4 @@
export enum DataType {
GithubAccessToken = 'GithubAccessToken'
GithubAccessToken = 'GithubAccessToken',
FavoriteRepo = 'FavoriteRepo'
}

View File

@@ -10,10 +10,11 @@ interface GetAllParams {
prefix?: string
includeDocs?: boolean
includeAttachments?: boolean
keys?: string[]
}
class Data {
private locale = new PouchDb('local-db', {
private locale = new PouchDb('lite-note', {
adapter: 'indexeddb'
})
@@ -28,6 +29,24 @@ class Data {
}
}
public async update<DT extends DataType>(model: Model<DT>): Promise<boolean> {
try {
if (model._id) {
const oldModel = await this.get(model._id)
if (oldModel) {
const result = await this.locale.put({ ...oldModel, ...model })
return result.ok
}
}
const result = await this.locale.put(model)
return result.ok
} catch (error) {
console.warn(error)
return false
}
}
public async remove(id: string): Promise<boolean> {
try {
const doc = await this.get(id)
@@ -57,8 +76,19 @@ class Data {
public async getAll<DT extends DataType, T extends Model<DT>>({
prefix,
includeDocs = true,
includeAttachments = false
includeAttachments = false,
keys = []
}: GetAllParams): Promise<T[]> {
if (keys.length) {
const response = await this.locale.allDocs({
include_docs: includeDocs,
attachments: includeAttachments,
keys: keys.map((key) => this.generateId(prefix, key))
})
return response.rows.map((row) => row.doc).filter((doc) => !!doc) as T[]
}
const response = await this.locale.allDocs({
include_docs: includeDocs,
attachments: includeAttachments,
@@ -69,7 +99,11 @@ class Data {
return response.rows.map((row) => row.doc) as T[]
}
public generateId(type: DataType, id?: string) {
public generateId(type?: DataType | string, id?: string) {
if (!type) {
return id || nanoid()
}
return `${type}-${id || nanoid()}`
}
}

View File

@@ -1,10 +1,11 @@
import { Octokit } from '@octokit/rest'
import { RepoBase } from '@/modules/interfaces/RepoBase'
import { useAsyncState } from '@vueuse/core'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
export const useRepos = () => {
const { username, accessToken } = useGitHubLogin()
const repos = useAsyncState(async () => {
const repos = useAsyncState<RepoBase[]>(async () => {
if (!accessToken.value || !username.value) {
return []
}
@@ -18,7 +19,11 @@ export const useRepos = () => {
per_page: 100
})
return repoList.data.items.map((item) => item.name)
return repoList.data.items.map((item) => ({
id: `${item.id}`,
name: item.name,
isPrivate: item.private
}))
}, [])
return {

View File

@@ -0,0 +1,5 @@
export interface RepoBase {
id: string
name: string
isPrivate: boolean
}

View File

@@ -0,0 +1,8 @@
import { DataType } from '@/data/DataType.enum'
import { Model } from '@/data/models/Model'
export interface FavoriteRepo extends Model<DataType.FavoriteRepo> {
isFavorite: boolean
isPrivate: boolean
name: string
}

View File

@@ -0,0 +1,47 @@
import { computed, onMounted, ref } from 'vue'
import { DataType } from '@/data/DataType.enum'
import { FavoriteRepo } from '@/modules/models/FavoriteRepo'
import { RepoBase } from '@/modules/interfaces/RepoBase'
import { data } from '@/data/data'
import { useRepos } from '@/hooks/useRepos.hook'
export const useFavoriteRepos = () => {
const { repos } = useRepos()
const savedRepos = ref<FavoriteRepo[]>([])
const getFavorites = async () => {
savedRepos.value = await data.getAll<DataType.FavoriteRepo, FavoriteRepo>({
prefix: DataType.FavoriteRepo,
keys: repos.value.map((repo) => repo.id)
})
}
const savedFavoriteRepos = computed(() =>
savedRepos.value.filter((repo) => repo.isFavorite)
)
onMounted(() => {
getFavorites()
})
const toggleFavorite = async (repo: RepoBase, isFavorite: boolean) => {
const favorite: FavoriteRepo = {
_id: data.generateId(DataType.FavoriteRepo, repo.id),
$type: DataType.FavoriteRepo,
isFavorite,
name: repo.name,
isPrivate: repo.isPrivate
}
await data.update(favorite)
await getFavorites()
}
return {
savedRepos,
savedFavoriteRepos,
addFavorite: (repo: RepoBase) => toggleFavorite(repo, true),
removeFavorite: (repo: RepoBase) => toggleFavorite(repo, false)
}
}

View File

@@ -0,0 +1,42 @@
import { RepoBase } from '@/modules/interfaces/RepoBase'
import { computed } from 'vue'
import { useFavoriteRepos } from '@/modules/repo/hooks/useFavoriteRepos.hook'
import { useRepos } from '@/hooks/useRepos.hook'
export const useRepoList = () => {
const { savedFavoriteRepos, addFavorite, removeFavorite } = useFavoriteRepos()
const { repos } = useRepos()
const favoriteRepos = computed(() => {
return repos.value.filter((repo) =>
savedFavoriteRepos.value.find(
(fav) => fav._id?.includes(repo.id) ?? false
)
)
})
const otherRepos = computed(() => {
return repos.value.filter(
(repo) => !favoriteRepos.value.find((favorite) => favorite.id === repo.id)
)
})
const favoriteCheckboxes = computed(() =>
favoriteRepos.value.map((favorite) => favorite.id)
)
const toggleCheckbox = async (repo: RepoBase) => {
if (favoriteCheckboxes.value.includes(repo.id)) {
await removeFavorite(repo)
} else {
await addFavorite(repo)
}
}
return {
favoriteRepos,
otherRepos,
favoriteCheckboxes,
toggleCheckbox
}
}

View File

@@ -1,8 +1,8 @@
@charset "utf-8";
@import url('https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap');
$primary: #2C3A47;
$link: #58B19F;
$primary: #2c3a47;
$link: #58b19f;
@import '~bulma/bulma.sass';

View File

@@ -1,32 +1,88 @@
<template>
<div class="repo-list">
<h1>Repositories</h1>
<go-back />
<h1 class="title is-1">Repositories</h1>
<span v-if="!isReady">loading...</span>
<ul>
<li v-for="repo in repos" :key="repo">
<router-link
:to="{ name: 'Home', params: { user: username, repo: repo } }"
>
{{ repo }}
</router-link>
</li>
</ul>
<div v-else class="columns is-centered">
<div class="column is-one-third">
<table class="table is-striped is-hoverable is-fullwidth">
<tr v-for="repo in favoriteRepos" :key="repo.id">
<td>
<input
type="checkbox"
name="favorites"
:value="repo.id"
:checked="favoriteCheckboxes.includes(repo.id)"
@click="toggleCheckbox(repo)"
/>
</td>
<td>
<span v-if="repo.isPrivate">🔏</span>
<router-link
:to="{
name: 'Home',
params: { user: username, repo: repo.name }
}"
>
{{ repo.name }}
</router-link>
</td>
</tr>
<tr v-for="repo in otherRepos" :key="repo.id">
<td>
<input
type="checkbox"
name="favorites"
:value="repo.id"
:checked="favoriteCheckboxes.includes(repo.id)"
@click="toggleCheckbox(repo)"
/>
</td>
<td>
<router-link
:to="{
name: 'Home',
params: { user: username, repo: repo.name }
}"
>
{{ repo.name }}
</router-link>
</td>
</tr>
</table>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { useRepos } from '@/hooks/useRepos.hook'
import { useGitHubLogin } from '@/hooks/useGitHubLogin.hook'
import { useRepoList } from '@/modules/repo/hooks/userRepoList.hook'
import { useRepos } from '@/hooks/useRepos.hook'
import GoBack from '@/components/GoBack.vue'
export default defineComponent({
name: 'RepoList',
components: { GoBack },
setup() {
const { username } = useGitHubLogin()
const { isReady } = useRepos()
const {
favoriteRepos,
otherRepos,
favoriteCheckboxes,
toggleCheckbox
} = useRepoList()
return {
...useRepos(),
username
isReady,
username,
favoriteRepos,
otherRepos,
favoriteCheckboxes,
toggleCheckbox
}
}
})
@@ -35,6 +91,9 @@ export default defineComponent({
<style lang="scss" scoped>
.repo-list {
flex: 1;
padding: 1rem;
text-align: center;
overflow-y: auto;
}
</style>