Change: リアクションデッキのTS化 (WIP)
This commit is contained in:
parent
7c65b6f9df
commit
0c27b62a25
6 changed files with 138 additions and 44 deletions
|
@ -589,7 +589,7 @@ class Status extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
data-id={status.get('id')}
|
data-id={status.get('id')}
|
||||||
>
|
>
|
||||||
{(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
|
{(connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
|
||||||
|
|
||||||
<div onClick={this.handleHeaderClick} onAuxClick={this.handleHeaderClick} className='status__info'>
|
<div onClick={this.handleHeaderClick} onAuxClick={this.handleHeaderClick} className='status__info'>
|
||||||
<Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time'>
|
<Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time'>
|
||||||
|
@ -608,7 +608,6 @@ class Status extends ImmutablePureComponent {
|
||||||
<DisplayName account={status.get('account')} />
|
<DisplayName account={status.get('account')} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
|
{matchedFilters && <FilterWarning title={matchedFilters.join(', ')} expanded={this.state.showDespiteFilter} onClick={this.handleFilterToggle} />}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,10 @@ export const ActionBar: React.FC = () => {
|
||||||
text: intl.formatMessage(messages.emoji_reactions),
|
text: intl.formatMessage(messages.emoji_reactions),
|
||||||
to: '/emoji_reactions',
|
to: '/emoji_reactions',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.reaction_deck),
|
||||||
|
to: '/reaction_deck',
|
||||||
|
},
|
||||||
{ text: intl.formatMessage(messages.lists), to: '/lists' },
|
{ text: intl.formatMessage(messages.lists), to: '/lists' },
|
||||||
{
|
{
|
||||||
text: intl.formatMessage(messages.followed_tags),
|
text: intl.formatMessage(messages.followed_tags),
|
||||||
|
|
|
@ -10,10 +10,10 @@ import { navigateToStatus } from 'mastodon/actions/statuses';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { AvatarGroup } from 'mastodon/components/avatar_group';
|
import { AvatarGroup } from 'mastodon/components/avatar_group';
|
||||||
import EmojiView from 'mastodon/components/emoji_view';
|
import EmojiView from 'mastodon/components/emoji_view';
|
||||||
import type { EmojiReactionGroup } from 'mastodon/models/notification_group';
|
|
||||||
import type { IconProp } from 'mastodon/components/icon';
|
import type { IconProp } from 'mastodon/components/icon';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
|
import type { EmojiReactionGroup } from 'mastodon/models/notification_group';
|
||||||
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
import { NOTIFICATIONS_GROUP_MAX_AVATARS } from 'mastodon/models/notification_group';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,36 @@
|
||||||
@typescript-eslint/no-unsafe-assignment */
|
@typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
DragStartEvent,
|
||||||
|
DragEndEvent,
|
||||||
|
UniqueIdentifier,
|
||||||
|
// Announcements,
|
||||||
|
// ScreenReaderInstructions,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
closestCenter,
|
||||||
|
KeyboardSensor,
|
||||||
|
PointerSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
DragOverlay,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
|
import {
|
||||||
|
SortableContext,
|
||||||
|
sortableKeyboardCoordinates,
|
||||||
|
rectSortingStrategy,
|
||||||
|
useSortable,
|
||||||
|
} from '@dnd-kit/sortable';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
|
||||||
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
import MenuIcon from '@/material-icons/400-24px/menu.svg?react';
|
||||||
import EmojiReactionIcon from '@/material-icons/400-24px/mood.svg?react';
|
import EmojiReactionIcon from '@/material-icons/400-24px/mood.svg?react';
|
||||||
import { updateReactionDeck } from 'mastodon/actions/reaction_deck';
|
import { updateReactionDeck } from 'mastodon/actions/reaction_deck';
|
||||||
|
@ -37,14 +61,25 @@ const ReactionEmoji: React.FC<{
|
||||||
onChange: (index: number, emoji: any) => void;
|
onChange: (index: number, emoji: any) => void;
|
||||||
onRemove: (index: number) => void;
|
onRemove: (index: number) => void;
|
||||||
}> = ({ index, emoji, emojiMap, onChange, onRemove }) => {
|
}> = ({ index, emoji, emojiMap, onChange, onRemove }) => {
|
||||||
const handleChange = useCallback((emoji: any) => {
|
const handleChange = useCallback(
|
||||||
|
(emoji: any) => {
|
||||||
onChange(index, emoji);
|
onChange(index, emoji);
|
||||||
}, [index, onChange]);
|
},
|
||||||
|
[index, onChange],
|
||||||
|
);
|
||||||
|
|
||||||
const handleRemove = useCallback(() => {
|
const handleRemove = useCallback(() => {
|
||||||
onRemove(index);
|
onRemove(index);
|
||||||
}, [index, onRemove]);
|
}, [index, onRemove]);
|
||||||
|
|
||||||
|
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||||
|
useSortable({ id: index.toString() });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
|
};
|
||||||
|
|
||||||
let content: ReactNode;
|
let content: ReactNode;
|
||||||
const mapEmoji = emojiMap.find((e: any) => e.get('shortcode') === emoji);
|
const mapEmoji = emojiMap.find((e: any) => e.get('shortcode') === emoji);
|
||||||
|
|
||||||
|
@ -69,19 +104,28 @@ const ReactionEmoji: React.FC<{
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div
|
||||||
|
className='reaction_deck_container__row'
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<Icon id='bars' icon={MenuIcon} className='handle' />
|
||||||
|
</span>
|
||||||
<div className='reaction_deck__emoji'>
|
<div className='reaction_deck__emoji'>
|
||||||
<div className='reaction_deck__emoji__wrapper'>
|
<div className='reaction_deck__emoji__wrapper'>
|
||||||
<div className='reaction_deck__emoji__wrapper__content'>
|
<div className='reaction_deck__emoji__wrapper__content'>
|
||||||
<EmojiPickerDropdown onPickEmoji={handleChange} />
|
<EmojiPickerDropdown onPickEmoji={handleChange} />
|
||||||
<div>
|
<div>{content}</div>
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='reaction_deck__emoji__wrapper__options'>
|
<div className='reaction_deck__emoji__wrapper__options'>
|
||||||
<Button secondary text={'Remove'} onClick={handleRemove} />
|
<Button secondary text={'Remove'} onClick={handleRemove} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -119,7 +163,7 @@ export const ReactionDeck: React.FC<{
|
||||||
newDeck[index] = emoji.native || emoji.id.replace(':', '');
|
newDeck[index] = emoji.native || emoji.id.replace(':', '');
|
||||||
onChange(newDeck);
|
onChange(newDeck);
|
||||||
},
|
},
|
||||||
[onChange, deck]
|
[onChange, deck],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
|
@ -134,12 +178,48 @@ export const ReactionDeck: React.FC<{
|
||||||
const handleAdd = useCallback(
|
const handleAdd = useCallback(
|
||||||
(emoji: any) => {
|
(emoji: any) => {
|
||||||
const newDeck = deckToArray(deck);
|
const newDeck = deckToArray(deck);
|
||||||
newDeck.push('👍');
|
const newEmoji = emoji.native || emoji.id.replace(':', '');
|
||||||
|
newDeck.push(newEmoji);
|
||||||
onChange(newDeck);
|
onChange(newDeck);
|
||||||
},
|
},
|
||||||
[onChange, deck],
|
[onChange, deck],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
|
||||||
|
|
||||||
|
const sensors = useSensors(
|
||||||
|
useSensor(PointerSensor, {
|
||||||
|
activationConstraint: {
|
||||||
|
distance: 5,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
useSensor(KeyboardSensor, {
|
||||||
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDragStart = useCallback(
|
||||||
|
(e: DragStartEvent) => {
|
||||||
|
const { active } = e;
|
||||||
|
|
||||||
|
setActiveId(active.id);
|
||||||
|
},
|
||||||
|
[setActiveId],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDragEnd = useCallback(
|
||||||
|
(e: DragEndEvent) => {
|
||||||
|
const { active, over } = e;
|
||||||
|
|
||||||
|
if (over && active.id !== over.id) {
|
||||||
|
//onChange(deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveId(null);
|
||||||
|
},
|
||||||
|
[dispatch, setActiveId],
|
||||||
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (!deck) {
|
if (!deck) {
|
||||||
return (
|
return (
|
||||||
|
@ -159,16 +239,28 @@ export const ReactionDeck: React.FC<{
|
||||||
showBackButton
|
showBackButton
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<DndContext
|
||||||
|
sensors={sensors}
|
||||||
|
collisionDetection={closestCenter}
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
>
|
||||||
|
<SortableContext items={deck.toArray()} strategy={rectSortingStrategy}>
|
||||||
{deck.map((emoji: any, index) => (
|
{deck.map((emoji: any, index) => (
|
||||||
|
<div key={index} id={index.toString()}>
|
||||||
<ReactionEmoji
|
<ReactionEmoji
|
||||||
emojiMap={emojiMap}
|
emojiMap={emojiMap}
|
||||||
key={index}
|
|
||||||
emoji={emoji.get('name')}
|
emoji={emoji.get('name')}
|
||||||
index={index}
|
index={index}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onRemove={handleRemove}
|
onRemove={handleRemove}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</SortableContext>
|
||||||
|
|
||||||
|
<DragOverlay>{activeId ? <span>Test</span> : null}</DragOverlay>
|
||||||
|
</DndContext>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<EmojiPickerDropdown
|
<EmojiPickerDropdown
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const config = {
|
const config = {
|
||||||
// '*': 'prettier --ignore-unknown --write',
|
'*': 'prettier --ignore-unknown --write',
|
||||||
'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
|
'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
|
||||||
// '*.{js,jsx,ts,tsx}': 'eslint --fix',
|
'*.{js,jsx,ts,tsx}': 'eslint --fix',
|
||||||
// '*.{css,scss}': 'stylelint --fix',
|
'*.{css,scss}': 'stylelint --fix',
|
||||||
// '*.haml': 'bin/haml-lint -a',
|
'*.haml': 'bin/haml-lint -a',
|
||||||
// '**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
|
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|
|
@ -5,13 +5,12 @@ import { optimizeLodashImports } from '@optimize-lodash/rollup-plugin';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import { PluginOption } from 'vite';
|
import { PluginOption } from 'vite';
|
||||||
import svgr from 'vite-plugin-svgr';
|
import svgr from 'vite-plugin-svgr';
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
import RailsPlugin from 'vite-plugin-rails';
|
import RailsPlugin from 'vite-plugin-rails';
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import legacy from '@vitejs/plugin-legacy';
|
import legacy from '@vitejs/plugin-legacy';
|
||||||
import vitePluginRequire from "vite-plugin-require";
|
|
||||||
|
|
||||||
import { defineConfig, UserConfigFnPromise, UserConfig } from 'vite';
|
import { defineConfig, UserConfigFnPromise, UserConfig } from 'vite';
|
||||||
import postcssPresetEnv from 'postcss-preset-env';
|
import postcssPresetEnv from 'postcss-preset-env';
|
||||||
|
@ -107,7 +106,6 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vitePluginRequire(),
|
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
RailsPlugin({
|
RailsPlugin({
|
||||||
compress: mode === 'production' && command === 'build',
|
compress: mode === 'production' && command === 'build',
|
||||||
|
@ -153,7 +151,8 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => {
|
||||||
svgr(),
|
svgr(),
|
||||||
// Old library types need to be converted
|
// Old library types need to be converted
|
||||||
optimizeLodashImports() as PluginOption,
|
optimizeLodashImports() as PluginOption,
|
||||||
!!process.env.ANALYZE_BUNDLE_SIZE && (visualizer() as PluginOption),
|
// Disable in knyblue because kmyblue-developer cannot launch foreman
|
||||||
|
// !!process.env.ANALYZE_BUNDLE_SIZE && (visualizer() as PluginOption),
|
||||||
],
|
],
|
||||||
} satisfies UserConfig;
|
} satisfies UserConfig;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue