Skip to content

Commit

Permalink
feat: load機能追加
Browse files Browse the repository at this point in the history
  • Loading branch information
nakasyou committed Feb 2, 2024
1 parent 98f1722 commit 9a9d4e3
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 18 deletions.
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"semi": false,
"singleQuote": true
"singleQuote": true,
"trailingComma": "none"
}
22 changes: 21 additions & 1 deletion src/islands/note-x/components/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { noteBookState, notes, setNoteBookState } from "../store"
import IconX from "@tabler/icons/x.svg?raw"
import { removeIconSize } from "../utils/icon/removeIconSize"
import { save } from "../utils/file-format"
import { load, save } from "../utils/file-format"

const CloseBtn = () => {
return (
Expand All @@ -25,6 +25,25 @@ export const Menu = () => {

atagForDownload.click()
}
const onLoad = () => {
const inputElement = document.createElement('input')
inputElement.type = 'file'
inputElement.oninput = async () => {
const files = inputElement.files
if (!files) {
return
}
const targetFile = files[0]
if (!targetFile) {
return
}

const loadResult = await load(targetFile)

console.log(loadResult)
}
inputElement.click()
}
return (
<div class="">
<div
Expand All @@ -50,6 +69,7 @@ export const Menu = () => {
<div class="flex justify-center items-center gap-4">
<button
class="filled-button"
onClick={onLoad}
>
Load
</button>
Expand Down
153 changes: 140 additions & 13 deletions src/islands/note-x/utils/file-format/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { zipSync, zip } from 'fflate'
import type { Note } from '../../components/notes-utils'
import { manifest0, type Manifest0, type Note0 } from './manifest-schema'

import { zipSync, zip, unzipSync, type Unzipped } from 'fflate'
import type { Note, NoteData } from '../../components/notes-utils'
import { manifest0, note0, type Manifest0, type Note0 } from './manifest-schema'
import { parse, type Output } from 'valibot'
interface FileTree {
[path: string]: Uint8Array
}

const textEncoder = new TextEncoder()
export const json2uint8Array = (jsonData: unknown): Uint8Array => textEncoder.encode(JSON.stringify(jsonData))
export const json2uint8Array = (jsonData: unknown): Uint8Array =>
textEncoder.encode(JSON.stringify(jsonData))

export const save = async (notes: Note[]): Promise<Blob> => {
const fileTree: FileTree = {}
Expand All @@ -19,40 +20,166 @@ export const save = async (notes: Note[]): Promise<Blob> => {

return {
version: 0,
noteIds,
noteIds
}
})()

fileTree['manifest.json'] = json2uint8Array(manifest)
fileTree['notes.json'] = json2uint8Array(manifest)

for (const [index, noteId] of Object.entries(manifest.noteIds)) {
const thisNote = notes[parseInt(index)]
const baseNoteDir = `notes/${noteId}`
if (!thisNote) {
continue
}
const baseNoteDir = `notes/${noteId.id}`

const blobMimetypes: Record<string, string> = {}
for (const [name, blob] of Object.entries(thisNote.noteData.blobs)) {
if (!blob) {
continue
}
fileTree[`${baseNoteDir}/${name}`] = new Uint8Array(await blob.arrayBuffer())
fileTree[`${baseNoteDir}/blobs/${name}`] = new Uint8Array(
await blob.arrayBuffer()
)
blobMimetypes[name] = blob.type
}

const noteJson: Note0 = (() => {
return {
version: 0,
type: thisNote.noteData.type,
blobMimetypes
blobMimetypes,

noteData: thisNote.noteData.canToJsonData
}
})()
fileTree[`${baseNoteDir}/notes.json`] = textEncoder.encode(JSON.stringify(noteJson))
fileTree[`${baseNoteDir}/note.json`] = textEncoder.encode(
JSON.stringify(noteJson)
)
}
const nnoteBuff = zipSync(fileTree)
const nnoteFile = new Blob([nnoteBuff], {
type: "application/x.nanohanote.nnote"
type: 'application/x.nanohanote.nnote'
})

return nnoteFile
}

export const load = (data: Blob) => {}
type LoadErrorType =
| 'FILE_ISNT_ZIP'
| 'NOTES_JSON_IS_NOT_FOUND'
| 'INVALID_NOTES_JSON'
| 'NOTE_DEFINE_FILE_IS_NOT_FOUND'
| 'NOTE_DEFINE_FILE_IS_INVALID'
| 'NOTE_BLOB_FILE_IS_NOT_FOUND'

type LoadResult =
| {
success: true
notes: NoteData[]
}
| {
success: false
error: {
type: LoadErrorType
debug?: unknown
}
}

export const load = async (data: Blob): Promise<LoadResult> => {
let unzipped: FileTree
// 解凍
try {
unzipped = unzipSync(new Uint8Array(await data.arrayBuffer()))
} catch (e) {
return {
success: false,
error: { type: 'FILE_ISNT_ZIP' }
}
}
const notesJson = unzipped['notes.json']
if (!notesJson) {
return {
success: false,
error: { type: 'NOTES_JSON_IS_NOT_FOUND' }
}
}

let notesJsonData: Output<typeof manifest0>
try {
notesJsonData = parse(
manifest0,
JSON.parse(new TextDecoder().decode(notesJson))
)
} catch (e) {
return {
success: false,
error: {
type: 'INVALID_NOTES_JSON'
}
}
}

const newNoteDatas: NoteData[] = []
for (const { id } of notesJsonData.noteIds) {
const noteFolder = `notes/${id}`
const noteDefineJsonPath = `${noteFolder}/note.json`

console.log(unzipped, noteDefineJsonPath)
const noteDefineJsonBuff = unzipped[noteDefineJsonPath]
if (!noteDefineJsonBuff) {
return {
success: false,
error: {
type: 'NOTE_DEFINE_FILE_IS_NOT_FOUND',
debug: {
onErrorNoteId: id
}
}
}
}

let noteDefineJsonData: Note0
try {
noteDefineJsonData = parse(
note0,
JSON.parse(new TextDecoder().decode(noteDefineJsonBuff))
)
} catch (error) {
return {
success: false,
error: {
type: 'NOTE_DEFINE_FILE_IS_INVALID',
debug: {
onErrorNoteId: id
}
}
}
}
const blobs: Record<string, Blob> = {}
for (const [name, mime] of Object.entries(noteDefineJsonData.blobMimetypes)) {
const blobPath = `${noteFolder}/blobs/${name}`
const blob = unzipped[blobPath]
if (!blob) {
return {
success: false,
error: {
type: 'NOTE_BLOB_FILE_IS_NOT_FOUND'
}
}
}
blobs[name] = new Blob([blob], {
type: mime
})
}
newNoteDatas.push({
canToJsonData: noteDefineJsonData.noteData,
blobs,
type: noteDefineJsonData.type
})
}
return {
success: true,
notes: newNoteDatas
}
}
6 changes: 4 additions & 2 deletions src/islands/note-x/utils/file-format/manifest-schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { object, literal, array, string, uuid, type Input, union, record } from 'valibot'
import { object, literal, array, string, uuid, type Input, union, record, unknown } from 'valibot'

/**
* Manifest Version 0
Expand All @@ -23,6 +23,8 @@ export const note0 = object({
literal('image')
]),

blobMimetypes: record(string(), string())
blobMimetypes: record(string(), string()),

noteData: unknown()
})
export type Note0 = Input<typeof note0>
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"allowImportingTsExtensions": true,
"types": [
"bun-types"
]
],
"noUncheckedIndexedAccess": true
}
}

0 comments on commit 9a9d4e3

Please sign in to comment.