flarum / cli

A CLI helper for developing Flarum extensions.
MIT License
39 stars 2 forks source link

πŸ”Ό Extension 2.0 Upgrade #43

Closed SychO9 closed 1 week ago

SychO9 commented 4 months ago

Brainstorm

Most of the changes will be simple pattern replacements, to do that we need to take into account some base behavior/abilities.


Dependencies βœ…

Infra βœ…

Frontend

mithril 2.0 -> 2.2

can't find exact instances of this in the code, but the more common instance is a copy from core of:

before

return [  
  <li className="Dropdown-header">{app.translator.trans('core.forum.search.users_heading')}</li>,  
  ...results.map((user) => {  
    const name = username(user);  

    const children = [highlight(name.text as string, query)];  

    return (  
      <li className="UserSearchResult" data-index={'users' + user.id()}>  
        <Link href={app.route.user(user)}>  
          {avatar(user)}  
          {{ ...name, text: undefined, children }}  
        </Link>  
      </li>    );  
  }),  
];

after

return [  
  <li className="Dropdown-header">{app.translator.trans('core.forum.search.users_heading')}</li>,  
  ...results.map((user) => {  
    const name = username(user, (name: string) => highlight(name, query));  

    return (  
      <li className="UserSearchResult" data-index={'users' + user.id()}>  
        <Link href={app.route.user(user)}>  
          {avatar(user)}  
          {name}  
        </Link>  
      </li>    );  
  }),  
];

Export Registry

Compat API βœ…

compat API no longer works, instead all modules must be imported

before https://github.com/flarum/framework/blob/1.x/extensions/tags/js/src/forum/compat.js after https://github.com/flarum/framework/blob/2.x/extensions/tags/js/src/forum/forum.ts

Importing Modules βœ…

// Before  
import Tag from 'flarum/tags/common/models/Tag';  

// After  
import Tag from 'ext:flarum/tags/common/models/Tag';

Code splitting βœ…

// after extend('flarum/forum/components/LogInModal', 'oninit', function() {
console.log('LogInModal is loaded');
});


**forum**
    Composer
    DiscussionsUserPage
    ForgotPasswordModal
    NotificationsPage
    PostStreamScrubber
    SearchModal
    SignUpModal
    DiscussionComposer
    EditPostComposer
    LogInModal
    PostStream
    ReplyComposer
    SettingsPage
    UserSecurityPage

**common**
    EditUserModal
### Misc

- [`IndexPage.prototype.sidebar`Β ->Β `IndexSidebar`](https://github.com/flarum/framework/blob/72f89c0209a5c4bbdc5482ecbdc2435dcd57550f/framework/core/js/src/forum/components/IndexSidebar.tsx) βœ…
- [`IndexPage.prototype.navItems`Β ->Β `IndexSidebar.prototype.navItems`](https://github.com/flarum/framework/blob/72f89c0209a5c4bbdc5482ecbdc2435dcd57550f/framework/core/js/src/forum/components/IndexSidebar.tsx) βœ…
- [`IndexPage.prototype.sidebarItems`Β ->Β `IndexSidebar.prototype.items`](https://github.com/flarum/framework/blob/72f89c0209a5c4bbdc5482ecbdc2435dcd57550f/framework/core/js/src/forum/components/IndexSidebar.tsx) βœ…
- `IndexPage.prototype.currentTag`Β ->Β `app.currentTag` βœ…
- `avatar(...)`Β  `icon(...)`Β -> `<Avatar ... />` `<Icon ... />` βœ…
- `this.currentTag` -> `app.currentTag` βœ…
- `UploadImageButton`Β namespace `admin` -> `common` βœ…
- `UploadImageButton` props βœ…
- `FormModal` vsΒ `Modal` βœ…
    - detect `onsubmit` method βœ…
    - => exists, switch to `FormModal` inheritance βœ…
- if inherits `Page` => warn about usingΒ `PageStructure` βœ…
* if inherits `NotificationsDropdown` => `HeaderDropdown` βœ…
```tsx
// before
 ... extends NotificationsDropdown

  getMenu() {
    return (
      <div className={'Dropdown-menu ' + this.attrs.menuClassName} onclick={this.menuClick.bind(this)}>
        {this.showing && <FlagList state={this.attrs.state} />}
      </div>
    );
  }

// after
 ... extends HeaderDropdown

  getContent() {
    return <FlagList state={this.attrs.state} />;
  }

// after import Form from 'flarum/common/components/Form';

$2

* `initializers.has('lock')`                     =>  `initializers.has('flarum-lock')` βœ…
* `initializers.has('subscriptions')`    => `initializers.has('flarum-subscriptions')` βœ…
* `initializers.has('flarum/nicknames')`  => `initializers.add('flarum-nicknames')` βœ…

# Backend

### Misc

* `(Extend\Notification)->type()` remove second arg. βœ…
##### `$dates` βœ…
```php
// before
$dates = [...]

// after
$casts = [... => 'datetime']

Filesystem

NullAdapterβœ…
// before
League\Flysystem\Adapter\NullAdapter

NullAdapter

// after
League\Flysystem\InMemory\InMemoryFilesystemAdapter

InMemoryFilesystemAdapter

// Before
League\Flysystem\Adpter\Local

Local

// after
League\Flysystem\Local\LocalFilesystemAdapter

LocalFilesystemAdapter

βœ…

// before
new FilesystemAdapter(new Filesystem(new LocalAdapter($path)));

// after
new FilesystemAdapter(new Filesystem($adapter = new LocalAdapter($path)), $adapter);

Mailer

JSON:API

Attempt to use advanced steps for this, but likely not possible to do any automated upgrading. warn instead to read:

http://localhost:3000/extend/update-2_0#jsonapi

Search

// after class TagFilter implements FilterInterface { use ValidateFilterTrait;

/**
 * @var SlugManager
 */
protected $slugger;

public function __construct(SlugManager $slugger)
{
    $this->slugger = $slugger;
}

public function getFilterKey(): string
{
    return 'tag';
}

public function filter(FilterState $filterState, $filterValue, bool $negate)
{
    $this->constrain($filterState->getQuery(), $filterValue, $negate, $filterState->getActor());
}

protected function constrain(Builder $query, $rawSlugs, $negate, User $actor)
{
    ...
}

}


```ts
export default class TagGambit extends KeyValueGambit {
  predicates = true;

  key(): string {
    return app.translator.trans('flarum-tags.lib.gambits.discussions.tag.key', {}, true);
  }

  hint(): string {
    return app.translator.trans('flarum-tags.lib.gambits.discussions.tag.hint', {}, true);
  }

  filterKey(): string {
    return 'tag';
  }

  gambitValueToFilterValue(value: string): string[] {
    return [value];
  }

  fromFilter(value: any, negate: boolean): string {
    let gambits = [];

    if (Array.isArray(value)) {
      gambits = value.map((value) => this.fromFilter(value.toString(), negate));
    } else {
      return `${negate ? '-' : ''}${this.key()}:${this.filterValueToGambitValue(value)}`;
    }

    return gambits.join(' ');
  }

  filterValueToGambitValue(value: string): string {
    return value;
  }
}
// before
(new Extend\Filter(PostFilterer::class))
    ->addFilter(PostTagFilter::class),

(new Extend\Filter(DiscussionFilterer::class))
    ->addFilter(TagFilterGambit::class)
    ->addFilterMutator(HideHiddenTagsFromAllDiscussionsPage::class),

(new Extend\SimpleFlarumSearch(DiscussionSearcher::class))
    ->addGambit(TagFilterGambit::class),

(new Extend\SimpleFlarumSearch(TagSearcher::class))
    ->setFullTextGambit(FullTextGambit::class),

// after
(new Extend\SearchDriver(DatabaseSearchDriver::class))
    ->addFilter(PostSearcher::class, PostTagFilter::class)
    ->addFilter(DiscussionSearcher::class, TagFilter::class)
    ->addMutator(DiscussionSearcher::class, HideHiddenTagsFromAllDiscussionsPage::class)
    ->addSearcher(Tag::class, TagSearcher::class)
    ->setFulltext(TagSearcher::class, FulltextFilter::class),

LESS βœ…

// before  
& when (@config-dark-mode) {  
  background: black;  
}  

// after  
[data-theme^=dark] & {  
  background: black;  
}
// before  
& when (@config-colored-header) {  
  background: black;  
}  

// after  
[data-colored-header^=true] & {  
  background: black;  
}