Spaces:
Sleeping
Sleeping
Yann
commited on
Commit
Β·
5b508b2
1
Parent(s):
c529a9f
removed all firebase features
Browse files- .firebaserc +0 -5
- README.md +4 -4
- firebase.json +0 -17
- firestore.indexes.json +0 -4
- firestore.rules +0 -15
- package.json +1 -5
- src/common/song/Song.ts +0 -3
- src/community/components/Navigation.tsx +0 -11
- src/community/components/RootView.tsx +0 -3
- src/community/components/SongList.tsx +0 -39
- src/community/stores/CommunitySongStore.ts +0 -22
- src/community/stores/RootStore.ts +0 -4
- src/firebase/firebase.ts +0 -23
- src/firebase/song.ts +0 -155
- src/main/components/CloudFileDialog/CloudFileDialog.tsx +0 -41
- src/main/components/CloudFileDialog/CloudFileList.tsx +0 -176
- src/main/components/CloudFileDialog/CloudFileRow.tsx +0 -120
- src/main/components/FirebaseAuth/StyledFirebaseAuth.tsx +0 -106
- src/main/components/Navigation/CloudFileMenu.tsx +0 -244
- src/main/components/Navigation/FileMenuButton.tsx +3 -6
- src/main/components/Navigation/Navigation.tsx +0 -1
- src/main/components/Navigation/UserButton.tsx +1 -11
- src/main/components/RootView/RootView.tsx +0 -2
- src/main/components/SignInDialog/SignInDialog.tsx +0 -39
- src/main/components/SignInDialog/SignInDialogContent.tsx +0 -86
- src/main/stores/AuthStore.ts +0 -17
- src/main/stores/CloudFileStore.ts +0 -65
- src/main/stores/RootStore.ts +0 -4
.firebaserc
DELETED
@@ -1,5 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"projects": {
|
3 |
-
"default": "signal-9546d"
|
4 |
-
}
|
5 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
---
|
2 |
-
title: Midi
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
|
|
1 |
---
|
2 |
+
title: Signal Midi Frontend Demo
|
3 |
+
emoji: π§βπ³
|
4 |
+
colorFrom: yellow
|
5 |
+
colorTo: green
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
firebase.json
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"firestore": {
|
3 |
-
"rules": "firestore.rules",
|
4 |
-
"indexes": "firestore.indexes.json"
|
5 |
-
},
|
6 |
-
"emulators": {
|
7 |
-
"auth": {
|
8 |
-
"port": 9099
|
9 |
-
},
|
10 |
-
"firestore": {
|
11 |
-
"port": 8080
|
12 |
-
},
|
13 |
-
"ui": {
|
14 |
-
"enabled": true
|
15 |
-
}
|
16 |
-
}
|
17 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
firestore.indexes.json
DELETED
@@ -1,4 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"indexes": [],
|
3 |
-
"fieldOverrides": []
|
4 |
-
}
|
|
|
|
|
|
|
|
|
|
firestore.rules
DELETED
@@ -1,15 +0,0 @@
|
|
1 |
-
rules_version = '2';
|
2 |
-
service cloud.firestore {
|
3 |
-
match /databases/{database}/documents {
|
4 |
-
match /songs/{songId} {
|
5 |
-
allow read, delete: if request.auth != null && request.auth.uid == resource.data.userId;
|
6 |
-
allow update: if request.auth != null && request.auth.uid == resource.data.userId && !('uid' in request.writeFields);
|
7 |
-
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
|
8 |
-
}
|
9 |
-
match /songData/{songDataId} {
|
10 |
-
allow read, delete: if request.auth != null && request.auth.uid == resource.data.userId;
|
11 |
-
allow update: if request.auth != null && request.auth.uid == resource.data.userId && !('uid' in request.writeFields);
|
12 |
-
allow create: if request.auth != null && request.auth.uid == request.resource.data.userId;
|
13 |
-
}
|
14 |
-
}
|
15 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package.json
CHANGED
@@ -8,9 +8,7 @@
|
|
8 |
"serve": "npx http-server dist",
|
9 |
"test": "jest",
|
10 |
"format": "prettier --write src",
|
11 |
-
"lint": "prettier --check src"
|
12 |
-
"firebase": "firebase emulators:start",
|
13 |
-
"firebase:deploy": "firebase deploy"
|
14 |
},
|
15 |
"repository": {
|
16 |
"type": "git",
|
@@ -41,8 +39,6 @@
|
|
41 |
"@sentry/react": "^7.69.0",
|
42 |
"@sentry/tracing": "^7.69.0",
|
43 |
"color": "^4.2.3",
|
44 |
-
"firebase": "^10.4.0",
|
45 |
-
"firebaseui": "^6.1.0",
|
46 |
"gl-matrix": "^3.4.3",
|
47 |
"lodash": "^4.17.21",
|
48 |
"mdi-react": "^9.2.0",
|
|
|
8 |
"serve": "npx http-server dist",
|
9 |
"test": "jest",
|
10 |
"format": "prettier --write src",
|
11 |
+
"lint": "prettier --check src"
|
|
|
|
|
12 |
},
|
13 |
"repository": {
|
14 |
"type": "git",
|
|
|
39 |
"@sentry/react": "^7.69.0",
|
40 |
"@sentry/tracing": "^7.69.0",
|
41 |
"color": "^4.2.3",
|
|
|
|
|
42 |
"gl-matrix": "^3.4.3",
|
43 |
"lodash": "^4.17.21",
|
44 |
"mdi-react": "^9.2.0",
|
src/common/song/Song.ts
CHANGED
@@ -9,7 +9,6 @@ import {
|
|
9 |
transaction,
|
10 |
} from "mobx"
|
11 |
import { createModelSchema, list, object, primitive } from "serializr"
|
12 |
-
import { FirestoreSong, FirestoreSongData } from "../../firebase/song"
|
13 |
import { TIME_BASE } from "../../main/Constants"
|
14 |
import { isNotUndefined } from "../helpers/array"
|
15 |
import { Measure } from "../measure/Measure"
|
@@ -25,8 +24,6 @@ export default class Song {
|
|
25 |
timebase: number = TIME_BASE
|
26 |
name: string = ""
|
27 |
fileHandle: FileSystemFileHandle | null = null
|
28 |
-
firestoreReference: DocumentReference<FirestoreSong> | null = null
|
29 |
-
firestoreDataReference: DocumentReference<FirestoreSongData> | null = null
|
30 |
isSaved = true
|
31 |
|
32 |
constructor() {
|
|
|
9 |
transaction,
|
10 |
} from "mobx"
|
11 |
import { createModelSchema, list, object, primitive } from "serializr"
|
|
|
12 |
import { TIME_BASE } from "../../main/Constants"
|
13 |
import { isNotUndefined } from "../helpers/array"
|
14 |
import { Measure } from "../measure/Measure"
|
|
|
24 |
timebase: number = TIME_BASE
|
25 |
name: string = ""
|
26 |
fileHandle: FileSystemFileHandle | null = null
|
|
|
|
|
27 |
isSaved = true
|
28 |
|
29 |
constructor() {
|
src/community/components/Navigation.tsx
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
import styled from "@emotion/styled"
|
2 |
import { FC } from "react"
|
3 |
-
import { auth } from "../../firebase/firebase"
|
4 |
import { UserButtonContent } from "../../main/components/Navigation/UserButtonContent"
|
5 |
import { useStores } from "../hooks/useStores"
|
6 |
|
@@ -26,9 +25,6 @@ const NavigationWrapper = styled.div`
|
|
26 |
`
|
27 |
|
28 |
export const Navigation: FC = () => {
|
29 |
-
const {
|
30 |
-
authStore: { user },
|
31 |
-
} = useStores()
|
32 |
|
33 |
return (
|
34 |
<NavigationWrapper>
|
@@ -36,13 +32,6 @@ export const Navigation: FC = () => {
|
|
36 |
<LogoWrapper href="/">
|
37 |
<img src="logo-white.svg" style={{ height: "1.7rem" }} />
|
38 |
</LogoWrapper>
|
39 |
-
<UserButtonContent
|
40 |
-
user={user}
|
41 |
-
onClickSignIn={() => {}}
|
42 |
-
onClickSignOut={async () => {
|
43 |
-
await auth.signOut()
|
44 |
-
}}
|
45 |
-
/>
|
46 |
</Container>
|
47 |
</NavigationWrapper>
|
48 |
)
|
|
|
1 |
import styled from "@emotion/styled"
|
2 |
import { FC } from "react"
|
|
|
3 |
import { UserButtonContent } from "../../main/components/Navigation/UserButtonContent"
|
4 |
import { useStores } from "../hooks/useStores"
|
5 |
|
|
|
25 |
`
|
26 |
|
27 |
export const Navigation: FC = () => {
|
|
|
|
|
|
|
28 |
|
29 |
return (
|
30 |
<NavigationWrapper>
|
|
|
32 |
<LogoWrapper href="/">
|
33 |
<img src="logo-white.svg" style={{ height: "1.7rem" }} />
|
34 |
</LogoWrapper>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
</Container>
|
36 |
</NavigationWrapper>
|
37 |
)
|
src/community/components/RootView.tsx
CHANGED
@@ -2,7 +2,6 @@ import styled from "@emotion/styled"
|
|
2 |
import { FC } from "react"
|
3 |
import { BottomPlayer } from "./BottomPlayer"
|
4 |
import { Navigation } from "./Navigation"
|
5 |
-
import { SongList } from "./SongList"
|
6 |
|
7 |
const Container = styled.div`
|
8 |
display: flex;
|
@@ -35,8 +34,6 @@ export const RootView: FC = () => {
|
|
35 |
<Navigation />
|
36 |
<Content>
|
37 |
<Inner>
|
38 |
-
<Title>Community Tracks</Title>
|
39 |
-
<SongList />
|
40 |
</Inner>
|
41 |
</Content>
|
42 |
<BottomPlayer />
|
|
|
2 |
import { FC } from "react"
|
3 |
import { BottomPlayer } from "./BottomPlayer"
|
4 |
import { Navigation } from "./Navigation"
|
|
|
5 |
|
6 |
const Container = styled.div`
|
7 |
display: flex;
|
|
|
34 |
<Navigation />
|
35 |
<Content>
|
36 |
<Inner>
|
|
|
|
|
37 |
</Inner>
|
38 |
</Content>
|
39 |
<BottomPlayer />
|
src/community/components/SongList.tsx
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
import { observer } from "mobx-react-lite"
|
2 |
-
import { FC, useEffect } from "react"
|
3 |
-
import { useStores } from "../hooks/useStores"
|
4 |
-
import { SongListItem } from "./SongListItem"
|
5 |
-
|
6 |
-
export const SongList: FC = observer(() => {
|
7 |
-
const { authStore, communitySongStore } = useStores()
|
8 |
-
|
9 |
-
useEffect(() => {
|
10 |
-
;(async () => {
|
11 |
-
if (authStore.user) {
|
12 |
-
// TODO: Remove checking user is logged-in
|
13 |
-
await communitySongStore.load()
|
14 |
-
}
|
15 |
-
})()
|
16 |
-
}, [])
|
17 |
-
|
18 |
-
const { songs } = communitySongStore
|
19 |
-
|
20 |
-
const items = songs.map((d) => ({
|
21 |
-
song: {
|
22 |
-
id: d.id,
|
23 |
-
name: d.data().name,
|
24 |
-
updatedAt: new Date(d.data().updatedAt.toDate()),
|
25 |
-
},
|
26 |
-
user: {
|
27 |
-
name: d.data().userId,
|
28 |
-
photoURL: "",
|
29 |
-
},
|
30 |
-
}))
|
31 |
-
|
32 |
-
return (
|
33 |
-
<>
|
34 |
-
{items.map((s) => (
|
35 |
-
<SongListItem song={s.song} user={s.user} />
|
36 |
-
))}
|
37 |
-
</>
|
38 |
-
)
|
39 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/community/stores/CommunitySongStore.ts
DELETED
@@ -1,22 +0,0 @@
|
|
1 |
-
import { QueryDocumentSnapshot } from "@firebase/firestore"
|
2 |
-
import { makeObservable, observable } from "mobx"
|
3 |
-
import { FirestoreSong, getCurrentUserSongs } from "../../firebase/song"
|
4 |
-
|
5 |
-
export class CommunitySongStore {
|
6 |
-
isLoading = false
|
7 |
-
songs: QueryDocumentSnapshot<FirestoreSong>[] = []
|
8 |
-
|
9 |
-
constructor() {
|
10 |
-
makeObservable(this, {
|
11 |
-
isLoading: observable,
|
12 |
-
songs: observable,
|
13 |
-
})
|
14 |
-
}
|
15 |
-
|
16 |
-
async load() {
|
17 |
-
this.isLoading = true
|
18 |
-
const snapshot = await getCurrentUserSongs()
|
19 |
-
this.songs = snapshot.docs
|
20 |
-
this.isLoading = false
|
21 |
-
}
|
22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/community/stores/RootStore.ts
CHANGED
@@ -1,13 +1,9 @@
|
|
1 |
import Player from "../../common/player"
|
2 |
import { SoundFontSynth } from "../../main/services/SoundFontSynth"
|
3 |
-
import { AuthStore } from "../../main/stores/AuthStore"
|
4 |
-
import { CommunitySongStore } from "./CommunitySongStore"
|
5 |
import { SongStore } from "./SongStore"
|
6 |
|
7 |
export default class RootStore {
|
8 |
readonly songStore = new SongStore()
|
9 |
-
readonly authStore = new AuthStore()
|
10 |
-
readonly communitySongStore = new CommunitySongStore()
|
11 |
readonly player: Player
|
12 |
readonly synth: SoundFontSynth
|
13 |
|
|
|
1 |
import Player from "../../common/player"
|
2 |
import { SoundFontSynth } from "../../main/services/SoundFontSynth"
|
|
|
|
|
3 |
import { SongStore } from "./SongStore"
|
4 |
|
5 |
export default class RootStore {
|
6 |
readonly songStore = new SongStore()
|
|
|
|
|
7 |
readonly player: Player
|
8 |
readonly synth: SoundFontSynth
|
9 |
|
src/firebase/firebase.ts
DELETED
@@ -1,23 +0,0 @@
|
|
1 |
-
import { initializeApp } from "firebase/app"
|
2 |
-
import { connectAuthEmulator, getAuth } from "firebase/auth"
|
3 |
-
import { connectFirestoreEmulator, getFirestore } from "firebase/firestore"
|
4 |
-
|
5 |
-
const firebaseConfig = {
|
6 |
-
apiKey: "AIzaSyC5b6N6A1fFxdUyWdZh0RqxvfdM--YD8P0",
|
7 |
-
authDomain: "signal-9546d.firebaseapp.com",
|
8 |
-
projectId: "signal-9546d",
|
9 |
-
storageBucket: "signal-9546d.appspot.com",
|
10 |
-
messagingSenderId: "312735607354",
|
11 |
-
appId: "1:312735607354:web:78b487832370b170e32303",
|
12 |
-
}
|
13 |
-
|
14 |
-
const app = initializeApp(firebaseConfig)
|
15 |
-
|
16 |
-
export const auth = getAuth(app)
|
17 |
-
|
18 |
-
export const firestore = getFirestore(app)
|
19 |
-
|
20 |
-
if (process.env.NODE_ENV !== "production") {
|
21 |
-
connectAuthEmulator(auth, "http://localhost:9099")
|
22 |
-
connectFirestoreEmulator(firestore, "localhost", 8080)
|
23 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/firebase/song.ts
DELETED
@@ -1,155 +0,0 @@
|
|
1 |
-
import {
|
2 |
-
addDoc,
|
3 |
-
Bytes,
|
4 |
-
collection,
|
5 |
-
deleteDoc,
|
6 |
-
DocumentReference,
|
7 |
-
FirestoreDataConverter,
|
8 |
-
getDoc,
|
9 |
-
getDocs,
|
10 |
-
query,
|
11 |
-
QueryDocumentSnapshot,
|
12 |
-
serverTimestamp,
|
13 |
-
Timestamp,
|
14 |
-
updateDoc,
|
15 |
-
where,
|
16 |
-
} from "firebase/firestore"
|
17 |
-
import { songFromMidi, songToMidi } from "../common/midi/midiConversion"
|
18 |
-
import Song from "../common/song"
|
19 |
-
import { auth, firestore } from "./firebase"
|
20 |
-
|
21 |
-
export interface FirestoreSongData {
|
22 |
-
createdAt: Timestamp
|
23 |
-
updatedAt: Timestamp
|
24 |
-
data?: Bytes
|
25 |
-
userId: string
|
26 |
-
}
|
27 |
-
|
28 |
-
export interface FirestoreSong {
|
29 |
-
name: string
|
30 |
-
createdAt: Timestamp
|
31 |
-
updatedAt: Timestamp
|
32 |
-
dataRef: DocumentReference
|
33 |
-
userId: string
|
34 |
-
}
|
35 |
-
|
36 |
-
export const songConverter: FirestoreDataConverter<FirestoreSong> = {
|
37 |
-
fromFirestore(snapshot, options) {
|
38 |
-
const data = snapshot.data(options)
|
39 |
-
return data as FirestoreSong
|
40 |
-
},
|
41 |
-
toFirestore(song) {
|
42 |
-
return song
|
43 |
-
},
|
44 |
-
}
|
45 |
-
|
46 |
-
export const songDataConverter: FirestoreDataConverter<FirestoreSongData> = {
|
47 |
-
fromFirestore(snapshot, options) {
|
48 |
-
const data = snapshot.data(options)
|
49 |
-
return data as FirestoreSongData
|
50 |
-
},
|
51 |
-
toFirestore(song) {
|
52 |
-
return song
|
53 |
-
},
|
54 |
-
}
|
55 |
-
|
56 |
-
export const songCollection = collection(firestore, "songs").withConverter(
|
57 |
-
songConverter,
|
58 |
-
)
|
59 |
-
|
60 |
-
export const songDataCollection = collection(
|
61 |
-
firestore,
|
62 |
-
"songData",
|
63 |
-
).withConverter(songDataConverter)
|
64 |
-
|
65 |
-
export const loadSong = async (
|
66 |
-
songSnapshot: QueryDocumentSnapshot<FirestoreSong>,
|
67 |
-
) => {
|
68 |
-
const snapshot = await getDoc(
|
69 |
-
songSnapshot.data().dataRef.withConverter(songDataConverter),
|
70 |
-
)
|
71 |
-
const data = snapshot.data()?.data
|
72 |
-
if (data === undefined) {
|
73 |
-
throw new Error("Song data does not exist")
|
74 |
-
}
|
75 |
-
const song = songFromMidi(data.toUint8Array())
|
76 |
-
song.name = songSnapshot.data().name
|
77 |
-
song.firestoreReference = songSnapshot.ref
|
78 |
-
song.firestoreDataReference = snapshot.ref
|
79 |
-
song.isSaved = true
|
80 |
-
return song
|
81 |
-
}
|
82 |
-
|
83 |
-
export const createSong = async (song: Song) => {
|
84 |
-
if (auth.currentUser === null) {
|
85 |
-
throw new Error("You must be logged in to save songs to the cloud")
|
86 |
-
}
|
87 |
-
|
88 |
-
const bytes = songToMidi(song)
|
89 |
-
|
90 |
-
const dataDoc = await addDoc(songDataCollection, {
|
91 |
-
createdAt: serverTimestamp(),
|
92 |
-
updatedAt: serverTimestamp(),
|
93 |
-
data: Bytes.fromUint8Array(bytes),
|
94 |
-
userId: auth.currentUser.uid,
|
95 |
-
})
|
96 |
-
|
97 |
-
const doc = await addDoc(songCollection, {
|
98 |
-
name: song.name,
|
99 |
-
createdAt: serverTimestamp(),
|
100 |
-
updatedAt: serverTimestamp(),
|
101 |
-
dataRef: dataDoc,
|
102 |
-
userId: auth.currentUser.uid,
|
103 |
-
})
|
104 |
-
|
105 |
-
song.firestoreDataReference = dataDoc
|
106 |
-
song.firestoreReference = doc
|
107 |
-
song.isSaved = true
|
108 |
-
}
|
109 |
-
|
110 |
-
export const updateSong = async (song: Song) => {
|
111 |
-
if (auth.currentUser === null) {
|
112 |
-
throw new Error("You must be logged in to save songs to the cloud")
|
113 |
-
}
|
114 |
-
|
115 |
-
if (
|
116 |
-
song.firestoreReference === null ||
|
117 |
-
song.firestoreDataReference === null
|
118 |
-
) {
|
119 |
-
throw new Error("This song is not loaded from the cloud")
|
120 |
-
}
|
121 |
-
|
122 |
-
const bytes = songToMidi(song)
|
123 |
-
|
124 |
-
await updateDoc(song.firestoreReference, {
|
125 |
-
updatedAt: serverTimestamp(),
|
126 |
-
name: song.name,
|
127 |
-
})
|
128 |
-
|
129 |
-
await updateDoc(song.firestoreDataReference, {
|
130 |
-
updatedAt: serverTimestamp(),
|
131 |
-
data: Bytes.fromUint8Array(bytes),
|
132 |
-
})
|
133 |
-
|
134 |
-
song.isSaved = true
|
135 |
-
}
|
136 |
-
|
137 |
-
export const deleteSong = async (
|
138 |
-
song: QueryDocumentSnapshot<FirestoreSong>,
|
139 |
-
) => {
|
140 |
-
if (auth.currentUser === null) {
|
141 |
-
throw new Error("You must be logged in to save songs to the cloud")
|
142 |
-
}
|
143 |
-
await deleteDoc(song.data().dataRef)
|
144 |
-
await deleteDoc(song.ref)
|
145 |
-
}
|
146 |
-
|
147 |
-
export const getCurrentUserSongs = async () => {
|
148 |
-
if (auth.currentUser === null) {
|
149 |
-
throw new Error("You must be logged in to get songs from the cloud")
|
150 |
-
}
|
151 |
-
|
152 |
-
return await getDocs(
|
153 |
-
query(songCollection, where("userId", "==", auth.currentUser.uid)),
|
154 |
-
)
|
155 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/CloudFileDialog/CloudFileDialog.tsx
DELETED
@@ -1,41 +0,0 @@
|
|
1 |
-
import { observer } from "mobx-react-lite"
|
2 |
-
import { useCallback } from "react"
|
3 |
-
import { Button } from "../../../components/Button"
|
4 |
-
import {
|
5 |
-
Dialog,
|
6 |
-
DialogActions,
|
7 |
-
DialogContent,
|
8 |
-
DialogTitle,
|
9 |
-
} from "../../../components/Dialog"
|
10 |
-
import { Localized } from "../../../components/Localized"
|
11 |
-
import { useStores } from "../../hooks/useStores"
|
12 |
-
import { CloudFileList } from "./CloudFileList"
|
13 |
-
|
14 |
-
export const CloudFileDialog = observer(() => {
|
15 |
-
const rootStore = useStores()
|
16 |
-
const {
|
17 |
-
rootViewStore,
|
18 |
-
rootViewStore: { openCloudFileDialog },
|
19 |
-
} = rootStore
|
20 |
-
|
21 |
-
const onClose = useCallback(
|
22 |
-
() => (rootViewStore.openCloudFileDialog = false),
|
23 |
-
[rootViewStore],
|
24 |
-
)
|
25 |
-
|
26 |
-
return (
|
27 |
-
<Dialog open={openCloudFileDialog} onOpenChange={onClose}>
|
28 |
-
<DialogTitle>
|
29 |
-
<Localized default="Files">files</Localized>
|
30 |
-
</DialogTitle>
|
31 |
-
<DialogContent>
|
32 |
-
<CloudFileList />
|
33 |
-
</DialogContent>
|
34 |
-
<DialogActions>
|
35 |
-
<Button onClick={onClose}>
|
36 |
-
<Localized default="Close">close</Localized>
|
37 |
-
</Button>
|
38 |
-
</DialogActions>
|
39 |
-
</Dialog>
|
40 |
-
)
|
41 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/CloudFileDialog/CloudFileList.tsx
DELETED
@@ -1,176 +0,0 @@
|
|
1 |
-
import styled from "@emotion/styled"
|
2 |
-
import { QueryDocumentSnapshot } from "firebase/firestore"
|
3 |
-
import ArrowDownward from "mdi-react/ArrowDownwardIcon"
|
4 |
-
import ArrowDropDown from "mdi-react/ArrowDropDownIcon"
|
5 |
-
import ArrowUpward from "mdi-react/ArrowUpwardIcon"
|
6 |
-
import { observer } from "mobx-react-lite"
|
7 |
-
import { FC, useEffect } from "react"
|
8 |
-
import { CircularProgress } from "../../../components/CircularProgress"
|
9 |
-
import { IconButton } from "../../../components/IconButton"
|
10 |
-
import { Localized } from "../../../components/Localized"
|
11 |
-
import { Menu, MenuItem } from "../../../components/Menu"
|
12 |
-
import { FirestoreSong, loadSong } from "../../../firebase/song"
|
13 |
-
import { setSong } from "../../actions"
|
14 |
-
import { useLocalization } from "../../hooks/useLocalization"
|
15 |
-
import { useStores } from "../../hooks/useStores"
|
16 |
-
import { useTheme } from "../../hooks/useTheme"
|
17 |
-
import { useToast } from "../../hooks/useToast"
|
18 |
-
import { CloudFileRow } from "./CloudFileRow"
|
19 |
-
|
20 |
-
const ArrowUp = styled(ArrowUpward)`
|
21 |
-
width: 1.1rem;
|
22 |
-
height: 1.1rem;
|
23 |
-
`
|
24 |
-
|
25 |
-
const ArrowDown = styled(ArrowDownward)`
|
26 |
-
width: 1.1rem;
|
27 |
-
height: 1.1rem;
|
28 |
-
`
|
29 |
-
|
30 |
-
const HeaderCell = styled.div`
|
31 |
-
display: flex;
|
32 |
-
align-items: center;
|
33 |
-
background: ${({ theme }) => theme.backgroundColor};
|
34 |
-
font-weight: ${({ isSelected }: { isSelected?: boolean }) =>
|
35 |
-
isSelected ? "bold" : "normal"};
|
36 |
-
cursor: pointer;
|
37 |
-
padding: 0 1rem;
|
38 |
-
box-sizing: border-box;
|
39 |
-
|
40 |
-
&:hover {
|
41 |
-
background: ${({ theme }) => theme.secondaryBackgroundColor};
|
42 |
-
}
|
43 |
-
`
|
44 |
-
|
45 |
-
const NameCell = styled(HeaderCell)`
|
46 |
-
flex-grow: 1;
|
47 |
-
`
|
48 |
-
|
49 |
-
const DateCell = styled(HeaderCell)`
|
50 |
-
width: 12rem;
|
51 |
-
`
|
52 |
-
|
53 |
-
const MenuCell = styled(HeaderCell)`
|
54 |
-
width: 4rem;
|
55 |
-
`
|
56 |
-
|
57 |
-
const Body = styled.div`
|
58 |
-
max-height: 20rem;
|
59 |
-
overflow-y: auto;
|
60 |
-
|
61 |
-
tr:hover td {
|
62 |
-
background: ${({ theme }) => theme.secondaryBackgroundColor};
|
63 |
-
}
|
64 |
-
`
|
65 |
-
|
66 |
-
const SortButton: FC<{ sortAscending: boolean }> = ({ sortAscending }) =>
|
67 |
-
sortAscending ? <ArrowDown /> : <ArrowUp />
|
68 |
-
|
69 |
-
const Container = styled.div``
|
70 |
-
|
71 |
-
const Header = styled.div`
|
72 |
-
display: flex;
|
73 |
-
height: 2.5rem;
|
74 |
-
`
|
75 |
-
|
76 |
-
export const CloudFileList = observer(() => {
|
77 |
-
const rootStore = useStores()
|
78 |
-
const { cloudFileStore, rootViewStore } = rootStore
|
79 |
-
const toast = useToast()
|
80 |
-
const theme = useTheme()
|
81 |
-
const localized = useLocalization()
|
82 |
-
const { isLoading, dateType, files, selectedColumn, sortAscending } =
|
83 |
-
cloudFileStore
|
84 |
-
|
85 |
-
useEffect(() => {
|
86 |
-
cloudFileStore.load()
|
87 |
-
}, [])
|
88 |
-
|
89 |
-
const onClickSong = async (song: QueryDocumentSnapshot<FirestoreSong>) => {
|
90 |
-
try {
|
91 |
-
const midiSong = await loadSong(song)
|
92 |
-
setSong(rootStore)(midiSong)
|
93 |
-
rootViewStore.openCloudFileDialog = false
|
94 |
-
} catch (e) {
|
95 |
-
toast.error((e as Error).message)
|
96 |
-
}
|
97 |
-
}
|
98 |
-
if (isLoading) {
|
99 |
-
return <CircularProgress />
|
100 |
-
}
|
101 |
-
|
102 |
-
const sortLabel = (() => {
|
103 |
-
switch (dateType) {
|
104 |
-
case "created":
|
105 |
-
return localized("created-date", "Created")
|
106 |
-
case "updated":
|
107 |
-
return localized("modified-date", "Modified")
|
108 |
-
}
|
109 |
-
})()
|
110 |
-
|
111 |
-
return (
|
112 |
-
<Container>
|
113 |
-
<Header>
|
114 |
-
<NameCell
|
115 |
-
onClick={() => {
|
116 |
-
if (cloudFileStore.selectedColumn === "name") {
|
117 |
-
cloudFileStore.sortAscending = !cloudFileStore.sortAscending
|
118 |
-
} else {
|
119 |
-
cloudFileStore.selectedColumn = "name"
|
120 |
-
}
|
121 |
-
}}
|
122 |
-
isSelected={selectedColumn === "name"}
|
123 |
-
>
|
124 |
-
<Localized default="Name">name</Localized>
|
125 |
-
<div style={{ width: "0.5rem" }}></div>
|
126 |
-
{selectedColumn === "name" && (
|
127 |
-
<SortButton sortAscending={sortAscending} />
|
128 |
-
)}
|
129 |
-
</NameCell>
|
130 |
-
<DateCell
|
131 |
-
onClick={() => {
|
132 |
-
if (cloudFileStore.selectedColumn === "date") {
|
133 |
-
cloudFileStore.sortAscending = !cloudFileStore.sortAscending
|
134 |
-
} else {
|
135 |
-
cloudFileStore.selectedColumn = "date"
|
136 |
-
}
|
137 |
-
}}
|
138 |
-
isSelected={selectedColumn === "date"}
|
139 |
-
>
|
140 |
-
{sortLabel}
|
141 |
-
<Menu
|
142 |
-
trigger={
|
143 |
-
<IconButton
|
144 |
-
style={{
|
145 |
-
marginLeft: "0.2em",
|
146 |
-
}}
|
147 |
-
>
|
148 |
-
<ArrowDropDown style={{ fill: theme.secondaryTextColor }} />
|
149 |
-
</IconButton>
|
150 |
-
}
|
151 |
-
>
|
152 |
-
<MenuItem onClick={() => (cloudFileStore.dateType = "created")}>
|
153 |
-
<Localized default="Created">created-date</Localized>
|
154 |
-
</MenuItem>
|
155 |
-
<MenuItem onClick={() => (cloudFileStore.dateType = "updated")}>
|
156 |
-
<Localized default="Modified">modified-date</Localized>
|
157 |
-
</MenuItem>
|
158 |
-
</Menu>
|
159 |
-
{selectedColumn === "date" && (
|
160 |
-
<SortButton sortAscending={sortAscending} />
|
161 |
-
)}
|
162 |
-
</DateCell>
|
163 |
-
<MenuCell></MenuCell>
|
164 |
-
</Header>
|
165 |
-
<Body>
|
166 |
-
{files.map((song) => (
|
167 |
-
<CloudFileRow
|
168 |
-
song={song}
|
169 |
-
dateType={dateType}
|
170 |
-
onClick={() => onClickSong(song)}
|
171 |
-
/>
|
172 |
-
))}
|
173 |
-
</Body>
|
174 |
-
</Container>
|
175 |
-
)
|
176 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/CloudFileDialog/CloudFileRow.tsx
DELETED
@@ -1,120 +0,0 @@
|
|
1 |
-
import styled from "@emotion/styled"
|
2 |
-
import { QueryDocumentSnapshot } from "firebase/firestore"
|
3 |
-
import DotsHorizontalIcon from "mdi-react/DotsHorizontalIcon"
|
4 |
-
import { observer } from "mobx-react-lite"
|
5 |
-
import { FC } from "react"
|
6 |
-
import { IconButton } from "../../../components/IconButton"
|
7 |
-
import { Localized } from "../../../components/Localized"
|
8 |
-
import { Menu, MenuItem } from "../../../components/Menu"
|
9 |
-
import { FirestoreSong } from "../../../firebase/song"
|
10 |
-
import { useLocalization } from "../../hooks/useLocalization"
|
11 |
-
import { useStores } from "../../hooks/useStores"
|
12 |
-
import { useTheme } from "../../hooks/useTheme"
|
13 |
-
import { useToast } from "../../hooks/useToast"
|
14 |
-
|
15 |
-
const Container = styled.div`
|
16 |
-
display: flex;
|
17 |
-
cursor: pointer;
|
18 |
-
height: 2.5rem;
|
19 |
-
overflow: hidden;
|
20 |
-
|
21 |
-
&:hover {
|
22 |
-
background: ${({ theme }) => theme.secondaryBackgroundColor};
|
23 |
-
}
|
24 |
-
`
|
25 |
-
|
26 |
-
const Cell = styled.div`
|
27 |
-
display: flex;
|
28 |
-
align-items: center;
|
29 |
-
padding: 0 1rem;
|
30 |
-
box-sizing: border-box;
|
31 |
-
`
|
32 |
-
|
33 |
-
const NameCell = styled(Cell)`
|
34 |
-
overflow: hidden;
|
35 |
-
flex-grow: 1;
|
36 |
-
`
|
37 |
-
|
38 |
-
const DateCell = styled(Cell)`
|
39 |
-
width: 12rem;
|
40 |
-
flex-shrink: 0;
|
41 |
-
`
|
42 |
-
|
43 |
-
const MenuCell = styled(Cell)`
|
44 |
-
width: 4rem;
|
45 |
-
flex-shrink: 0;
|
46 |
-
`
|
47 |
-
|
48 |
-
const NoWrapText = styled.span`
|
49 |
-
white-space: nowrap;
|
50 |
-
text-overflow: ellipsis;
|
51 |
-
overflow: hidden;
|
52 |
-
`
|
53 |
-
|
54 |
-
export interface CloudFileRowProps {
|
55 |
-
onClick: () => void
|
56 |
-
song: QueryDocumentSnapshot<FirestoreSong>
|
57 |
-
dateType: "created" | "updated"
|
58 |
-
}
|
59 |
-
|
60 |
-
export const CloudFileRow: FC<CloudFileRowProps> = observer(
|
61 |
-
({ song, onClick, dateType }) => {
|
62 |
-
const theme = useTheme()
|
63 |
-
const toast = useToast()
|
64 |
-
const localized = useLocalization()
|
65 |
-
const { cloudFileStore } = useStores()
|
66 |
-
const date: Date = (() => {
|
67 |
-
switch (dateType) {
|
68 |
-
case "created":
|
69 |
-
return song.data().createdAt.toDate()
|
70 |
-
case "updated":
|
71 |
-
return song.data().updatedAt.toDate()
|
72 |
-
}
|
73 |
-
})()
|
74 |
-
const songName =
|
75 |
-
song.data().name.length > 0
|
76 |
-
? song.data().name
|
77 |
-
: localized("untitled-song", "Untitled song")
|
78 |
-
const dateStr = date.toLocaleDateString() + " " + date.toLocaleTimeString()
|
79 |
-
return (
|
80 |
-
<Container onClick={onClick}>
|
81 |
-
<NameCell>
|
82 |
-
<NoWrapText>{songName}</NoWrapText>
|
83 |
-
</NameCell>
|
84 |
-
<DateCell>{dateStr}</DateCell>
|
85 |
-
<MenuCell>
|
86 |
-
<Menu
|
87 |
-
trigger={
|
88 |
-
<IconButton
|
89 |
-
style={{
|
90 |
-
marginLeft: "0.2em",
|
91 |
-
}}
|
92 |
-
>
|
93 |
-
<DotsHorizontalIcon
|
94 |
-
style={{ fill: theme.secondaryTextColor }}
|
95 |
-
/>
|
96 |
-
</IconButton>
|
97 |
-
}
|
98 |
-
>
|
99 |
-
<MenuItem
|
100 |
-
onClick={async (e) => {
|
101 |
-
e.stopPropagation()
|
102 |
-
try {
|
103 |
-
await cloudFileStore.deleteSong(song)
|
104 |
-
toast.info(localized("song-deleted", "Song deleted"))
|
105 |
-
} catch (e) {
|
106 |
-
console.error(e)
|
107 |
-
toast.error(
|
108 |
-
localized("song-delete-failed", "Song delete failed"),
|
109 |
-
)
|
110 |
-
}
|
111 |
-
}}
|
112 |
-
>
|
113 |
-
<Localized default="Delete">delete</Localized>
|
114 |
-
</MenuItem>
|
115 |
-
</Menu>
|
116 |
-
</MenuCell>
|
117 |
-
</Container>
|
118 |
-
)
|
119 |
-
},
|
120 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/FirebaseAuth/StyledFirebaseAuth.tsx
DELETED
@@ -1,106 +0,0 @@
|
|
1 |
-
// Copy from https://github.com/firebase/firebaseui-web-react/pull/173#issuecomment-1151532176
|
2 |
-
|
3 |
-
import styled from "@emotion/styled"
|
4 |
-
import { onAuthStateChanged } from "firebase/auth"
|
5 |
-
import * as firebaseui from "firebaseui"
|
6 |
-
import { useEffect, useRef, useState } from "react"
|
7 |
-
|
8 |
-
interface Props {
|
9 |
-
// The Firebase UI Web UI Config object.
|
10 |
-
// See: https://github.com/firebase/firebaseui-web#configuration
|
11 |
-
uiConfig: firebaseui.auth.Config
|
12 |
-
// Callback that will be passed the FirebaseUi instance before it is
|
13 |
-
// started. This allows access to certain configuration options such as
|
14 |
-
// disableAutoSignIn().
|
15 |
-
uiCallback?(ui: firebaseui.auth.AuthUI): void
|
16 |
-
// The Firebase App auth instance to use.
|
17 |
-
firebaseAuth: any // As firebaseui-web
|
18 |
-
}
|
19 |
-
|
20 |
-
const Container = styled.div`
|
21 |
-
ul.firebaseui-idp-list {
|
22 |
-
list-style-type: none;
|
23 |
-
padding: 0;
|
24 |
-
}
|
25 |
-
|
26 |
-
button.firebaseui-idp-button {
|
27 |
-
display: flex;
|
28 |
-
align-items: center;
|
29 |
-
padding: 0.5rem 1rem;
|
30 |
-
border-radius: 0.5rem;
|
31 |
-
border: 1px solid ${({ theme }) => theme.dividerColor};
|
32 |
-
background: inherit !important;
|
33 |
-
color: inherit;
|
34 |
-
min-height: 3rem;
|
35 |
-
min-width: 12rem;
|
36 |
-
justify-content: center;
|
37 |
-
cursor: pointer;
|
38 |
-
|
39 |
-
&:hover {
|
40 |
-
background: ${({ theme }) => theme.highlightColor} !important;
|
41 |
-
}
|
42 |
-
}
|
43 |
-
|
44 |
-
img.firebaseui-idp-icon {
|
45 |
-
width: 1.5rem;
|
46 |
-
}
|
47 |
-
|
48 |
-
span.firebaseui-idp-icon-wrapper {
|
49 |
-
display: flex;
|
50 |
-
margin-right: 1rem;
|
51 |
-
}
|
52 |
-
|
53 |
-
span.firebaseui-idp-text.firebaseui-idp-text-short {
|
54 |
-
display: none;
|
55 |
-
}
|
56 |
-
|
57 |
-
li.firebaseui-list-item {
|
58 |
-
margin-bottom: 1rem;
|
59 |
-
display: flex;
|
60 |
-
justify-content: center;
|
61 |
-
}
|
62 |
-
`
|
63 |
-
|
64 |
-
export const StyledFirebaseAuth = ({
|
65 |
-
uiConfig,
|
66 |
-
firebaseAuth,
|
67 |
-
uiCallback,
|
68 |
-
}: Props) => {
|
69 |
-
const [userSignedIn, setUserSignedIn] = useState(false)
|
70 |
-
const elementRef = useRef(null)
|
71 |
-
|
72 |
-
useEffect(() => {
|
73 |
-
// Get or Create a firebaseUI instance.
|
74 |
-
const firebaseUiWidget =
|
75 |
-
firebaseui.auth.AuthUI.getInstance() ||
|
76 |
-
new firebaseui.auth.AuthUI(firebaseAuth)
|
77 |
-
|
78 |
-
if (uiConfig.signInFlow === "popup") {
|
79 |
-
firebaseUiWidget.reset()
|
80 |
-
}
|
81 |
-
|
82 |
-
// We track the auth state to reset firebaseUi if the user signs out.
|
83 |
-
const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, (user) => {
|
84 |
-
if (!user && userSignedIn) {
|
85 |
-
firebaseUiWidget.reset()
|
86 |
-
}
|
87 |
-
setUserSignedIn(!!user)
|
88 |
-
})
|
89 |
-
|
90 |
-
// Trigger the callback if any was set.
|
91 |
-
if (uiCallback) {
|
92 |
-
uiCallback(firebaseUiWidget)
|
93 |
-
}
|
94 |
-
|
95 |
-
// Render the firebaseUi Widget.
|
96 |
-
// @ts-ignore
|
97 |
-
firebaseUiWidget.start(elementRef.current, uiConfig)
|
98 |
-
|
99 |
-
return () => {
|
100 |
-
unregisterAuthObserver()
|
101 |
-
firebaseUiWidget.reset()
|
102 |
-
}
|
103 |
-
}, [firebaseui, uiConfig])
|
104 |
-
|
105 |
-
return <Container ref={elementRef} />
|
106 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/Navigation/CloudFileMenu.tsx
DELETED
@@ -1,244 +0,0 @@
|
|
1 |
-
import { observer } from "mobx-react-lite"
|
2 |
-
import { ChangeEvent, FC } from "react"
|
3 |
-
import { emptySong } from "../../../common/song"
|
4 |
-
import { Localized } from "../../../components/Localized"
|
5 |
-
import { MenuDivider, MenuItem } from "../../../components/Menu"
|
6 |
-
import { createSong, updateSong } from "../../../firebase/song"
|
7 |
-
import { openSong, saveSong, setSong } from "../../actions"
|
8 |
-
import { hasFSAccess, openFile, saveFileAs } from "../../actions/file"
|
9 |
-
import { useDialog } from "../../hooks/useDialog"
|
10 |
-
import { useLocalization } from "../../hooks/useLocalization"
|
11 |
-
import { usePrompt } from "../../hooks/usePrompt"
|
12 |
-
import { useStores } from "../../hooks/useStores"
|
13 |
-
import { useToast } from "../../hooks/useToast"
|
14 |
-
import { FileInput } from "./LegacyFileMenu"
|
15 |
-
|
16 |
-
export const CloudFileMenu: FC<{ close: () => void }> = observer(
|
17 |
-
({ close }) => {
|
18 |
-
const rootStore = useStores()
|
19 |
-
const { rootViewStore } = rootStore
|
20 |
-
const toast = useToast()
|
21 |
-
const prompt = usePrompt()
|
22 |
-
const dialog = useDialog()
|
23 |
-
const localized = useLocalization()
|
24 |
-
|
25 |
-
const saveOrCreateSong = async () => {
|
26 |
-
const { song } = rootStore
|
27 |
-
if (song.firestoreReference !== null) {
|
28 |
-
if (song.name.length === 0) {
|
29 |
-
const text = await prompt.show({
|
30 |
-
title: localized("save-as", "Save as"),
|
31 |
-
})
|
32 |
-
if (text !== null && text.length > 0) {
|
33 |
-
song.name = text
|
34 |
-
}
|
35 |
-
}
|
36 |
-
await updateSong(song)
|
37 |
-
toast.success(localized("song-saved", "Song saved"))
|
38 |
-
} else {
|
39 |
-
if (song.name.length === 0) {
|
40 |
-
const text = await prompt.show({
|
41 |
-
title: localized("save-as", "Save as"),
|
42 |
-
})
|
43 |
-
if (text !== null && text.length > 0) {
|
44 |
-
song.name = text
|
45 |
-
}
|
46 |
-
}
|
47 |
-
await createSong(song)
|
48 |
-
toast.success(localized("song-created", "Song created"))
|
49 |
-
}
|
50 |
-
}
|
51 |
-
|
52 |
-
// true: saved or not necessary
|
53 |
-
// false: canceled
|
54 |
-
const saveIfNeeded = async (): Promise<boolean> => {
|
55 |
-
const { song } = rootStore
|
56 |
-
if (song.isSaved) {
|
57 |
-
return true
|
58 |
-
}
|
59 |
-
|
60 |
-
const res = await dialog.show({
|
61 |
-
title: localized(
|
62 |
-
"save-changes",
|
63 |
-
"Do you want to save your changes to the song?",
|
64 |
-
),
|
65 |
-
actions: [
|
66 |
-
{ title: localized("yes", "Yes"), key: "yes" },
|
67 |
-
{ title: localized("no", "No"), key: "no" },
|
68 |
-
{ title: localized("cancel", "Cancel"), key: "cancel" },
|
69 |
-
],
|
70 |
-
})
|
71 |
-
switch (res) {
|
72 |
-
case "yes":
|
73 |
-
await saveOrCreateSong()
|
74 |
-
return true
|
75 |
-
case "no":
|
76 |
-
return true
|
77 |
-
case "cancel":
|
78 |
-
return false
|
79 |
-
}
|
80 |
-
}
|
81 |
-
|
82 |
-
const onClickNew = async () => {
|
83 |
-
close()
|
84 |
-
try {
|
85 |
-
if (!(await saveIfNeeded())) {
|
86 |
-
return
|
87 |
-
}
|
88 |
-
const newSong = emptySong()
|
89 |
-
setSong(rootStore)(newSong)
|
90 |
-
await createSong(newSong)
|
91 |
-
toast.success(localized("song-created", "Song created"))
|
92 |
-
} catch (e) {
|
93 |
-
toast.error((e as Error).message)
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
const onClickOpen = async () => {
|
98 |
-
close()
|
99 |
-
try {
|
100 |
-
if (!(await saveIfNeeded())) {
|
101 |
-
return
|
102 |
-
}
|
103 |
-
rootViewStore.openCloudFileDialog = true
|
104 |
-
} catch (e) {
|
105 |
-
toast.error((e as Error).message)
|
106 |
-
}
|
107 |
-
}
|
108 |
-
|
109 |
-
const onClickSave = async () => {
|
110 |
-
close()
|
111 |
-
try {
|
112 |
-
await saveOrCreateSong()
|
113 |
-
} catch (e) {
|
114 |
-
toast.error((e as Error).message)
|
115 |
-
}
|
116 |
-
}
|
117 |
-
|
118 |
-
const onClickSaveAs = async () => {
|
119 |
-
const { song } = rootStore
|
120 |
-
close()
|
121 |
-
try {
|
122 |
-
const text = await prompt.show({
|
123 |
-
title: localized("save-as", "Save as"),
|
124 |
-
initialText: song.name,
|
125 |
-
})
|
126 |
-
if (text !== null && text.length > 0) {
|
127 |
-
song.name = text
|
128 |
-
} else {
|
129 |
-
return
|
130 |
-
}
|
131 |
-
await createSong(song)
|
132 |
-
toast.success(localized("song-saved", "Song saved"))
|
133 |
-
} catch (e) {
|
134 |
-
toast.error((e as Error).message)
|
135 |
-
}
|
136 |
-
}
|
137 |
-
|
138 |
-
const onClickRename = async () => {
|
139 |
-
close()
|
140 |
-
const { song } = rootStore
|
141 |
-
try {
|
142 |
-
if (song.name.length === 0) {
|
143 |
-
const text = await prompt.show({
|
144 |
-
title: localized("rename", "Rename"),
|
145 |
-
})
|
146 |
-
if (text !== null && text.length > 0) {
|
147 |
-
song.name = text
|
148 |
-
} else {
|
149 |
-
return Promise.resolve(false)
|
150 |
-
}
|
151 |
-
}
|
152 |
-
if (song.firestoreReference !== null) {
|
153 |
-
await updateSong(song)
|
154 |
-
} else {
|
155 |
-
await createSong(song)
|
156 |
-
}
|
157 |
-
toast.success(localized("song-saved", "Song saved"))
|
158 |
-
} catch (e) {
|
159 |
-
toast.error((e as Error).message)
|
160 |
-
}
|
161 |
-
}
|
162 |
-
|
163 |
-
const onClickImportLegacy = async (e: ChangeEvent<HTMLInputElement>) => {
|
164 |
-
close()
|
165 |
-
try {
|
166 |
-
await openSong(rootStore)(e.currentTarget)
|
167 |
-
await saveOrCreateSong()
|
168 |
-
} catch (e) {
|
169 |
-
toast.error((e as Error).message)
|
170 |
-
}
|
171 |
-
}
|
172 |
-
|
173 |
-
const onClickImport = async () => {
|
174 |
-
close()
|
175 |
-
try {
|
176 |
-
if (!(await saveIfNeeded())) {
|
177 |
-
return
|
178 |
-
}
|
179 |
-
await openFile(rootStore)
|
180 |
-
await saveOrCreateSong()
|
181 |
-
} catch (e) {
|
182 |
-
toast.error((e as Error).message)
|
183 |
-
}
|
184 |
-
}
|
185 |
-
|
186 |
-
const onClickExport = async () => {
|
187 |
-
try {
|
188 |
-
if (hasFSAccess) {
|
189 |
-
await saveFileAs(rootStore)
|
190 |
-
} else {
|
191 |
-
saveSong(rootStore)()
|
192 |
-
}
|
193 |
-
} catch (e) {
|
194 |
-
toast.error((e as Error).message)
|
195 |
-
}
|
196 |
-
}
|
197 |
-
|
198 |
-
return (
|
199 |
-
<>
|
200 |
-
<MenuItem onClick={onClickNew}>
|
201 |
-
<Localized default="New">new-song</Localized>
|
202 |
-
</MenuItem>
|
203 |
-
|
204 |
-
<MenuDivider />
|
205 |
-
|
206 |
-
<MenuItem onClick={onClickOpen}>
|
207 |
-
<Localized default="Open">open-song</Localized>
|
208 |
-
</MenuItem>
|
209 |
-
|
210 |
-
<MenuItem onClick={onClickSave} disabled={rootStore.song.isSaved}>
|
211 |
-
<Localized default="Save">save-song</Localized>
|
212 |
-
</MenuItem>
|
213 |
-
|
214 |
-
<MenuItem onClick={onClickSaveAs}>
|
215 |
-
<Localized default="Save As">save-as</Localized>
|
216 |
-
</MenuItem>
|
217 |
-
|
218 |
-
<MenuItem onClick={onClickRename}>
|
219 |
-
<Localized default="Rename">rename</Localized>
|
220 |
-
</MenuItem>
|
221 |
-
|
222 |
-
<MenuDivider />
|
223 |
-
|
224 |
-
{!hasFSAccess && (
|
225 |
-
<FileInput onChange={onClickImportLegacy}>
|
226 |
-
<MenuItem>
|
227 |
-
<Localized default="Import MIDI file">import-midi</Localized>
|
228 |
-
</MenuItem>
|
229 |
-
</FileInput>
|
230 |
-
)}
|
231 |
-
|
232 |
-
{hasFSAccess && (
|
233 |
-
<MenuItem onClick={onClickImport}>
|
234 |
-
<Localized default="Import MIDI file">import-midi</Localized>
|
235 |
-
</MenuItem>
|
236 |
-
)}
|
237 |
-
|
238 |
-
<MenuItem onClick={onClickExport}>
|
239 |
-
<Localized default="Export MIDI file">export-midi</Localized>
|
240 |
-
</MenuItem>
|
241 |
-
</>
|
242 |
-
)
|
243 |
-
},
|
244 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/Navigation/FileMenuButton.tsx
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
import CloudOutlined from "mdi-react/CloudOutlineIcon"
|
2 |
import KeyboardArrowDown from "mdi-react/KeyboardArrowDownIcon"
|
3 |
import { observer } from "mobx-react-lite"
|
4 |
import { FC, useCallback, useRef } from "react"
|
@@ -6,7 +5,6 @@ import { Localized } from "../../../components/Localized"
|
|
6 |
import { Menu, MenuDivider, MenuItem } from "../../../components/Menu"
|
7 |
import { hasFSAccess } from "../../actions/file"
|
8 |
import { useStores } from "../../hooks/useStores"
|
9 |
-
import { CloudFileMenu } from "./CloudFileMenu"
|
10 |
import { FileMenu } from "./FileMenu"
|
11 |
import { LegacyFileMenu } from "./LegacyFileMenu"
|
12 |
import { Tab } from "./Navigation"
|
@@ -15,8 +13,7 @@ export const FileMenuButton: FC = observer(() => {
|
|
15 |
const rootStore = useStores()
|
16 |
const {
|
17 |
rootViewStore,
|
18 |
-
exportStore
|
19 |
-
authStore: { user },
|
20 |
} = rootStore
|
21 |
const isOpen = rootViewStore.openDrawer
|
22 |
const handleClose = () => (rootViewStore.openDrawer = false)
|
@@ -45,9 +42,9 @@ export const FileMenuButton: FC = observer(() => {
|
|
45 |
</Tab>
|
46 |
}
|
47 |
>
|
48 |
-
{
|
49 |
|
50 |
-
{
|
51 |
|
52 |
<MenuDivider />
|
53 |
|
|
|
|
|
1 |
import KeyboardArrowDown from "mdi-react/KeyboardArrowDownIcon"
|
2 |
import { observer } from "mobx-react-lite"
|
3 |
import { FC, useCallback, useRef } from "react"
|
|
|
5 |
import { Menu, MenuDivider, MenuItem } from "../../../components/Menu"
|
6 |
import { hasFSAccess } from "../../actions/file"
|
7 |
import { useStores } from "../../hooks/useStores"
|
|
|
8 |
import { FileMenu } from "./FileMenu"
|
9 |
import { LegacyFileMenu } from "./LegacyFileMenu"
|
10 |
import { Tab } from "./Navigation"
|
|
|
13 |
const rootStore = useStores()
|
14 |
const {
|
15 |
rootViewStore,
|
16 |
+
exportStore
|
|
|
17 |
} = rootStore
|
18 |
const isOpen = rootViewStore.openDrawer
|
19 |
const handleClose = () => (rootViewStore.openDrawer = false)
|
|
|
42 |
</Tab>
|
43 |
}
|
44 |
>
|
45 |
+
{hasFSAccess && <FileMenu close={handleClose} />}
|
46 |
|
47 |
+
{!hasFSAccess && <LegacyFileMenu close={handleClose} />}
|
48 |
|
49 |
<MenuDivider />
|
50 |
|
src/main/components/Navigation/Navigation.tsx
CHANGED
@@ -89,7 +89,6 @@ export const IconStyle: CSSProperties = {
|
|
89 |
export const Navigation: FC = observer(() => {
|
90 |
const {
|
91 |
rootViewStore,
|
92 |
-
authStore: { user },
|
93 |
router,
|
94 |
} = useStores()
|
95 |
|
|
|
89 |
export const Navigation: FC = observer(() => {
|
90 |
const {
|
91 |
rootViewStore,
|
|
|
92 |
router,
|
93 |
} = useStores()
|
94 |
|
src/main/components/Navigation/UserButton.tsx
CHANGED
@@ -1,22 +1,12 @@
|
|
1 |
import { observer } from "mobx-react-lite"
|
2 |
import { FC } from "react"
|
3 |
-
import { auth } from "../../../firebase/firebase"
|
4 |
import { useStores } from "../../hooks/useStores"
|
5 |
import { UserButtonContent } from "./UserButtonContent"
|
6 |
|
7 |
export const UserButton: FC = observer(() => {
|
8 |
const {
|
9 |
rootViewStore,
|
10 |
-
authStore: { user },
|
11 |
} = useStores()
|
12 |
|
13 |
-
return (
|
14 |
-
<UserButtonContent
|
15 |
-
user={user}
|
16 |
-
onClickSignIn={() => (rootViewStore.openSignInDialog = true)}
|
17 |
-
onClickSignOut={async () => {
|
18 |
-
await auth.signOut()
|
19 |
-
}}
|
20 |
-
/>
|
21 |
-
)
|
22 |
})
|
|
|
1 |
import { observer } from "mobx-react-lite"
|
2 |
import { FC } from "react"
|
|
|
3 |
import { useStores } from "../../hooks/useStores"
|
4 |
import { UserButtonContent } from "./UserButtonContent"
|
5 |
|
6 |
export const UserButton: FC = observer(() => {
|
7 |
const {
|
8 |
rootViewStore,
|
|
|
9 |
} = useStores()
|
10 |
|
11 |
+
return (null)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
})
|
src/main/components/RootView/RootView.tsx
CHANGED
@@ -4,7 +4,6 @@ import { FC } from "react"
|
|
4 |
import { useStores } from "../../hooks/useStores"
|
5 |
import { ArrangeEditor } from "../ArrangeView/ArrangeEditor"
|
6 |
import { BuildInfo } from "../BuildInfo"
|
7 |
-
import { CloudFileDialog } from "../CloudFileDialog/CloudFileDialog"
|
8 |
import { ControlSettingDialog } from "../ControlSettingDialog/ControlSettingDialog"
|
9 |
import { ExportDialog } from "../ExportDialog/ExportDialog"
|
10 |
import { ExportProgressDialog } from "../ExportDialog/ExportProgressDialog"
|
@@ -14,7 +13,6 @@ import { Navigation } from "../Navigation/Navigation"
|
|
14 |
import { OnBeforeUnload } from "../OnBeforeUnload/OnBeforeUnload"
|
15 |
import { PianoRollEditor } from "../PianoRoll/PianoRollEditor"
|
16 |
import { SettingDialog } from "../SettingDialog/SettingDialog"
|
17 |
-
import { SignInDialog } from "../SignInDialog/SignInDialog"
|
18 |
import { TempoEditor } from "../TempoGraph/TempoEditor"
|
19 |
import { TransportPanel } from "../TransportPanel/TransportPanel"
|
20 |
import { ArrangeTransposeDialog } from "../TransposeDialog/ArrangeTransposeDialog"
|
|
|
4 |
import { useStores } from "../../hooks/useStores"
|
5 |
import { ArrangeEditor } from "../ArrangeView/ArrangeEditor"
|
6 |
import { BuildInfo } from "../BuildInfo"
|
|
|
7 |
import { ControlSettingDialog } from "../ControlSettingDialog/ControlSettingDialog"
|
8 |
import { ExportDialog } from "../ExportDialog/ExportDialog"
|
9 |
import { ExportProgressDialog } from "../ExportDialog/ExportProgressDialog"
|
|
|
13 |
import { OnBeforeUnload } from "../OnBeforeUnload/OnBeforeUnload"
|
14 |
import { PianoRollEditor } from "../PianoRoll/PianoRollEditor"
|
15 |
import { SettingDialog } from "../SettingDialog/SettingDialog"
|
|
|
16 |
import { TempoEditor } from "../TempoGraph/TempoEditor"
|
17 |
import { TransportPanel } from "../TransportPanel/TransportPanel"
|
18 |
import { ArrangeTransposeDialog } from "../TransposeDialog/ArrangeTransposeDialog"
|
src/main/components/SignInDialog/SignInDialog.tsx
DELETED
@@ -1,39 +0,0 @@
|
|
1 |
-
import { observer } from "mobx-react-lite"
|
2 |
-
import { FC, useCallback } from "react"
|
3 |
-
import { useLocalization } from "../../hooks/useLocalization"
|
4 |
-
import { useStores } from "../../hooks/useStores"
|
5 |
-
import { useToast } from "../../hooks/useToast"
|
6 |
-
import { SignInDialogContent } from "./SignInDialogContent"
|
7 |
-
|
8 |
-
export const SignInDialog: FC = observer(() => {
|
9 |
-
const rootStore = useStores()
|
10 |
-
const {
|
11 |
-
rootViewStore,
|
12 |
-
rootViewStore: { openSignInDialog },
|
13 |
-
} = rootStore
|
14 |
-
const toast = useToast()
|
15 |
-
const localized = useLocalization()
|
16 |
-
|
17 |
-
const onClose = useCallback(
|
18 |
-
() => (rootViewStore.openSignInDialog = false),
|
19 |
-
[rootViewStore],
|
20 |
-
)
|
21 |
-
|
22 |
-
const signInSuccessWithAuthResult = () => {
|
23 |
-
rootViewStore.openSignInDialog = false
|
24 |
-
toast.success(localized("success-sign-in", "Successfully signed in"))
|
25 |
-
}
|
26 |
-
|
27 |
-
const signInFailure = (error: firebaseui.auth.AuthUIError) => {
|
28 |
-
console.warn(error)
|
29 |
-
}
|
30 |
-
|
31 |
-
return (
|
32 |
-
<SignInDialogContent
|
33 |
-
open={openSignInDialog}
|
34 |
-
onClose={onClose}
|
35 |
-
onSuccess={signInSuccessWithAuthResult}
|
36 |
-
onFailure={signInFailure}
|
37 |
-
/>
|
38 |
-
)
|
39 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/components/SignInDialog/SignInDialogContent.tsx
DELETED
@@ -1,86 +0,0 @@
|
|
1 |
-
import { FC } from "react"
|
2 |
-
import {
|
3 |
-
Dialog,
|
4 |
-
DialogActions,
|
5 |
-
DialogContent,
|
6 |
-
DialogTitle,
|
7 |
-
} from "../../../components/Dialog"
|
8 |
-
import { StyledFirebaseAuth } from "../FirebaseAuth/StyledFirebaseAuth"
|
9 |
-
|
10 |
-
import styled from "@emotion/styled"
|
11 |
-
import "firebase/auth"
|
12 |
-
import { GithubAuthProvider, GoogleAuthProvider } from "firebase/auth"
|
13 |
-
import { Alert } from "../../../components/Alert"
|
14 |
-
import { Button } from "../../../components/Button"
|
15 |
-
import { Localized } from "../../../components/Localized"
|
16 |
-
import { auth } from "../../../firebase/firebase"
|
17 |
-
|
18 |
-
const BetaLabel = styled.span`
|
19 |
-
border: 1px solid currentColor;
|
20 |
-
font-size: 0.8rem;
|
21 |
-
padding: 0.1rem 0.4rem;
|
22 |
-
margin-left: 1em;
|
23 |
-
color: ${({ theme }) => theme.secondaryTextColor};
|
24 |
-
`
|
25 |
-
|
26 |
-
const Description = styled.div`
|
27 |
-
margin: 1rem 0 2rem 0;
|
28 |
-
line-height: 1.5;
|
29 |
-
`
|
30 |
-
|
31 |
-
export interface SignInDialogContentProps {
|
32 |
-
open: boolean
|
33 |
-
onClose: () => void
|
34 |
-
onSuccess: () => void
|
35 |
-
onFailure: (error: firebaseui.auth.AuthUIError) => void
|
36 |
-
}
|
37 |
-
|
38 |
-
export const SignInDialogContent: FC<SignInDialogContentProps> = ({
|
39 |
-
open,
|
40 |
-
onClose,
|
41 |
-
onSuccess,
|
42 |
-
onFailure,
|
43 |
-
}) => {
|
44 |
-
return (
|
45 |
-
<Dialog open={open} onOpenChange={onClose} style={{ minWidth: "20rem" }}>
|
46 |
-
<DialogTitle>
|
47 |
-
<Localized default="Sign in">sign-in</Localized>
|
48 |
-
<BetaLabel>Beta</BetaLabel>
|
49 |
-
</DialogTitle>
|
50 |
-
<DialogContent>
|
51 |
-
<Alert severity="info">
|
52 |
-
<Localized default="Since the cloud function is in beta during development, please download and save your important songs frequently.">
|
53 |
-
cloud-beta-warning
|
54 |
-
</Localized>
|
55 |
-
</Alert>
|
56 |
-
<Description>
|
57 |
-
<Localized default="By signing in, you can save your music to the cloud and resume composing from anywhere at any time.">
|
58 |
-
cloud-description
|
59 |
-
</Localized>
|
60 |
-
</Description>
|
61 |
-
<StyledFirebaseAuth
|
62 |
-
uiConfig={{
|
63 |
-
signInOptions: [
|
64 |
-
GoogleAuthProvider.PROVIDER_ID,
|
65 |
-
GithubAuthProvider.PROVIDER_ID,
|
66 |
-
],
|
67 |
-
callbacks: {
|
68 |
-
signInSuccessWithAuthResult() {
|
69 |
-
onSuccess()
|
70 |
-
return false
|
71 |
-
},
|
72 |
-
signInFailure: onFailure,
|
73 |
-
},
|
74 |
-
signInFlow: "popup",
|
75 |
-
}}
|
76 |
-
firebaseAuth={auth}
|
77 |
-
/>
|
78 |
-
</DialogContent>
|
79 |
-
<DialogActions>
|
80 |
-
<Button onClick={onClose}>
|
81 |
-
<Localized default="Close">close</Localized>
|
82 |
-
</Button>
|
83 |
-
</DialogActions>
|
84 |
-
</Dialog>
|
85 |
-
)
|
86 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/stores/AuthStore.ts
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
import { User } from "firebase/auth"
|
2 |
-
import { makeObservable, observable } from "mobx"
|
3 |
-
import { auth } from "../../firebase/firebase"
|
4 |
-
|
5 |
-
export class AuthStore {
|
6 |
-
user: User | null = null
|
7 |
-
|
8 |
-
constructor() {
|
9 |
-
makeObservable(this, {
|
10 |
-
user: observable,
|
11 |
-
})
|
12 |
-
|
13 |
-
auth.onAuthStateChanged((user) => {
|
14 |
-
this.user = user
|
15 |
-
})
|
16 |
-
}
|
17 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/stores/CloudFileStore.ts
DELETED
@@ -1,65 +0,0 @@
|
|
1 |
-
import { QueryDocumentSnapshot } from "@firebase/firestore"
|
2 |
-
import { orderBy } from "lodash"
|
3 |
-
import { computed, makeObservable, observable } from "mobx"
|
4 |
-
import {
|
5 |
-
FirestoreSong,
|
6 |
-
deleteSong,
|
7 |
-
getCurrentUserSongs,
|
8 |
-
} from "../../firebase/song"
|
9 |
-
import RootStore from "./RootStore"
|
10 |
-
|
11 |
-
export class CloudFileStore {
|
12 |
-
isLoading = false
|
13 |
-
selectedColumn: "name" | "date" = "date"
|
14 |
-
dateType: "created" | "updated" = "created"
|
15 |
-
sortAscending = false
|
16 |
-
_files: QueryDocumentSnapshot<FirestoreSong>[] = []
|
17 |
-
|
18 |
-
constructor(private readonly rootStore: RootStore) {
|
19 |
-
makeObservable(this, {
|
20 |
-
isLoading: observable,
|
21 |
-
selectedColumn: observable,
|
22 |
-
dateType: observable,
|
23 |
-
sortAscending: observable,
|
24 |
-
_files: observable,
|
25 |
-
files: computed,
|
26 |
-
})
|
27 |
-
}
|
28 |
-
|
29 |
-
async load() {
|
30 |
-
this.isLoading = true
|
31 |
-
const snapshot = await getCurrentUserSongs()
|
32 |
-
this._files = snapshot.docs
|
33 |
-
this.isLoading = false
|
34 |
-
}
|
35 |
-
|
36 |
-
get files() {
|
37 |
-
return orderBy(
|
38 |
-
this._files,
|
39 |
-
(f) => {
|
40 |
-
const data = f.data()
|
41 |
-
switch (this.selectedColumn) {
|
42 |
-
case "name":
|
43 |
-
return data.name
|
44 |
-
case "date":
|
45 |
-
switch (this.dateType) {
|
46 |
-
case "created":
|
47 |
-
return data.createdAt.seconds
|
48 |
-
case "updated":
|
49 |
-
return data.updatedAt.seconds
|
50 |
-
}
|
51 |
-
}
|
52 |
-
},
|
53 |
-
this.sortAscending ? "asc" : "desc",
|
54 |
-
)
|
55 |
-
}
|
56 |
-
|
57 |
-
async deleteSong(song: QueryDocumentSnapshot<FirestoreSong>) {
|
58 |
-
await deleteSong(song)
|
59 |
-
if (this.rootStore.song.firestoreReference?.id === song.id) {
|
60 |
-
this.rootStore.song.firestoreReference = null
|
61 |
-
this.rootStore.song.firestoreDataReference = null
|
62 |
-
}
|
63 |
-
await this.load()
|
64 |
-
}
|
65 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main/stores/RootStore.ts
CHANGED
@@ -8,8 +8,6 @@ import { MIDIInput, previewMidiInput } from "../services/MIDIInput"
|
|
8 |
import { MIDIRecorder } from "../services/MIDIRecorder"
|
9 |
import { SoundFontSynth } from "../services/SoundFontSynth"
|
10 |
import ArrangeViewStore from "./ArrangeViewStore"
|
11 |
-
import { AuthStore } from "./AuthStore"
|
12 |
-
import { CloudFileStore } from "./CloudFileStore"
|
13 |
import { ControlStore } from "./ControlStore"
|
14 |
import { ExportStore } from "./ExportStore"
|
15 |
import HistoryStore from "./HistoryStore"
|
@@ -33,8 +31,6 @@ export default class RootStore {
|
|
33 |
readonly tempoEditorStore = new TempoEditorStore(this)
|
34 |
readonly midiDeviceStore = new MIDIDeviceStore()
|
35 |
readonly exportStore = new ExportStore()
|
36 |
-
readonly authStore = new AuthStore()
|
37 |
-
readonly cloudFileStore = new CloudFileStore(this)
|
38 |
readonly settingStore = new SettingStore()
|
39 |
readonly player: Player
|
40 |
readonly synth: SoundFontSynth
|
|
|
8 |
import { MIDIRecorder } from "../services/MIDIRecorder"
|
9 |
import { SoundFontSynth } from "../services/SoundFontSynth"
|
10 |
import ArrangeViewStore from "./ArrangeViewStore"
|
|
|
|
|
11 |
import { ControlStore } from "./ControlStore"
|
12 |
import { ExportStore } from "./ExportStore"
|
13 |
import HistoryStore from "./HistoryStore"
|
|
|
31 |
readonly tempoEditorStore = new TempoEditorStore(this)
|
32 |
readonly midiDeviceStore = new MIDIDeviceStore()
|
33 |
readonly exportStore = new ExportStore()
|
|
|
|
|
34 |
readonly settingStore = new SettingStore()
|
35 |
readonly player: Player
|
36 |
readonly synth: SoundFontSynth
|