├── _config.yml
├── version
├── .github
└── FUNDING.yml
├── README
├── logo.png
├── monzo.png
└── screenshot.png
├── LauncherX
├── LauncherX
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── r_icon_16.png
│ │ │ ├── r_icon_32.png
│ │ │ ├── r_icon_64.png
│ │ │ ├── r_icon_1024.png
│ │ │ ├── r_icon_128.png
│ │ │ ├── r_icon_256-1.png
│ │ │ ├── r_icon_256.png
│ │ │ ├── r_icon_32-1.png
│ │ │ ├── r_icon_512-1.png
│ │ │ ├── r_icon_512.png
│ │ │ └── Contents.json
│ ├── LauncherX.entitlements
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── TwitterX.swift
│ └── Base.lproj
│ │ └── MainMenu.xib
└── LauncherX.xcodeproj
│ ├── xcshareddata
│ └── xcschemes
│ │ └── LauncherX.xcscheme
│ └── project.pbxproj
├── TwitterX
├── TwitterXAssets
│ ├── Images
│ │ ├── bookmarks.png
│ │ └── bookmarks@2x.png
│ └── Info.plist
├── TwitterX
│ ├── Theme
│ │ ├── TWXDuskTheme.h
│ │ └── TWXDuskTheme.m
│ ├── Features
│ │ ├── TWXTheme.h
│ │ ├── TWXSource.h
│ │ ├── TWXThemeMenu.h
│ │ ├── TWXFeature.h
│ │ ├── TWXRepliesCount.h
│ │ ├── TWXBookmarkActions.h
│ │ ├── TWXCharactersLimit.h
│ │ ├── TWXRoundedAvatars.h
│ │ ├── TWXBookmarksTimeline.h
│ │ ├── TWXLightThemeReborn.h
│ │ ├── TWXBookmarksSidebarItem.h
│ │ ├── TWXConsumerKeysOverride.h
│ │ ├── TWXDirectMessagesCrash.h
│ │ ├── TWXEnhancedFullscreen.h
│ │ ├── TWXRoundedStatusContent.h
│ │ ├── TWXSource.m
│ │ ├── TWXCharactersLimit.m
│ │ ├── TWXTheme.m
│ │ ├── TWXDirectMessagesCrash.m
│ │ ├── TWXEnhancedFullscreen.m
│ │ ├── TWXRoundedStatusContent.m
│ │ ├── TWXLightThemeReborn.m
│ │ ├── TWXThemeMenu.m
│ │ ├── TWXRoundedAvatars.m
│ │ ├── TWXBookmarkActions.m
│ │ ├── TWXRepliesCount.m
│ │ ├── TWXBookmarksTimeline.m
│ │ ├── TWXBookmarksSidebarItem.m
│ │ └── TWXConsumerKeysOverride.m
│ ├── Utilities
│ │ ├── NSString+TWX.h
│ │ ├── NSColor+TWX.h
│ │ ├── NSString+TWX.m
│ │ ├── NSImage+TWX.h
│ │ ├── NSColor+TWX.m
│ │ ├── NSBundle+TWX.h
│ │ ├── NSView+TWX.h
│ │ ├── NSBundle+TWX.m
│ │ ├── NSImage+TWX.m
│ │ ├── TWXRuntime.h
│ │ ├── NSView+TWX.m
│ │ └── TWXRuntime.m
│ ├── TwitterX.h
│ ├── Info.plist
│ ├── Service
│ │ ├── TWXAPI+Bookmarks.h
│ │ ├── TWXAPI.h
│ │ ├── TWXAPI.m
│ │ └── TWXAPI+Bookmarks.m
│ └── TwitterX.m
└── TwitterX.xcodeproj
│ ├── xcshareddata
│ └── xcschemes
│ │ └── TwitterX.xcscheme
│ └── project.pbxproj
├── TwitterX.xcworkspace
└── contents.xcworkspacedata
├── .gitignore
├── CHANGELOG.md
├── LICENSE
└── README.md
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/version:
--------------------------------------------------------------------------------
1 | {"version_number":"0.2.2"}
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: nakiostudio
2 |
--------------------------------------------------------------------------------
/README/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/logo.png
--------------------------------------------------------------------------------
/README/monzo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/monzo.png
--------------------------------------------------------------------------------
/README/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/README/screenshot.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TwitterX/TwitterXAssets/Images/bookmarks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/TwitterX/TwitterXAssets/Images/bookmarks.png
--------------------------------------------------------------------------------
/TwitterX/TwitterXAssets/Images/bookmarks@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/TwitterX/TwitterXAssets/Images/bookmarks@2x.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_16.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_64.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_1024.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_128.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256-1.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_256.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_32-1.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512-1.png
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakiostudio/TwitterX/HEAD/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/r_icon_512.png
--------------------------------------------------------------------------------
/TwitterX.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Theme/TWXDuskTheme.h:
--------------------------------------------------------------------------------
1 | //
2 | // TWXDuskheme.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TWXDuskTheme : NSObject
12 | + (id)themeIdentifier;
13 | @end
14 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXTheme.h:
--------------------------------------------------------------------------------
1 | //
2 | // TWXTheme.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TWXFeature.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface TWXTheme: NSObject
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXSource.h:
--------------------------------------------------------------------------------
1 | //
2 | // TWXSource.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TWXFeature.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface TWXSource: NSObject
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSString+TWX.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+TWX.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSString (TWX)
14 | -(NSString *) stringByStrippingHTML;
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSColor+TWX.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSColor+TWX.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSColor (TWX)
14 | + (instancetype)colorFromHexString:(NSString *)hexString;
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXThemeMenu.h:
--------------------------------------------------------------------------------
1 | //
2 | // TWXThemeMenu.h
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TWXFeature.h"
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface TWXThemeMenu: NSObject
15 |
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | # CI
26 | test_output/
27 | .fastlane/report.xml
28 | .fastlane/README.md
29 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/LauncherX.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 | com.apple.security.network.client
10 |
11 | com.apple.security.network.server
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSString+TWX.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+TWX.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "NSString+TWX.h"
10 |
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @implementation NSString (TWX)
15 | -(NSString *) stringByStrippingHTML {
16 | NSRange r;
17 | NSString *s = [self copy];
18 | while ((r = [s rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
19 | s = [s stringByReplacingCharactersInRange:r withString:@""];
20 | return s;
21 | }
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/TwitterX.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | FOUNDATION_EXPORT double TwitterXVersionNumber;
14 | FOUNDATION_EXPORT const unsigned char TwitterXVersionString[];
15 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSImage+TWX.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface NSImage (TWX)
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSColor+TWX.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSColor+TWX.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "NSColor+TWX.h"
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @implementation NSColor (TWX)
14 |
15 | + (instancetype)colorFromHexString:(NSString *)hexString {
16 | unsigned rgbValue = 0;
17 | NSScanner *scanner = [NSScanner scannerWithString:hexString];
18 | [scanner setScanLocation:1]; // bypass '#' character
19 | [scanner scanHexInt:&rgbValue];
20 | return [NSColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
21 | }
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXFeature.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @protocol TWXFeature
16 |
17 | + (void)loadFeature;
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRepliesCount.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXRepliesCount: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSBundle+TWX.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface NSBundle (TWX)
16 |
17 | + (nullable NSBundle *)twx_assetsBundle;
18 |
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarkActions.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXBookmarkActions: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXCharactersLimit.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXCharactersLimit: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRoundedAvatars.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXRoundedAvatars: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarksTimeline.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXBookmarksTimeline: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXLightThemeReborn.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXLightThemeReborn: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarksSidebarItem.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXBookmarksSidebarItem: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXConsumerKeysOverride.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXConsumerKeysOverride: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXDirectMessagesCrash.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXDirectMessagesCrash: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXEnhancedFullscreen.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXEnhancedFullscreen: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRoundedStatusContent.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXFeature.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXRoundedStatusContent: NSObject
17 |
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/TwitterX/TwitterXAssets/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | NSHumanReadableCopyright
22 | Copyright © 2018 twitterx. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v.0.2.2
4 |
5 | * Shown preferences menu when the login modal is presented.
6 |
7 | ## v.0.2.1
8 |
9 | * Fixed issue bookmarking retweets.
10 |
11 | ## v.0.2.0
12 |
13 | * Implemented Bookmarks.
14 |
15 | ## v.0.1.1
16 |
17 | * Drag and drop no longer needed as the Twitter app is
18 | automatically detected.
19 | * OAuth consumer key and secret configurable from the
20 | preferences menu.
21 |
22 | ## v.0.1.0
23 |
24 | * Fixed authentication. For this change to work you may need to
25 | sign out and sign in again.
26 |
27 | ## v.0.0.3
28 |
29 | * Fixed macOS 10.10 compatibility issues.
30 |
31 | ## v.0.0.2
32 |
33 | * Supported macOS versions greater or equal to `10.9`.
34 |
35 | ## v.0.0.1
36 |
37 | * Increased tweet length limit to 280 characters.
38 | * Rounded avatar images.
39 | * Fixed fullscreen toolbar bug.
40 | * Updated light theme appearance.
41 | * Added reply counters to timeline statuses.
42 | * Fixed crash loading direct messages.
43 |
44 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.2.2
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSHumanReadableCopyright
22 | Copyright © 2018 twitterx. All rights reserved.
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXSource.m:
--------------------------------------------------------------------------------
1 | //
2 | // TWXSource.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "TWXSource.h"
10 | #import "TWXRuntime.h"
11 | #import
12 | #import "NSString+TWX.h"
13 |
14 | @implementation TWXSource
15 | + (void)loadFeature {
16 | [TWXRuntime exchangeInstanceMethod:@"updateAttributedStrings" ofClass:@"TMNewDetailedStatusCell"];
17 | }
18 | @end
19 |
20 | @implementation NSObject (TWX)
21 | - (void)TMNewDetailedStatusCell_updateAttributedStrings {
22 | [self TMNewDetailedStatusCell_updateAttributedStrings];
23 | id status = [self valueForKey:@"_status"];
24 | NSString* sourceHTML = [status valueForKey:@"_source"];
25 | NSString* source = [sourceHTML stringByStrippingHTML];
26 | NSTextView* textView = [self valueForKey:@"dateTimeAndLocation"];
27 | textView.string = [textView.string stringByAppendingString:source];
28 | }
29 | @end
30 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Service/TWXAPI+Bookmarks.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXAPI.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @interface TWXAPI (Bookmarks)
17 |
18 | - (void)fetchBookmarksWithCompletion:(void(^)(NSArray *))completion;
19 |
20 | - (void)addBookmarkForTweetWithIdentifier:(NSString *)identifier;
21 |
22 | - (void)removeBookmarkForTweetWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion ;
23 |
24 | @end
25 |
26 | NS_ASSUME_NONNULL_END
27 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXCharactersLimit.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import "TWXCharactersLimit.h"
12 | #import "TWXRuntime.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @implementation TWXCharactersLimit
17 |
18 | + (void)loadFeature {
19 | [TWXRuntime exchangeInstanceMethod:@"characterLimit" ofClass:@"TwitterComposition"];
20 | }
21 |
22 | @end
23 |
24 | @implementation NSObject (TWX)
25 |
26 | - (NSInteger)TwitterComposition_characterLimit {
27 | return 280;
28 | }
29 |
30 | @end
31 |
32 | NS_ASSUME_NONNULL_END
33 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Service/TWXAPI.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface TWXAPI: NSObject
16 |
17 | - (instancetype)initWithTwitterAccount:(id)twitterAccount;
18 |
19 | - (void)v1_1_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback;
20 |
21 | - (void)v2_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback;
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSView+TWX.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface NSView (TWX)
16 |
17 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr;
18 |
19 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr constant:(CGFloat)constant;
20 |
21 | - (void)anchorDimenstionAttribute:(NSLayoutAttribute)attr toConstant:(CGFloat)constant;
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Carlos Vidal
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXTheme.m:
--------------------------------------------------------------------------------
1 | //
2 | // TWXTheme.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/08.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "TWXTheme.h"
10 | #import
11 | #import "TWXRuntime.h"
12 | #import "NSView+TWX.h"
13 | #import "TWXDuskTheme.h"
14 |
15 | @implementation TWXTheme
16 | + (void)loadFeature {
17 | [TWXRuntime exchangeClassMethod:@"saveThemeIdentifierToUserDefaults:" ofClass:@"TMTheme"];
18 | [TWXRuntime exchangeClassMethod:@"themeIdentifierFromUserDefaults" ofClass:@"TMTheme"];
19 | }
20 | @end
21 |
22 | @implementation NSObject (TWX)
23 |
24 | NSString *const TMSelectedThemeIdentifierDefaultsKey = @"TMSelectedThemeIdentifier";
25 |
26 | + (void)TMTheme_saveThemeIdentifierToUserDefaults:(id)arg1 {
27 | [[NSUserDefaults standardUserDefaults] setObject:arg1 forKey:TMSelectedThemeIdentifierDefaultsKey];
28 | [[NSUserDefaults standardUserDefaults] synchronize];
29 | }
30 | + (id)TMTheme_themeIdentifierFromUserDefaults {
31 | return [[NSUserDefaults standardUserDefaults] stringForKey:TMSelectedThemeIdentifierDefaultsKey];
32 | }
33 | @end
34 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 0.2.2
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright (c) 2018 Carlos Vidal - The MIT License
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSBundle+TWX.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import "NSBundle+TWX.h"
12 | #import "TWXRuntime.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @implementation NSBundle (TWX)
17 |
18 | + (nullable NSBundle *)twx_assetsBundle {
19 | static NSBundle *bundle;
20 | static dispatch_once_t onceToken;
21 | dispatch_once(&onceToken, ^{
22 | NSBundle *const frameworkBundle = [NSBundle bundleForClass:[TWXRuntime class]];
23 | NSURL *__nullable const assetsBundleURL = [frameworkBundle URLForResource:@"TwitterXAssets" withExtension:@"bundle"];
24 | if (assetsBundleURL) {
25 | bundle = [NSBundle bundleWithURL:assetsBundleURL];
26 | }
27 | });
28 | return bundle;
29 | }
30 |
31 | @end
32 |
33 | NS_ASSUME_NONNULL_END
34 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXDirectMessagesCrash.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXDirectMessagesCrash.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation TWXDirectMessagesCrash
18 |
19 | + (void)loadFeature {
20 | [TWXRuntime exchangeClassMethod:@"attributedTextForMessage:cellType:" ofClass:@"TMMessageCell"];
21 | }
22 |
23 | @end
24 |
25 | @implementation NSObject (TWX)
26 |
27 | + (nullable id)TMMessageCell_attributedTextForMessage:(nullable id)message cellType:(NSInteger)cellType {
28 | if ([NSStringFromClass(self) isEqualToString:@"TMMessageCell"] && message == nil) {
29 | return nil;
30 | }
31 | return [self TMMessageCell_attributedTextForMessage:message cellType:cellType];
32 | }
33 |
34 | @end
35 |
36 | NS_ASSUME_NONNULL_END
37 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSImage+TWX.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import "NSImage+TWX.h"
12 | #import "TWXRuntime.h"
13 | #import "NSBundle+TWX.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation NSImage (TWX)
18 |
19 | + (void)load {
20 | [TWXRuntime exchangeClassMethod:@"imageNamed:" ofClass:@"NSImage"];
21 | }
22 |
23 | @end
24 |
25 | @implementation NSObject (TWX)
26 |
27 | + (NSImage *)NSImage_imageNamed:(NSString *)name {
28 | NSParameterAssert(name);
29 |
30 | NSImage *__nullable const mainBundleImage = [self NSImage_imageNamed:name];
31 | if (mainBundleImage) {
32 | return mainBundleImage;
33 | }
34 |
35 | NSBundle *__nullable assetsBundle = [NSBundle twx_assetsBundle];
36 | return [assetsBundle imageForResource:name];
37 | }
38 |
39 | @end
40 |
41 | NS_ASSUME_NONNULL_END
42 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXEnhancedFullscreen.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXEnhancedFullscreen.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation TWXEnhancedFullscreen
18 |
19 | + (void)loadFeature {
20 | [TWXRuntime exchangeInstanceMethod:@"backgroundColor" ofClass:@"NSToolbarFullScreenWindow"];
21 | }
22 |
23 | @end
24 |
25 | @implementation NSObject (TWX)
26 |
27 | - (nullable NSColor *)NSToolbarFullScreenWindow_backgroundColor {
28 | if ([self isKindOfClass:[@"NSToolbarFullScreenWindow" twx_class]]) {
29 | NSWindow *const window = (NSWindow *)self;
30 | [window.contentView.layer.sublayers.firstObject setHidden:YES];
31 | return [NSColor clearColor];
32 | }
33 | return [self NSToolbarFullScreenWindow_backgroundColor];
34 | }
35 |
36 | @end
37 |
38 | NS_ASSUME_NONNULL_END
39 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/TWXRuntime.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @interface TWXRuntime: NSObject
16 |
17 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString prefix:(nullable NSString *)prefix;
18 |
19 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString;
20 |
21 | + (void)exchangeClassMethod:(NSString *)method ofClass:(NSString *)classString;
22 |
23 | @end
24 |
25 | @interface NSString (TWXRuntime)
26 |
27 | - (Class)twx_class;
28 |
29 | @end
30 |
31 | @interface NSObject (TWXRuntime)
32 |
33 | - (void)twx_performSelector:(SEL)selector value:(unsigned long long)value;
34 |
35 | - (void)twx_performSelector:(SEL)selector withObject:(id)object value:(unsigned long long)value;
36 |
37 | - (void)twx_invoke:(NSString*)method arg:(id)arg;
38 |
39 | - (id)twx_invokeAndReturnValue:(NSString*)method;
40 |
41 | @end
42 |
43 | NS_ASSUME_NONNULL_END
44 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "r_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "r_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "r_icon_32-1.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "r_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "r_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "r_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "r_icon_256-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "r_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "r_icon_512-1.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "r_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRoundedStatusContent.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXRoundedStatusContent.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation TWXRoundedStatusContent
18 |
19 | + (void)loadFeature {
20 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMNewTimelineStatusCell"];
21 | }
22 |
23 | @end
24 |
25 | @implementation NSObject (TWX)
26 |
27 | - (void)TMNewTimelineStatusCell_layout {
28 | [self TMNewTimelineStatusCell_layout];
29 |
30 | if ([self isKindOfClass:[@"TMNewTimelineStatusCell" twx_class]]) {
31 | NSView *const cell = (NSView *)self;
32 | NSView *const containerView = (NSView *)[self performSelector:@selector(containerView)];
33 | containerView.layer.cornerRadius = 12.0f;
34 | NSTextField *const textField = (NSTextField *)[self performSelector:@selector(tweetTextField)];
35 | if (cell.layer.backgroundColor) {
36 | textField.backgroundColor = [NSColor colorWithCGColor:cell.layer.backgroundColor];
37 | }
38 | }
39 | }
40 |
41 | @end
42 |
43 | NS_ASSUME_NONNULL_END
44 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/TwitterX.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import "TwitterX.h"
12 | #import "TWXCharactersLimit.h"
13 | #import "TWXRoundedAvatars.h"
14 | #import "TWXEnhancedFullscreen.h"
15 | #import "TWXRoundedStatusContent.h"
16 | #import "TWXLightThemeReborn.h"
17 | #import "TWXConsumerKeysOverride.h"
18 | #import "TWXRepliesCount.h"
19 | #import "TWXDirectMessagesCrash.h"
20 | #import "TWXBookmarkActions.h"
21 | #import "TWXBookmarksTimeline.h"
22 | #import "TWXBookmarksSidebarItem.h"
23 | #import "TWXThemeMenu.h"
24 | #import "TWXTheme.h"
25 | #import "TWXSource.h"
26 |
27 | NS_ASSUME_NONNULL_BEGIN
28 |
29 | @implementation NSObject (TWX)
30 |
31 | + (void)load {
32 | [TWXCharactersLimit loadFeature];
33 | [TWXRoundedAvatars loadFeature];
34 | [TWXEnhancedFullscreen loadFeature];
35 | [TWXRoundedStatusContent loadFeature];
36 | [TWXLightThemeReborn loadFeature];
37 | [TWXConsumerKeysOverride loadFeature];
38 | [TWXRepliesCount loadFeature];
39 | [TWXDirectMessagesCrash loadFeature];
40 | [TWXBookmarkActions loadFeature];
41 | [TWXBookmarksTimeline loadFeature];
42 | [TWXBookmarksSidebarItem loadFeature];
43 | [TWXTheme loadFeature];
44 | [TWXThemeMenu loadFeature];
45 | [TWXSource loadFeature];
46 | }
47 |
48 | @end
49 |
50 | NS_ASSUME_NONNULL_END
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [](#)
2 |
3 | An educational project aiming to maintain Twitter for macOS working while adding new features...
4 | All this by injecting code into the official binary.
5 |
6 |
7 |
8 |
9 |
10 | ## TL;DR;
11 | [Tutorial](https://medium.com/@clipper101/the-best-twitter-app-for-macos-d86778e535c3) by Clipp3r.
12 |
13 | ## Why
14 |
15 | Twitter for macOS provides the best *native and free* experience, although it lacks some features
16 | available on the web version.
17 |
18 | ## How
19 |
20 | > As of 16th of March 2018 you need to provide a valid consumer key and consumer secret in order
21 | > to make the application work. Using custom keys implies uncertain consequences, use TwitterX at
22 | > your own risk.
23 |
24 | For this project to work you need to have Twitter for macOS in your computer. Twitter removed the
25 | application from the macOS AppStore but if you ever installed the application it will still be
26 | available under the **purchases** section of the AppStore.
27 |
28 | TwitterX is a framework that once it is loaded modifies the application behaviour at runtime. This
29 | project provides **LauncherX**, an application that eases the task of loading TwitterX.
30 |
31 | [Download LauncherX](https://github.com/nakiostudio/TwitterX/releases)
32 |
33 | If you want to run the unmodified version of Twitter simply open the Twitter application instead of
34 | LauncherX.
35 |
36 | **This project does not and will never modify the Twitter binary. Upsetting Twitter is not among the
37 | goals of this project therefore modifying or distributing code copyrighted by Twitter will never be
38 | an option.**
39 |
40 | ## Future
41 |
42 | The application is still fully functional although it is hard to tell for how long will this continue.
43 |
44 | ## Author
45 |
46 | [Carlos Vidal](https://www.twitter.com/nakiostudio)
47 |
48 | ## LICENSE
49 |
50 | This project is licensed under the terms of the MIT license. See the LICENSE file.
51 |
52 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXLightThemeReborn.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXLightThemeReborn.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation TWXLightThemeReborn
18 |
19 | + (void)loadFeature {
20 | [TWXRuntime exchangeInstanceMethod:@"sidebarBackgroundColor" ofClass:@"TMLightTheme"];
21 | [TWXRuntime exchangeInstanceMethod:@"sidebarButtonColor" ofClass:@"TMLightTheme"];
22 | [TWXRuntime exchangeInstanceMethod:@"sidebarButtonSelectedColor" ofClass:@"TMLightTheme"];
23 | }
24 |
25 | @end
26 |
27 | @implementation NSObject (TWX)
28 |
29 | - (NSColor *)TMLightTheme_sidebarBackgroundColor {
30 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) {
31 | return [NSColor whiteColor];
32 | }
33 | return [self TMLightTheme_sidebarBackgroundColor];
34 | }
35 |
36 | - (NSColor *)TMLightTheme_sidebarButtonColor {
37 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) {
38 | return [NSColor colorWithRed:102.0f/255.0f green:119.0f/255.0f blue:135.0f/255.0f alpha:1.0f];
39 | }
40 | return [self TMLightTheme_sidebarButtonColor];
41 | }
42 |
43 | - (NSColor *)TMLightTheme_sidebarButtonSelectedColor {
44 | if ([self isKindOfClass:[@"TMLightTheme" twx_class]]) {
45 | return [NSColor colorWithRed:29.0f/255.0f green:161.0f/255.0f blue:242.0f/255.0f alpha:1.0f];
46 | }
47 | return [self TMLightTheme_sidebarButtonColor];
48 | }
49 |
50 | @end
51 |
52 | NS_ASSUME_NONNULL_END
53 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/NSView+TWX.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import "NSView+TWX.h"
12 |
13 | NS_ASSUME_NONNULL_BEGIN
14 |
15 | @implementation NSView (TWX)
16 |
17 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr {
18 | [self anchorToAttribute:toAttr ofView:view fromAttribute:fromAttr constant:0.f];
19 | }
20 |
21 | - (void)anchorToAttribute:(NSLayoutAttribute)toAttr ofView:(NSView *)view fromAttribute:(NSLayoutAttribute)fromAttr constant:(CGFloat)constant {
22 | NSParameterAssert(view);
23 |
24 | NSLayoutConstraint *const constraint =
25 | [NSLayoutConstraint
26 | constraintWithItem:self
27 | attribute:fromAttr
28 | relatedBy:NSLayoutRelationEqual
29 | toItem:view
30 | attribute:toAttr
31 | multiplier:1.0f
32 | constant:constant];
33 |
34 | if (@available(macOS 10.10, *)) {
35 | [NSLayoutConstraint activateConstraints:@[constraint]];
36 | return;
37 | }
38 |
39 | NSView *const ownerView = (fromAttr == NSLayoutAttributeWidth || fromAttr == NSLayoutAttributeHeight) ? self : self.superview;
40 | [ownerView addConstraint:constraint];
41 | }
42 |
43 | - (void)anchorDimenstionAttribute:(NSLayoutAttribute)attr toConstant:(CGFloat)constant {
44 | NSLayoutConstraint *const constraint =
45 | [NSLayoutConstraint
46 | constraintWithItem:self
47 | attribute:attr
48 | relatedBy:NSLayoutRelationEqual
49 | toItem:nil
50 | attribute:attr
51 | multiplier:1.0f
52 | constant:constant];
53 |
54 | if (@available(macOS 10.10, *)) {
55 | [NSLayoutConstraint activateConstraints:@[constraint]];
56 | return;
57 | }
58 |
59 | [self addConstraint:constraint];
60 | }
61 |
62 | @end
63 |
64 | NS_ASSUME_NONNULL_END
65 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | import AppKit
12 |
13 | @NSApplicationMain
14 | class AppDelegate: NSObject, NSApplicationDelegate {
15 |
16 | private let twitterX: TwitterX = TwitterX()
17 |
18 | func applicationDidFinishLaunching(_ aNotification: Notification) {
19 | // Check for updates and launch Twitter app
20 | self.twitterX.checkUpdates { [weak self] (isThereUpdate) in
21 | if isThereUpdate {
22 | self?.displayUpdateModal()
23 | }
24 | self?.launchTwitterApp()
25 | }
26 | }
27 |
28 | // MARK: - Private
29 |
30 | private func launchTwitterApp() {
31 | self.twitterX.launchTwitterApp { [weak self] (success, error) in
32 | if success {
33 | exit(0)
34 | }
35 | if let error = error {
36 | self?.displayError(error)
37 | }
38 | }
39 | }
40 |
41 | private func displayError(_ error: Error) {
42 | let alert: NSAlert = NSAlert(error: error)
43 | alert.runModal()
44 | }
45 |
46 | private func displayUpdateModal() {
47 | let alert: NSAlert = NSAlert()
48 | alert.messageText = "There is a new version of TwitterX available"
49 | alert.informativeText = "New tweaks and fixes are available. Click on \"Get update\" to visit the changelog and download the latest version."
50 | alert.addButton(withTitle: "Get update")
51 | alert.addButton(withTitle: "Not now")
52 | if alert.runModal().rawValue == 1000 {
53 | NSWorkspace.shared.open(URL(string: "https://github.com/nakiostudio/TwitterX/releases")!)
54 | }
55 | }
56 |
57 | @IBAction private func didTapQuitMenuItem(_ sender: Any?) {
58 | exit(0)
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/TwitterX.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | import AppKit
12 |
13 | final class TwitterX {
14 |
15 | private static let latestVersionURL: URL = URL(string: "https://raw.githubusercontent.com/nakiostudio/TwitterX/master/version")!
16 |
17 | private let urlSession: URLSession
18 | private let userDefaults: UserDefaults
19 | private let twitterAppURL: URL?
20 | private let twitterXFrameworkURL: URL!
21 | private let versionNumber: String!
22 |
23 | init(urlSession: URLSession = .shared, userDefaults: UserDefaults = .standard) {
24 | self.urlSession = urlSession
25 | self.userDefaults = userDefaults
26 | self.twitterAppURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.twitter.twitter-mac")
27 | let twitterXFrameworkPath: String = Bundle.main.path(forResource: "TwitterX", ofType: "framework")! + "/Versions/A/TwitterX"
28 | self.twitterXFrameworkURL = URL(fileURLWithPath: twitterXFrameworkPath)
29 | self.versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
30 | }
31 |
32 | // MARK: - Public
33 |
34 | func launchTwitterApp(completion: (Bool, Error?) -> Void) {
35 | guard let twitterAppURL = self.twitterAppURL else {
36 | completion(false, nil)
37 | return
38 | }
39 |
40 | do {
41 | try NSWorkspace.shared.launchApplication(
42 | at: twitterAppURL,
43 | options: [],
44 | configuration: [.environment: ["DYLD_INSERT_LIBRARIES": self.twitterXFrameworkURL.path]]
45 | )
46 | completion(true, nil)
47 | } catch let error {
48 | completion(false, error)
49 | }
50 | }
51 |
52 | func checkUpdates(completion: @escaping (Bool) -> Void) {
53 | self.urlSession.dataTask(with: TwitterX.latestVersionURL) { (data, response, error) in
54 | guard let data = data, error == nil,
55 | let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any],
56 | let latestVersionNumber = json?["version_number"] as? String else {
57 | DispatchQueue.main.async {
58 | completion(false)
59 | }
60 | return
61 | }
62 |
63 | DispatchQueue.main.async {
64 | completion(latestVersionNumber.compare(self.versionNumber, options: .numeric) == .orderedDescending)
65 | }
66 | }.resume()
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXThemeMenu.m:
--------------------------------------------------------------------------------
1 | //
2 | // TWXThemeMenu.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "TWXThemeMenu.h"
10 | #import
11 | #import "TWXRuntime.h"
12 | #import "NSView+TWX.h"
13 | #import "TWXDuskTheme.h"
14 |
15 | @implementation TWXThemeMenu
16 | + (void)loadFeature {
17 | [[@"TMTheme" twx_class] twx_invoke:@"registerThemeClass:" arg:TWXDuskTheme.class];
18 | [TWXRuntime exchangeInstanceMethod:@"setGeneralView:"
19 | ofClass:@"TweetiePreferencesWindowController"
20 | prefix: @"TWXThemeMenu"];
21 | [TWXRuntime exchangeInstanceMethod:@"didChangeDarkModeSetting:" ofClass:@"TweetiePreferencesWindowController"];
22 | [TWXRuntime exchangeInstanceMethod:@"windowDidLoad" ofClass:@"TweetiePreferencesWindowController"];
23 | }
24 | @end
25 |
26 | @implementation NSObject (TWX)
27 |
28 | - (void)TweetiePreferencesWindowController_windowDidLoad {
29 | [self TweetiePreferencesWindowController_windowDidLoad];
30 |
31 | // select current identifier
32 | NSPopUpButton* themePopUpButton = [self valueForKey:@"_darkButton"];
33 | NSString* currentThemeIdentifier = [[@"TMTheme" twx_class] twx_invokeAndReturnValue:@"currentThemeIdentifier"];
34 | NSInteger currentThemeIndex = [[self TWX_allThemeIdentifiers] indexOfObject:currentThemeIdentifier];
35 | NSLog(@"TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView %ld", (long)currentThemeIndex);
36 | [themePopUpButton selectItemAtIndex:currentThemeIndex];
37 | }
38 |
39 | - (NSArray*)TWX_allThemeNames {
40 | return @[NSLocalizedStringFromTable(@"YLa-rz-sxj.title", @"Preferences", nil),
41 | NSLocalizedStringFromTable(@"07L-lv-aR5.title", @"Preferences", nil),
42 | @"Dusk"];
43 | }
44 |
45 | - (NSArray*)TWX_allThemeIdentifiers {
46 | return @[@"com.twitter.twitter.themeIdentifiers.lightTheme",
47 | @"com.twitter.twitter.themeIdentifiers.darkTheme",
48 | TWXDuskTheme.themeIdentifier];
49 | }
50 |
51 | - (void)TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:(NSView *)view {
52 | if (![self isKindOfClass:[@"TweetiePreferencesWindowController" twx_class]]) {
53 | [self TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:view];
54 | return;
55 | }
56 |
57 | NSPopUpButton* themePopUpButton = [self valueForKey:@"_darkButton"];
58 | [themePopUpButton removeAllItems];
59 | for (id name in [self TWX_allThemeNames]) {
60 | [themePopUpButton addItemWithTitle:name];
61 | }
62 |
63 | [self TWXThemeMenu_TweetiePreferencesWindowController_setGeneralView:view];
64 | }
65 |
66 | - (void)TweetiePreferencesWindowController_didChangeDarkModeSetting:(NSPopUpButton *)sender {
67 | NSInteger index = [sender indexOfSelectedItem];
68 | NSString* identifier = [self TWX_allThemeIdentifiers][index];
69 | [[@"TMTheme" twx_class] twx_invoke:@"switchToThemeWithIdentifier:" arg:identifier];
70 | [[@"TMTheme" twx_class] twx_invoke:@"saveThemeIdentifierToUserDefaults:" arg:identifier];
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRoundedAvatars.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXRoundedAvatars.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface NSView (TWX)
18 |
19 | - (void)roundView;
20 |
21 | @end
22 |
23 | @implementation TWXRoundedAvatars
24 |
25 | + (void)loadFeature {
26 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMAvatarImageView"];
27 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMEditableAvatarView"];
28 | [TWXRuntime exchangeInstanceMethod:@"layout" ofClass:@"TMComposerAvatarView"];
29 | [TWXRuntime exchangeInstanceMethod:@"setBaseImage:" ofClass:@"TMAvatarSidebarButton"];
30 | }
31 |
32 | @end
33 |
34 | @implementation NSObject (Feature)
35 |
36 | - (void)TMAvatarImageView_layout {
37 | [self TMAvatarImageView_layout];
38 |
39 | if ([self isKindOfClass:[@"TMAvatarImageView" twx_class]]) {
40 | [(NSView *)self roundView];
41 | }
42 | }
43 |
44 | - (void)TMEditableAvatarView_layout {
45 | [self TMEditableAvatarView_layout];
46 |
47 | if ([self isKindOfClass:[@"TMEditableAvatarView" twx_class]]) {
48 | [(NSView *)self roundView];
49 | [((NSView *)self).superview roundView];
50 | }
51 | }
52 |
53 | - (void)TMComposerAvatarView_layout {
54 | [self TMComposerAvatarView_layout];
55 |
56 | if ([self isKindOfClass:[@"TMComposerAvatarView" twx_class]]) {
57 | [(NSView *)self roundView];
58 | }
59 | }
60 |
61 | - (void)TMAvatarSidebarButton_setBaseImage:(nullable NSImage *)image {
62 | if (image == nil || ![self isKindOfClass:[@"TMAvatarSidebarButton" twx_class]]) {
63 | [self TMAvatarSidebarButton_setBaseImage:image];
64 | return;
65 | }
66 |
67 | NSImage *const roundedImage = [[NSImage alloc] initWithSize:[image size]];
68 | [roundedImage lockFocus];
69 | [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
70 | NSRect const imageFrame = NSRectFromCGRect(CGRectMake(0, 0, image.size.width, image.size.height));
71 | [[NSBezierPath bezierPathWithRoundedRect:imageFrame xRadius:image.size.height * 0.5f yRadius:image.size.height * 0.5f] addClip];
72 | [image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0, 0, image.size.width, image.size.height) operation:NSCompositingOperationSourceOver fraction:1];
73 | [roundedImage unlockFocus];
74 |
75 | [self TMAvatarSidebarButton_setBaseImage:roundedImage];
76 | }
77 |
78 | @end
79 |
80 | @implementation NSView (TWX)
81 |
82 | - (void)roundView {
83 | self.wantsLayer = YES;
84 | self.layer.masksToBounds = YES;
85 | self.layer.cornerRadius = self.bounds.size.height * 0.5f;
86 | }
87 |
88 | @end
89 |
90 | NS_ASSUME_NONNULL_END
91 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarkActions.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import
13 | #import "TWXBookmarkActions.h"
14 | #import "TWXRuntime.h"
15 | #import "TWXAPI+Bookmarks.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | static void *TwitterAPIRefKey = &TwitterAPIRefKey;
20 |
21 | @implementation TWXBookmarkActions
22 |
23 | + (void)loadFeature {
24 | [TWXRuntime exchangeInstanceMethod:@"_addMenuItemsForStatus:toMenu:" ofClass:@"TMBaseViewController"];
25 | }
26 |
27 | @end
28 |
29 | @implementation NSObject (TWX)
30 |
31 | - (void)TMBaseViewController__addMenuItemsForStatus:(id)status toMenu:(NSMenu *)menu {
32 | NSParameterAssert(status && menu);
33 |
34 | if (![self isKindOfClass:[@"TMBaseViewController" twx_class]]) {
35 | [self TMBaseViewController__addMenuItemsForStatus:status toMenu:menu];
36 | return;
37 | }
38 |
39 | __block NSInteger indexToInsert = 0;
40 | [menu.itemArray enumerateObjectsUsingBlock:^(NSMenuItem *_Nonnull item, NSUInteger idx, BOOL *_Nonnull stop) {
41 | if ([item.title isEqualToString:@""]) {
42 | indexToInsert = idx;
43 | *stop = YES;
44 | }
45 | }];
46 |
47 | NSString *const title = [self twx_isBookmarksScreen] ? @"Remove from Bookmarks" : @"Add to Bookmarks...";
48 | NSMenuItem *const bookmarksMenuItem = [[NSMenuItem alloc] initWithTitle:title action:@selector(twx_didTapBookmarkMenuItem:) keyEquivalent:@"M"];
49 | bookmarksMenuItem.target = self;
50 | bookmarksMenuItem.representedObject = status;
51 | [menu insertItem:bookmarksMenuItem atIndex:indexToInsert];
52 | }
53 |
54 | #pragma mark - Convenience
55 |
56 | - (void)twx_didTapBookmarkMenuItem:(NSMenuItem *)menuItem {
57 | NSParameterAssert(menuItem.representedObject);
58 |
59 | TWXAPI *const api = [self twx_api];
60 | id const status = menuItem.representedObject;
61 | id __nullable const retweetedStatus = [status performSelector:@selector(retweetedStatus)];
62 | id const targetStatus = retweetedStatus ?: status;
63 | NSString *__nullable const statusID = [targetStatus performSelector:@selector(statusID)];
64 | if (!statusID) {
65 | return;
66 | }
67 |
68 | if (![self twx_isBookmarksScreen]) {
69 | [api addBookmarkForTweetWithIdentifier:statusID];
70 | return;
71 | }
72 |
73 | __weak typeof(self) weakSelf = self;
74 | [api removeBookmarkForTweetWithIdentifier:statusID completion:^(id _Nonnull request) {
75 | [weakSelf performSelector:@selector(twx_fetchBookmarks)];
76 | }];
77 | }
78 |
79 | - (BOOL)twx_isBookmarksScreen {
80 | NSViewController *const viewController = (NSViewController *)self;
81 | return [viewController.title isEqualToString:@"Bookmarks"];
82 | }
83 |
84 | - (TWXAPI *)twx_api {
85 | TWXAPI *__nullable const existingAPI = objc_getAssociatedObject(self, TwitterAPIRefKey);
86 | if (existingAPI) {
87 | return existingAPI;
88 | }
89 |
90 | TWXAPI *const newAPI = [[TWXAPI alloc] initWithTwitterAccount:[self performSelector:@selector(account)]];
91 | objc_setAssociatedObject(self, TwitterAPIRefKey, newAPI, OBJC_ASSOCIATION_RETAIN);
92 | return newAPI;
93 | }
94 |
95 | @end
96 |
97 | NS_ASSUME_NONNULL_END
98 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Utilities/TWXRuntime.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXRuntime.h"
13 |
14 | NS_ASSUME_NONNULL_BEGIN
15 |
16 | @implementation TWXRuntime
17 |
18 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString prefix:(nullable NSString *)prefix {
19 | NSParameterAssert(method);
20 | NSParameterAssert(classString);
21 |
22 | SEL const originalSelector = NSSelectorFromString(method);
23 | NSString* selectorName = [[classString stringByAppendingString:@"_"] stringByAppendingString:method];
24 | if (prefix) {
25 | selectorName = [[prefix stringByAppendingString:@"_"] stringByAppendingString:selectorName];
26 | }
27 | SEL const swizzledSelector = NSSelectorFromString(selectorName);
28 |
29 | Method const originalMethod = class_getInstanceMethod([classString twx_class], originalSelector);
30 | Method const swizzledMethod = class_getInstanceMethod([NSObject class], swizzledSelector);
31 | method_exchangeImplementations(originalMethod, swizzledMethod);
32 | }
33 |
34 | + (void)exchangeInstanceMethod:(NSString *)method ofClass:(NSString *)classString {
35 | [self exchangeInstanceMethod:method ofClass:classString prefix:nil];
36 | }
37 |
38 | + (void)exchangeClassMethod:(NSString *)method ofClass:(NSString *)classString {
39 | NSParameterAssert(method);
40 | NSParameterAssert(classString);
41 |
42 | SEL const originalSelector = NSSelectorFromString(method);
43 | SEL const swizzledSelector = NSSelectorFromString([[classString stringByAppendingString:@"_"] stringByAppendingString:method]);
44 |
45 | Method const originalMethod = class_getClassMethod([classString twx_class], originalSelector);
46 | Method const swizzledMethod = class_getClassMethod([NSObject class], swizzledSelector);
47 | method_exchangeImplementations(originalMethod, swizzledMethod);
48 | }
49 |
50 | @end
51 |
52 | @implementation NSString (TWXRuntime)
53 |
54 | - (Class)twx_class {
55 | return NSClassFromString(self);
56 | }
57 |
58 | @end
59 |
60 | @implementation NSObject (TWXRuntime)
61 |
62 | - (void)twx_performSelector:(SEL)selector value:(unsigned long long)value {
63 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
64 | invocation.target = self;
65 | invocation.selector = selector;
66 | [invocation setArgument:&value atIndex:2];
67 | [invocation invoke];
68 | }
69 |
70 | - (void)twx_performSelector:(SEL)selector withObject:(id)object value:(unsigned long long)value {
71 | NSParameterAssert(object);
72 |
73 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
74 | invocation.target = self;
75 | invocation.selector = selector;
76 | [invocation setArgument:&object atIndex:2];
77 | [invocation setArgument:&value atIndex:3];
78 | [invocation invoke];
79 |
80 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
81 | object;
82 | });
83 | }
84 |
85 | - (void)twx_invoke:(NSString*)method arg:(id)arg {
86 | SEL aSelector = NSSelectorFromString(method);
87 |
88 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:aSelector]];
89 |
90 | [invocation setTarget:self];
91 | [invocation setSelector:aSelector];
92 | [invocation setArgument:&arg atIndex:2];
93 |
94 | [invocation invoke];
95 | }
96 |
97 | - (id)twx_invokeAndReturnValue:(NSString*)method {
98 | SEL aSelector = NSSelectorFromString(method);
99 |
100 | id returnStruct;
101 |
102 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:aSelector]];
103 |
104 | [invocation setTarget:self];
105 | [invocation setSelector:aSelector];
106 |
107 | [invocation invoke];
108 | [invocation getReturnValue:&returnStruct];
109 |
110 | return returnStruct;
111 | }
112 |
113 | @end
114 |
115 | NS_ASSUME_NONNULL_END
116 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX.xcodeproj/xcshareddata/xcschemes/TwitterX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
70 |
74 |
75 |
76 |
82 |
83 |
84 |
85 |
89 |
90 |
91 |
92 |
93 |
94 |
100 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXRepliesCount.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import
13 | #import "TWXRepliesCount.h"
14 | #import "TWXRuntime.h"
15 | #import "NSView+TWX.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | static void *TwitterStatusRepliesCountKey = &TwitterStatusRepliesCountKey;
20 |
21 | @interface TWXRepliesTextField: NSTextField
22 |
23 | @end
24 |
25 | @implementation TWXRepliesTextField
26 |
27 | @end
28 |
29 | @implementation TWXRepliesCount
30 |
31 | + (void)loadFeature {
32 | [TWXRuntime exchangeInstanceMethod:@"v1_1_GET:parameters:callback:" ofClass:@"TwitterAPI"];
33 | [TWXRuntime exchangeClassMethod:@"statusWithDictionary:" ofClass:@"TwitterStatus"];
34 | [TWXRuntime exchangeInstanceMethod:@"setStatus:" ofClass:@"TMNewTimelineStatusCell"];
35 | }
36 |
37 | @end
38 |
39 | @implementation NSObject (TWX)
40 |
41 | - (void)TwitterAPI_v1_1_GET:(NSString *)endpoint parameters:(NSDictionary *)parameters callback:(id)callback {
42 | NSMutableDictionary *customParameters = [parameters mutableCopy];
43 |
44 | if ([self isKindOfClass:[@"TwitterAPI" twx_class]] && [endpoint rangeOfString:@"statuses/"].location != NSNotFound) {
45 | [customParameters setObject:@"iPhone-13" forKey:@"cards_platform"];
46 | [customParameters setObject:@1 forKey:@"include_reply_count"];
47 | }
48 |
49 | [self TwitterAPI_v1_1_GET:endpoint parameters:customParameters callback:callback];
50 | }
51 |
52 | + (nullable id)TwitterStatus_statusWithDictionary:(NSDictionary *)dictionary {
53 | id __nullable const status = [self TwitterStatus_statusWithDictionary:dictionary];
54 |
55 | if ([NSStringFromClass(self) isEqualToString:@"TwitterStatus"]) {
56 | objc_setAssociatedObject(status, TwitterStatusRepliesCountKey, dictionary[@"reply_count"], OBJC_ASSOCIATION_COPY);
57 | }
58 |
59 | return status;
60 | }
61 |
62 | - (void)TMNewTimelineStatusCell_setStatus:(id)status {
63 | [self TMNewTimelineStatusCell_setStatus:status];
64 |
65 | if ([self isKindOfClass:[@"TMNewTimelineStatusCell" twx_class]]) {
66 | NSView *const cell = (NSView *)self;
67 | NSTextField *__nullable const retweetsTextField = (id)[self performSelector:@selector(retweetTextField)];
68 | NSButton *__nullable const replyButton = (id)[self performSelector:@selector(replyButton)];
69 | NSTextField *__nullable repliesTextField;
70 | for (NSView *subview in cell.subviews) {
71 | if ([subview isKindOfClass:[TWXRepliesTextField class]]) {
72 | repliesTextField = (id)subview;
73 | break;
74 | }
75 | }
76 |
77 | if (repliesTextField == nil && replyButton) {
78 | repliesTextField = [[TWXRepliesTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)];
79 | repliesTextField.translatesAutoresizingMaskIntoConstraints = NO;
80 | repliesTextField.backgroundColor = [NSColor clearColor];
81 | repliesTextField.bordered = retweetsTextField.bordered;
82 | repliesTextField.bezeled = retweetsTextField.bezeled;
83 | repliesTextField.editable = retweetsTextField.editable;
84 | repliesTextField.selectable = retweetsTextField.selectable;
85 | repliesTextField.font = [NSFont systemFontOfSize:13.0f];
86 | repliesTextField.textColor = [[[@"TMTheme" twx_class] performSelector:@selector(currentTheme)] performSelector:@selector(buttonGrayIconColor)];
87 | [cell addSubview:repliesTextField];
88 |
89 | [repliesTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:replyButton fromAttribute:NSLayoutAttributeLeading];
90 | [repliesTextField anchorToAttribute:NSLayoutAttributeCenterY ofView:replyButton fromAttribute:NSLayoutAttributeCenterY];
91 | [repliesTextField setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
92 | [repliesTextField setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
93 | }
94 |
95 | NSNumber *__nullable const repliesCount = objc_getAssociatedObject(status, TwitterStatusRepliesCountKey) ?: @(0);
96 | repliesTextField.stringValue = repliesCount.integerValue > 0 ? repliesCount.stringValue : @"";
97 | }
98 | }
99 |
100 | @end
101 |
102 | NS_ASSUME_NONNULL_END
103 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX.xcodeproj/xcshareddata/xcschemes/LauncherX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
68 |
69 |
70 |
71 |
72 |
73 |
84 |
86 |
92 |
93 |
94 |
95 |
96 |
97 |
103 |
105 |
111 |
112 |
113 |
114 |
116 |
117 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Service/TWXAPI.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXAPI.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @interface TWXAPI ()
18 |
19 | @property (nonatomic, readonly, strong) id twitterAPI;
20 | @property (nonatomic, readonly, strong) id twitterAccount;
21 |
22 | @end
23 |
24 | @implementation TWXAPI
25 |
26 | - (instancetype)initWithTwitterAccount:(id)twitterAccount {
27 | NSParameterAssert(twitterAccount);
28 | NSParameterAssert([twitterAccount isKindOfClass:[@"TwitterAccount" twx_class]]);
29 |
30 | self = [super init];
31 |
32 | if (self) {
33 | self->_twitterAccount = twitterAccount;
34 | self->_twitterAPI = [[self class] createTwitterAPIWithAccount:twitterAccount];
35 | }
36 |
37 | return self;
38 | }
39 |
40 | #pragma mark - Public
41 |
42 | - (void)v1_1_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback {
43 | NSString *const apiRoot = @"https://api.twitter.com/1.1";
44 | [self requestTo:endpoint apiRoot:apiRoot method:method parameters:parameters multiPartFormData:nil persistent:NO callback:callback start:@"" excludingParameters:nil];
45 | }
46 |
47 | - (void)v2_requestTo:(NSString *)endpoint method:(NSString*)method parameters:(NSDictionary *)parameters callback:(void(^)(id))callback {
48 | NSString *const apiRoot = @"https://api.twitter.com/2";
49 | [self requestTo:endpoint apiRoot:apiRoot method:method parameters:parameters multiPartFormData:nil persistent:NO callback:callback start:@"" excludingParameters:nil];
50 | }
51 |
52 | #pragma mark - Private
53 |
54 | - (void)requestTo:(NSString *)endpoint apiRoot:(NSString *)apiRoot method:(NSString*)method parameters:(NSDictionary *)parameters multiPartFormData:(nullable NSData *)data
55 | persistent:(BOOL)persistent callback:(void(^)(id))callback start:(NSString *)start excludingParameters:(nullable NSDictionary *)excludingParameters {
56 | NSParameterAssert(endpoint && apiRoot && method && parameters && callback && start);
57 |
58 | const SEL requestSelector = @selector(request:apiRoot:method:parameters:multiPartFormData:persistent:callback:start:excludingParameters:);
59 | NSInvocation *const requestInvocation = [NSInvocation invocationWithMethodSignature:[self.twitterAPI methodSignatureForSelector:requestSelector]];
60 | requestInvocation.selector = requestSelector;
61 | requestInvocation.target = self.twitterAPI;
62 | [requestInvocation setArgument:&endpoint atIndex:2];
63 | [requestInvocation setArgument:&apiRoot atIndex:3];
64 | [requestInvocation setArgument:&method atIndex:4];
65 | [requestInvocation setArgument:¶meters atIndex:5];
66 | [requestInvocation setArgument:&data atIndex:6];
67 | [requestInvocation setArgument:&persistent atIndex:7];
68 | [requestInvocation setArgument:&callback atIndex:8];
69 | [requestInvocation setArgument:&start atIndex:9];
70 | [requestInvocation setArgument:&excludingParameters atIndex:10];
71 | [requestInvocation invoke];
72 |
73 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
74 | endpoint; apiRoot; method; parameters; data; start; excludingParameters;
75 | });
76 | }
77 |
78 | #pragma mark - Static
79 |
80 | + (nullable id)createTwitterAPIWithAccount:(id)twitterAccount {
81 | NSParameterAssert(twitterAccount);
82 |
83 | void *result;
84 |
85 | // Arguments
86 | NSString *apiRoot = @"https://api.twitter.com/2";
87 | void(^__nullable callback)(id) = nil;
88 |
89 | // Invocation
90 | id const allocatedAPI = [NSClassFromString(@"TwitterAPI") alloc];
91 | const SEL selector = @selector(initWithAccount:apiRoot:callback:);
92 | NSInvocation *const invocation = [NSInvocation invocationWithMethodSignature:[allocatedAPI methodSignatureForSelector:selector]];
93 | invocation.selector = selector;
94 | invocation.target = allocatedAPI;
95 | [invocation setArgument:&twitterAccount atIndex:2];
96 | [invocation setArgument:&apiRoot atIndex:3];
97 | [invocation setArgument:&callback atIndex:4];
98 | [invocation invoke];
99 | [invocation getReturnValue:&result];
100 |
101 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
102 | twitterAccount; apiRoot; callback;
103 | });
104 |
105 | return (__bridge NSObject *)result;
106 | }
107 |
108 | @end
109 |
110 | NS_ASSUME_NONNULL_END
111 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Service/TWXAPI+Bookmarks.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import "TWXAPI+Bookmarks.h"
13 | #import "TWXRuntime.h"
14 |
15 | NS_ASSUME_NONNULL_BEGIN
16 |
17 | @implementation TWXAPI (Bookmarks)
18 |
19 | - (void)fetchBookmarksWithCompletion:(void(^)(NSArray *))completion {
20 | NSParameterAssert(completion);
21 |
22 | NSDictionary *const parameters = [[self class] defaultParameters];
23 | [self v2_requestTo:@"timeline/bookmark.json" method:@"GET" parameters:parameters callback:^(id _Nonnull request) {
24 | NSData *__nullable const data = (id)[request performSelector:@selector(data)];
25 | if (!data) {
26 | completion(@[]);
27 | return;
28 | }
29 |
30 | NSError *parseError;
31 | NSDictionary *__nullable const jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&parseError];
32 | if (parseError || !jsonResponse) {
33 | completion(@[]);
34 | return;
35 | }
36 |
37 | NSMutableArray *const tweets = [NSMutableArray array];
38 | NSDictionary *__nullable const tweetsDictionaries = jsonResponse[@"globalObjects"][@"tweets"];
39 | NSDictionary *__nullable const userDictionaries = jsonResponse[@"globalObjects"][@"users"];
40 | NSArray *const sortedTweetIds = [[self class] sortedTweetIdentifiersForInJsonResponse:jsonResponse];
41 | for (NSString *const tweetId in sortedTweetIds) {
42 | NSMutableDictionary *__nullable const tweetDictionary = tweetsDictionaries[tweetId];
43 | if (!tweetDictionary) {
44 | continue;
45 | }
46 | NSMutableDictionary *const mutableTweetDictionary = [tweetDictionary mutableCopy];
47 | [mutableTweetDictionary setObject:userDictionaries[mutableTweetDictionary[@"user_id_str"]] ?: @{} forKey:@"user"];
48 | id const tweet = [[@"TwitterStatus" twx_class] performSelector:@selector(statusWithDictionary:) withObject:mutableTweetDictionary];
49 | if (tweet) {
50 | [tweets addObject:tweet];
51 | }
52 | }
53 |
54 | completion([tweets copy]);
55 | }];
56 | }
57 |
58 | - (void)addBookmarkForTweetWithIdentifier:(NSString *)identifier {
59 | NSMutableDictionary *const parameters = [[[self class] defaultParameters] mutableCopy];
60 | [parameters setObject:@(identifier.integerValue) forKey:@"tweet_id"];
61 | [self v1_1_requestTo:@"bookmark/entries/add.json" method:@"POST" parameters:parameters callback:^(id _Nonnull request) {
62 | [[NSNotificationCenter defaultCenter] postNotificationName:@"com.twitter.mac.TMSidebarItemViewControllerBookmarkNotification" object:nil];
63 | }];
64 | }
65 |
66 | - (void)removeBookmarkForTweetWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion {
67 | __weak typeof(self) weakSelf = self;
68 | [self fetchBookmarksWithCompletion:^(NSArray *_Nonnull tweets) {
69 | for (id const tweet in tweets) {
70 | NSString *const tweetID = [tweet performSelector:@selector(statusID)];
71 | if ([tweetID isEqualToString:identifier]) {
72 | [weakSelf _proceedDeletingBookmarkWithIdentifier:identifier completion:completion];
73 | return;
74 | }
75 | }
76 | }];
77 | }
78 |
79 | #pragma mark - Private
80 |
81 | - (void)_proceedDeletingBookmarkWithIdentifier:(NSString *)identifier completion:(void(^)(id))completion {
82 | NSMutableDictionary *const parameters = [[[self class] defaultParameters] mutableCopy];
83 | [parameters setObject:@(identifier.integerValue) forKey:@"tweet_id"];
84 | [self v1_1_requestTo:@"bookmark/entries/remove.json" method:@"POST" parameters:parameters callback:completion];
85 | }
86 |
87 | #pragma mark - Private static
88 |
89 | + (NSDictionary *)defaultParameters {
90 | return @{
91 | @"cards_platform": @"iPhone-13",
92 | @"contributor_details": @(1),
93 | @"include_cards": @(1),
94 | @"include_carousels": @(1),
95 | @"include_entities": @(1),
96 | @"include_ext_media_color": @"true",
97 | @"include_media_features": @"true",
98 | @"include_my_retweet": @"true",
99 | @"include_profile_interstitial_type": @"true",
100 | @"include_profile_location": @"true",
101 | @"include_reply_count": @(1),
102 | @"include_user_entities": @"true",
103 | @"include_user_hashtag_entities": @"true",
104 | @"include_user_mention_entities": @"true",
105 | @"include_user_symbol_entities": @"true",
106 | @"request_context": @"ptr",
107 | @"tweet_mode": @"extended"
108 | };
109 | }
110 |
111 | + (NSArray *)sortedTweetIdentifiersForInJsonResponse:(NSDictionary *)jsonResponse {
112 | NSParameterAssert(jsonResponse);
113 |
114 | NSArray *__nullable const instructionsArray = jsonResponse[@"timeline"][@"instructions"];
115 | if (!instructionsArray || instructionsArray.count == 0) {
116 | return @[];
117 | }
118 |
119 | NSMutableArray *const result = [NSMutableArray array];
120 | NSArray *const expectedEntries = instructionsArray[0][@"addEntries"][@"entries"] ?: @[];
121 | for (NSDictionary *const entry in expectedEntries) {
122 | NSString *const entryId = entry[@"entryId"];
123 | if ([entryId hasPrefix:@"tweet-"]) {
124 | NSString *const tweetId = [entryId stringByReplacingCharactersInRange:NSMakeRange(0, 6) withString:@""];
125 | [result addObject:tweetId];
126 | }
127 | }
128 |
129 | return result;
130 | }
131 |
132 | @end
133 |
134 | NS_ASSUME_NONNULL_END
135 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarksTimeline.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import
13 | #import "TWXBookmarksTimeline.h"
14 | #import "TWXRuntime.h"
15 | #import "TWXAPI+Bookmarks.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | static void *TWXBookmarksTimelineAPIRefKey = &TWXBookmarksTimelineAPIRefKey;
20 | static void *TWXBookmarksTimelineStatusesRefKey = &TWXBookmarksTimelineStatusesRefKey;
21 | static void *TWXBookmarksTimelineIsBookmarksScreenRefKey = &TWXBookmarksTimelineIsBookmarksScreenRefKey;
22 |
23 | @implementation TWXBookmarksTimeline
24 |
25 | + (void)loadFeature {
26 | [TWXRuntime exchangeInstanceMethod:@"viewDidLoad" ofClass:@"TWMHomeTimelineController"];
27 | [TWXRuntime exchangeInstanceMethod:@"viewDidAppear" ofClass:@"TWMHomeTimelineController"];
28 | [TWXRuntime exchangeInstanceMethod:@"viewWillDisappear" ofClass:@"TWMHomeTimelineController"];
29 | [TWXRuntime exchangeInstanceMethod:@"numberOfStatusCells" ofClass:@"TWMHomeTimelineController"];
30 | [TWXRuntime exchangeInstanceMethod:@"numberOfRowsInTableView:" ofClass:@"TWMHomeTimelineController"];
31 | [TWXRuntime exchangeInstanceMethod:@"statusForRow:" ofClass:@"TWMHomeTimelineController"];
32 | }
33 |
34 | @end
35 |
36 | @implementation NSObject (TWX)
37 |
38 | - (void)TWMHomeTimelineController_viewDidLoad {
39 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
40 | [self TWMHomeTimelineController_viewDidLoad];
41 | return;
42 | }
43 |
44 | [self TWMHomeTimelineController_viewDidLoad];
45 |
46 | for (NSString *const name in [self twx_undesiredNotificationNames]) {
47 | [[NSNotificationCenter defaultCenter] removeObserver:self name:name object:nil];
48 | }
49 |
50 | [self twx_fetchBookmarks];
51 | }
52 |
53 | - (void)TWMHomeTimelineController_viewDidAppear {
54 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
55 | [self TWMHomeTimelineController_viewDidAppear];
56 | return;
57 | }
58 |
59 | NSTableView *const tableView = [self performSelector:@selector(tableView)];
60 | tableView.delegate = (id)self;
61 | tableView.dataSource = (id)self;
62 | }
63 |
64 | - (void)TWMHomeTimelineController_viewWillDisappear {
65 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
66 | [self TWMHomeTimelineController_viewWillDisappear];
67 | return;
68 | }
69 | }
70 |
71 | - (NSInteger)TWMHomeTimelineController_numberOfStatusCells {
72 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
73 | return [self TWMHomeTimelineController_numberOfStatusCells];
74 | }
75 |
76 | return [self twx_statuses].count;
77 | }
78 |
79 | - (NSInteger)TWMHomeTimelineController_numberOfRowsInTableView:(NSTableView *)tableView {
80 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
81 | return [self TWMHomeTimelineController_numberOfRowsInTableView:tableView];
82 | }
83 |
84 | return [self twx_statuses].count;
85 | }
86 |
87 | - (id)TWMHomeTimelineController_statusForRow:(NSInteger)row {
88 | if (![self isKindOfClass:[@"TWMHomeTimelineController" twx_class]] || ![self twx_isBookmarksScreen]) {
89 | return [self TWMHomeTimelineController_statusForRow:row];
90 | }
91 |
92 | NSArray *const statuses = [self twx_statuses];
93 | if (statuses.count > row) {
94 | return statuses[row];
95 | }
96 |
97 | return nil;
98 | }
99 |
100 | #pragma mark - Convenience
101 |
102 | - (BOOL)twx_isBookmarksScreen {
103 | NSViewController *const viewController = (NSViewController *)self;
104 | return [viewController.title isEqualToString:@"Bookmarks"];
105 | }
106 |
107 | - (NSArray *)twx_statuses {
108 | return objc_getAssociatedObject(self, TWXBookmarksTimelineStatusesRefKey) ?: @[];
109 | }
110 |
111 | - (void)twx_fetchBookmarks {
112 | __weak typeof(self) weakSelf = self;
113 | [[self twx_api] fetchBookmarksWithCompletion:^(NSArray *_Nonnull statuses) {
114 | dispatch_async(dispatch_get_main_queue(), ^{
115 | objc_setAssociatedObject(weakSelf, TWXBookmarksTimelineStatusesRefKey, statuses, OBJC_ASSOCIATION_COPY);
116 | NSTableView *const tableView = [weakSelf performSelector:@selector(tableView)];
117 | [tableView reloadData];
118 |
119 | if (statuses.count > 0) {
120 | [weakSelf performSelector:@selector(showTableView)];
121 | return;
122 | }
123 |
124 | tableView.hidden = YES;
125 |
126 | NSTextField *const emptyLabel = [self performSelector:@selector(emptyLabel)];
127 | emptyLabel.stringValue = statuses.count == 0 ? @"You haven't added any Tweets to your Bookmarks yet" : @"";
128 | emptyLabel.hidden = statuses.count != 0;
129 |
130 | NSView *const loadingView = [self performSelector:@selector(loadingView)];
131 | loadingView.hidden = NO;
132 |
133 | NSView *const loadingIndicator = [self performSelector:@selector(loadingIndicator)];
134 | loadingIndicator.hidden = YES;
135 | });
136 | }];
137 | }
138 |
139 | - (TWXAPI *)twx_api {
140 | TWXAPI *__nullable const existingAPI = objc_getAssociatedObject(self, TWXBookmarksTimelineAPIRefKey);
141 | if (existingAPI) {
142 | return existingAPI;
143 | }
144 |
145 | TWXAPI *const newAPI = [[TWXAPI alloc] initWithTwitterAccount:[self performSelector:@selector(account)]];
146 | objc_setAssociatedObject(self, TWXBookmarksTimelineAPIRefKey, newAPI, OBJC_ASSOCIATION_RETAIN);
147 | return newAPI;
148 | }
149 |
150 | - (NSArray *)twx_undesiredNotificationNames {
151 | return @[
152 | @"com.twitter.mac.TWMHomeTimelineHasNewerTweetsNotification",
153 | @"com.twitter.mac.TWMHomeTimelineRateLimitNotification",
154 | @"com.twitter.mac.TWMHomeTimelineRateLimitExpiredNotification",
155 | @"com.twitter.mac.TWMHomeTimelineHasUpdatedTimelineActivityNotification",
156 | @"com.twitter.mac.TWMHomeTimelineTweetDeletedNotification"
157 | ];
158 | }
159 |
160 | @end
161 |
162 | NS_ASSUME_NONNULL_END
163 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXBookmarksSidebarItem.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import
13 | #import "TWXBookmarksSidebarItem.h"
14 | #import "TWXRuntime.h"
15 | #import "NSView+TWX.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | static void *TWXBookmarksSidebarItemBookmarksButtonRefKey = &TWXBookmarksSidebarItemBookmarksButtonRefKey;
20 |
21 | @implementation TWXBookmarksSidebarItem
22 |
23 | + (void)loadFeature {
24 | [TWXRuntime exchangeInstanceMethod:@"viewDidLoad" ofClass:@"TMSidebarItemViewController"];
25 | [TWXRuntime exchangeInstanceMethod:@"themeChanged:" ofClass:@"TMSidebarItemViewController"];
26 | [TWXRuntime exchangeInstanceMethod:@"toggleButtonStates:" ofClass:@"TMSidebarItemViewController"];
27 | [TWXRuntime exchangeInstanceMethod:@"jumpToAccount:item:animated:" ofClass:@"TMRootViewController"];
28 | }
29 |
30 | @end
31 |
32 | @implementation NSObject (TWX)
33 |
34 | - (void)TMSidebarItemViewController_viewDidLoad {
35 | [self TMSidebarItemViewController_viewDidLoad];
36 |
37 | NSButton *const directMessagesButton = [self performSelector:@selector(directMessagesButton)];
38 | NSButton *const profileButton = [self performSelector:@selector(profileButton)];
39 | NSView *const containerView = [profileButton superview];
40 |
41 | for (NSLayoutConstraint *const constraint in containerView.constraints) {
42 | if (constraint.secondItem == directMessagesButton && constraint.secondAttribute == NSLayoutAttributeBottom) {
43 | [containerView removeConstraint:constraint];
44 | break;
45 | }
46 | }
47 |
48 | NSLayoutConstraint *const heightConstraint = [self performSelector:@selector(heightConstraint)];
49 | heightConstraint.constant += 43.f;
50 |
51 | NSLayoutConstraint *const containerHeightConstraint = [self performSelector:@selector(buttonContainerHeightConstraint)];
52 | containerHeightConstraint.constant += 43.f;
53 |
54 | [self performSelector:@selector(setHeightConstraint:) withObject:nil];
55 |
56 | NSButton *const bookmarksButton = [self twx_bookmarksButton];
57 | [bookmarksButton performSelector:@selector(setColorsForStates:) withObject:[profileButton performSelector:@selector(colorsForStates)]];
58 | [containerView addSubview:bookmarksButton];
59 | [bookmarksButton anchorToAttribute:NSLayoutAttributeLeading ofView:containerView fromAttribute:NSLayoutAttributeLeading];
60 | [bookmarksButton anchorToAttribute:NSLayoutAttributeTrailing ofView:containerView fromAttribute:NSLayoutAttributeTrailing];
61 | [bookmarksButton anchorToAttribute:NSLayoutAttributeBottom ofView:directMessagesButton fromAttribute:NSLayoutAttributeTop constant:5.0f];
62 | [profileButton anchorToAttribute:NSLayoutAttributeBottom ofView:bookmarksButton fromAttribute:NSLayoutAttributeTop constant: 5.0f];
63 |
64 | [[NSNotificationCenter defaultCenter]
65 | addObserver:self
66 | selector:@selector(twx_didReceiveAddedToBookmarkNotification:)
67 | name:@"com.twitter.mac.TMSidebarItemViewControllerBookmarkNotification"
68 | object:nil];
69 | }
70 |
71 | - (void)TMSidebarItemViewController_themeChanged:(id)theme {
72 | if (![self isKindOfClass:[@"TMSidebarItemViewController" twx_class]]) {
73 | [self TMSidebarItemViewController_themeChanged:theme];
74 | return;
75 | }
76 |
77 | [self TMSidebarItemViewController_themeChanged:theme];
78 |
79 | NSButton *const directMessagesButton = [self performSelector:@selector(directMessagesButton)];
80 | NSButton *const bookmarksButton = [self twx_bookmarksButton];
81 | [bookmarksButton performSelector:@selector(setColorsForStates:) withObject:[directMessagesButton performSelector:@selector(colorsForStates)]];
82 | }
83 |
84 | - (void)TMSidebarItemViewController_toggleButtonStates:(NSInteger)states {
85 | [self TMSidebarItemViewController_toggleButtonStates:states];
86 | [[self twx_bookmarksButton] setState:NSControlStateValueOff];
87 | }
88 |
89 | - (void)TMRootViewController_jumpToAccount:(id)account item:(NSInteger)item animated:(BOOL)animated {
90 | if (![self isKindOfClass:[@"TMRootViewController" twx_class]] || item != 1000) {
91 | return [self TMRootViewController_jumpToAccount:account item:item animated:animated];
92 | }
93 |
94 | NSInteger const currentItem = (NSInteger)[self performSelector:@selector(currentItem)];
95 | if (currentItem == item) {
96 | return;
97 | }
98 |
99 | NSViewController *const rootViewController = (NSViewController *)self;
100 | id const appDelegate = [NSApp delegate];
101 | [appDelegate performSelector:@selector(setSelectedAccount:) withObject:account];
102 |
103 | NSViewController *bookmarksTimelineController = [[[@"TWMHomeTimelineController" twx_class] alloc] init];
104 | [bookmarksTimelineController performSelector:@selector(setAccount:) withObject:account];
105 | [bookmarksTimelineController performSelector:@selector(setTitle:) withObject:@"Bookmarks"];
106 |
107 | NSViewController *const navigationController = [rootViewController performSelector:@selector(navigationController)];
108 | [navigationController twx_performSelector:@selector(setRootViewController:animated:) withObject:bookmarksTimelineController value:animated];
109 |
110 | [rootViewController.view.window makeFirstResponder:bookmarksTimelineController.view];
111 | [rootViewController performSelector:@selector(setCurrentContentController:) withObject:bookmarksTimelineController];
112 | [rootViewController twx_performSelector:@selector(setCurrentItem:) value:item];
113 | }
114 |
115 | #pragma mark - Convenience
116 |
117 | - (NSButton *)twx_bookmarksButton {
118 | NSButton *__nullable const existingButton = objc_getAssociatedObject(self, TWXBookmarksSidebarItemBookmarksButtonRefKey);
119 | if (existingButton) {
120 | return existingButton;
121 | }
122 |
123 | NSButton *const newButton = [[[@"TMSidebarButton" twx_class] alloc] init];
124 | newButton.translatesAutoresizingMaskIntoConstraints = NO;
125 | [newButton performSelector:@selector(commonInit)];
126 | [newButton performSelector:@selector(setBaseImageName:) withObject:@"bookmarks"];
127 | [newButton setTarget:self];
128 | [newButton setAction:@selector(twx_switchToBookmarks:)];
129 | objc_setAssociatedObject(self, TWXBookmarksSidebarItemBookmarksButtonRefKey, newButton, OBJC_ASSOCIATION_RETAIN);
130 | return newButton;
131 | }
132 |
133 | - (void)twx_switchToBookmarks:(NSButton *)sender {
134 | NSArray *buttons = @[
135 | [self performSelector:@selector(homeButton)],
136 | [self performSelector:@selector(momentsButton)],
137 | [self performSelector:@selector(notificationsButton)],
138 | [self performSelector:@selector(directMessagesButton)],
139 | [self performSelector:@selector(listsButton)],
140 | [self performSelector:@selector(profileButton)],
141 | [self performSelector:@selector(searchButton)]
142 | ];
143 | [buttons enumerateObjectsUsingBlock:^(NSButton * _Nonnull button, NSUInteger idx, BOOL * _Nonnull stop) {
144 | [button setState:NSControlStateValueOff];
145 | }];
146 |
147 | NSButton *const bookmarksButton = [self twx_bookmarksButton];
148 | [bookmarksButton setState:NSControlStateValueOn];
149 | [bookmarksButton twx_performSelector:@selector(setUnreadItems:) value:0];
150 |
151 | NSInteger const bookmarksIndex = 1000;
152 | NSViewController *__nullable const parentViewController = ((NSViewController *)self).parentViewController;
153 | id const account = [self performSelector:@selector(account)];
154 | [parentViewController twx_performSelector:@selector(_internalJumpToAccount:item:) withObject:account value:bookmarksIndex];
155 | }
156 |
157 | - (void)twx_didReceiveAddedToBookmarkNotification:(NSNotification *)notification {
158 | dispatch_async(dispatch_get_main_queue(), ^{
159 | NSButton *const bookmarksButton = [self twx_bookmarksButton];
160 | if (bookmarksButton.state == NSControlStateValueOff) {
161 | [bookmarksButton twx_performSelector:@selector(setUnreadItems:) value:1];
162 | }
163 | });
164 | }
165 |
166 | @end
167 |
168 | NS_ASSUME_NONNULL_END
169 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Features/TWXConsumerKeysOverride.m:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT) - Copyright (c) 2018 Carlos Vidal
2 | //
3 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
8 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9 | // SOFTWARE.
10 |
11 | #import
12 | #import
13 | #import "TWXConsumerKeysOverride.h"
14 | #import "TWXRuntime.h"
15 | #import "NSView+TWX.h"
16 |
17 | NS_ASSUME_NONNULL_BEGIN
18 |
19 | NSString *const TWXOAuthConsumerKeyDefaultsKey = @"TWXOAuthConsumerKeyDefaults";
20 | NSString *const TWXOAuthConsumerSecretDefaultsKey = @"TWXOAuthConsumerSecretDefaults";
21 | static void *TWXConsumerTextFieldsDelegateKey = &TWXConsumerTextFieldsDelegateKey;
22 |
23 | @interface TWXConsumerTextFieldsDelegate: NSObject
24 |
25 | @end
26 |
27 | @implementation TWXConsumerKeysOverride
28 |
29 | + (void)loadFeature {
30 | [TWXRuntime exchangeClassMethod:@"userAgent" ofClass:@"TwitterAPI"];
31 | [TWXRuntime exchangeInstanceMethod:@"setValue:forHTTPHeaderField:" ofClass:@"ABHTTPRequest"];
32 | [TWXRuntime exchangeInstanceMethod:@"oAuthConsumerKey" ofClass:@"TwitterAccount"];
33 | [TWXRuntime exchangeInstanceMethod:@"oAuthConsumerSecret" ofClass:@"TwitterAccount"];
34 | [TWXRuntime exchangeInstanceMethod:@"setGeneralView:" ofClass:@"TweetiePreferencesWindowController"];
35 | [TWXRuntime exchangeInstanceMethod:@"windowDidLoad" ofClass:@"TMLoginWindowController"];
36 | [TWXRuntime exchangeInstanceMethod:@"worksWhenModal" ofClass:@"NSWindow"];
37 | }
38 |
39 | @end
40 |
41 | @implementation NSObject (TWX)
42 |
43 | + (NSString *)TwitterAPI_userAgent {
44 | return @"Twitter-iPhone/7.17.1 iOS/9.3.5 (Apple;iPhone4,1;;;;;1;2011)";
45 | }
46 |
47 | - (void)ABHTTPRequest_setValue:(id)value forHTTPHeaderField:(NSString *)field {
48 | if (![self isKindOfClass:[@"ABHTTPRequest" twx_class]]) {
49 | [self ABHTTPRequest_setValue:value forHTTPHeaderField:field];
50 | return;
51 | }
52 |
53 | id customValue = value;
54 |
55 | if ([field isEqualToString:@"X-Twitter-Client"]) {
56 | customValue = @"Twitter-iPhone";
57 | }
58 | else if ([field isEqualToString:@"X-Twitter-Client-Version"]) {
59 | customValue = @"7.17.1";
60 | [self ABHTTPRequest_setValue:@"5" forHTTPHeaderField:@"X-Twitter-API-Version"];
61 | }
62 |
63 | [self ABHTTPRequest_setValue:customValue forHTTPHeaderField:field];
64 | }
65 |
66 | - (NSString *)TwitterAccount_oAuthConsumerKey {
67 | if (![self isKindOfClass:[@"TwitterAccount" twx_class]]) {
68 | return [self TwitterAccount_oAuthConsumerKey];
69 | }
70 |
71 | return [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerKeyDefaultsKey] ?: [self TwitterAccount_oAuthConsumerKey];
72 | }
73 |
74 | - (NSString *)TwitterAccount_oAuthConsumerSecret {
75 | if (![self isKindOfClass:[@"TwitterAccount" twx_class]]) {
76 | return [self TwitterAccount_oAuthConsumerSecret];
77 | }
78 |
79 | return [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerSecretDefaultsKey] ?: [self TwitterAccount_oAuthConsumerSecret];
80 | }
81 |
82 | - (void)TweetiePreferencesWindowController_setGeneralView:(NSView *)view {
83 | if (![self isKindOfClass:[@"TweetiePreferencesWindowController" twx_class]]) {
84 | [self TweetiePreferencesWindowController_setGeneralView:view];
85 | return;
86 | }
87 |
88 | TWXConsumerTextFieldsDelegate *const delegate = [[TWXConsumerTextFieldsDelegate alloc] init];
89 | objc_setAssociatedObject(self, TWXConsumerTextFieldsDelegateKey, delegate, OBJC_ASSOCIATION_RETAIN);
90 |
91 | // Consumer Key TextField
92 | NSTextField *const consumerKeyTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)];
93 | consumerKeyTextField.translatesAutoresizingMaskIntoConstraints = NO;
94 | consumerKeyTextField.stringValue = [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerKeyDefaultsKey] ?: @"";
95 | consumerKeyTextField.tag = 0;
96 | consumerKeyTextField.delegate = delegate;
97 | [view addSubview:consumerKeyTextField];
98 |
99 | [consumerKeyTextField anchorDimenstionAttribute:NSLayoutAttributeWidth toConstant:185.f];
100 | [consumerKeyTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:view fromAttribute:NSLayoutAttributeTrailing constant:-94.f];
101 | [consumerKeyTextField anchorToAttribute:NSLayoutAttributeTop ofView:view fromAttribute:NSLayoutAttributeTop constant: 27.0];
102 |
103 | NSTextField *const consumerKeyLabel = [[self class] createLabel];
104 | consumerKeyLabel.translatesAutoresizingMaskIntoConstraints = NO;
105 | consumerKeyLabel.stringValue = @"OAuth consumer key:";
106 | consumerKeyLabel.alignment = NSRightTextAlignment;
107 | [view addSubview:consumerKeyLabel];
108 |
109 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLeading ofView:view fromAttribute:NSLayoutAttributeLeading constant:20.f];
110 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeTrailing constant:-9.f];
111 | [consumerKeyLabel anchorToAttribute:NSLayoutAttributeLastBaseline ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeLastBaseline];
112 |
113 | // Consumer Secret TextField
114 | NSTextField *const consumerSecretTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)];
115 | consumerSecretTextField.translatesAutoresizingMaskIntoConstraints = NO;
116 | consumerSecretTextField.stringValue = [[NSUserDefaults standardUserDefaults] stringForKey:TWXOAuthConsumerSecretDefaultsKey] ?: @"";
117 | consumerSecretTextField.tag = 1;
118 | consumerSecretTextField.delegate = delegate;
119 | [view addSubview:consumerSecretTextField];
120 |
121 | [consumerSecretTextField anchorDimenstionAttribute:NSLayoutAttributeWidth toConstant:185.f];
122 | [consumerSecretTextField anchorToAttribute:NSLayoutAttributeTrailing ofView:view fromAttribute:NSLayoutAttributeTrailing constant:-94.f];
123 | [consumerSecretTextField anchorToAttribute:NSLayoutAttributeBottom ofView:consumerKeyTextField fromAttribute:NSLayoutAttributeTop constant:10.f];
124 |
125 | NSTextField *const consumerSecretLabel = [[self class] createLabel];
126 | consumerSecretLabel.translatesAutoresizingMaskIntoConstraints = NO;
127 | consumerSecretLabel.stringValue = @"OAuth consumer secret:";
128 | consumerSecretLabel.alignment = NSRightTextAlignment;
129 | [view addSubview:consumerSecretLabel];
130 |
131 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLeading ofView:view fromAttribute:NSLayoutAttributeLeading constant:20.f];
132 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTrailing constant:-9.f];
133 | [consumerSecretLabel anchorToAttribute:NSLayoutAttributeLastBaseline ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeLastBaseline];
134 |
135 | // Info
136 | NSTextField *const infoLabel = [[self class] createLabel];
137 | infoLabel.translatesAutoresizingMaskIntoConstraints = NO;
138 | infoLabel.stringValue = @"(requires re-authentication if changed)";
139 | infoLabel.font = [NSFont systemFontOfSize:9.f];
140 | infoLabel.alphaValue = 0.48f;
141 | [view addSubview:infoLabel];
142 |
143 | [infoLabel anchorToAttribute:NSLayoutAttributeLeading ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeLeading];
144 | [infoLabel anchorToAttribute:NSLayoutAttributeTrailing ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTrailing];
145 | [infoLabel anchorToAttribute:NSLayoutAttributeBottom ofView:consumerSecretTextField fromAttribute:NSLayoutAttributeTop constant:2.f];
146 |
147 | // Anchor existing content
148 | view.frame = NSMakeRect(0.f, 0.f, 481.f, 444.f);
149 | for (NSLayoutConstraint *constraint in view.constraints) {
150 | if ([constraint.firstItem isKindOfClass:[NSPopUpButton class]] && constraint.firstAttribute == NSLayoutAttributeTop && constraint.secondItem == view) {
151 | [view removeConstraint:constraint];
152 | [constraint.firstItem anchorToAttribute:NSLayoutAttributeBottom ofView:infoLabel fromAttribute:NSLayoutAttributeTop constant:20.f];
153 | break;
154 | }
155 | }
156 |
157 | [self TweetiePreferencesWindowController_setGeneralView:view];
158 | }
159 |
160 | - (BOOL)NSWindow_worksWhenModal {
161 | return YES;
162 | }
163 |
164 | - (void)TMLoginWindowController_windowDidLoad {
165 | [self TMLoginWindowController_windowDidLoad];
166 | if ([self isKindOfClass:[@"TMLoginWindowController" twx_class]]) {
167 | [[NSApp delegate] performSelector:@selector(preferences:) withObject:nil];
168 | }
169 | }
170 |
171 | #pragma mark - Static
172 |
173 | + (NSTextField *)createLabel {
174 | NSTextField *const label = [[NSTextField alloc] initWithFrame:NSMakeRect(0.f, 0.f, 0.f, 0.f)];
175 | label.font = [NSFont systemFontOfSize:13.f];
176 | label.drawsBackground = NO;
177 | label.selectable = NO;
178 | label.bordered = NO;
179 | label.editable = NO;
180 | label.bezeled = NO;
181 | return label;
182 | }
183 |
184 | @end
185 |
186 | @implementation TWXConsumerTextFieldsDelegate
187 |
188 | - (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
189 | NSString *const defaultsKey = control.tag == 0 ? TWXOAuthConsumerKeyDefaultsKey : TWXOAuthConsumerSecretDefaultsKey;
190 | [[NSUserDefaults standardUserDefaults] setObject:fieldEditor.string forKey:defaultsKey];
191 | return YES;
192 | }
193 |
194 | @end
195 |
196 | NS_ASSUME_NONNULL_END
197 |
--------------------------------------------------------------------------------
/LauncherX/LauncherX.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C1A59BD420542686003189A1 /* TwitterX.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A59BD320542686003189A1 /* TwitterX.swift */; };
11 | C1DEB0332053195700137EAA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1DEB0322053195700137EAA /* AppDelegate.swift */; };
12 | C1DEB0352053195700137EAA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB0342053195700137EAA /* Assets.xcassets */; };
13 | C1DEB0382053195700137EAA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB0362053195700137EAA /* MainMenu.xib */; };
14 | C1DEB049205325B100137EAA /* TwitterX.framework in Resources */ = {isa = PBXBuildFile; fileRef = C1DEB046205325A700137EAA /* TwitterX.framework */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXCopyFilesBuildPhase section */
18 | C1DEB0442053256C00137EAA /* Embed Frameworks */ = {
19 | isa = PBXCopyFilesBuildPhase;
20 | buildActionMask = 2147483647;
21 | dstPath = "";
22 | dstSubfolderSpec = 10;
23 | files = (
24 | );
25 | name = "Embed Frameworks";
26 | runOnlyForDeploymentPostprocessing = 0;
27 | };
28 | /* End PBXCopyFilesBuildPhase section */
29 |
30 | /* Begin PBXFileReference section */
31 | C1A59BD320542686003189A1 /* TwitterX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterX.swift; sourceTree = ""; };
32 | C1DEB02F2053195700137EAA /* LauncherX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LauncherX.app; sourceTree = BUILT_PRODUCTS_DIR; };
33 | C1DEB0322053195700137EAA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
34 | C1DEB0342053195700137EAA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
35 | C1DEB0372053195700137EAA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
36 | C1DEB0392053195700137EAA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
37 | C1DEB03A2053195700137EAA /* LauncherX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LauncherX.entitlements; sourceTree = ""; };
38 | C1DEB046205325A700137EAA /* TwitterX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TwitterX.framework; sourceTree = BUILT_PRODUCTS_DIR; };
39 | /* End PBXFileReference section */
40 |
41 | /* Begin PBXFrameworksBuildPhase section */
42 | C1DEB02C2053195700137EAA /* Frameworks */ = {
43 | isa = PBXFrameworksBuildPhase;
44 | buildActionMask = 2147483647;
45 | files = (
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXFrameworksBuildPhase section */
50 |
51 | /* Begin PBXGroup section */
52 | C1DEB0262053195700137EAA = {
53 | isa = PBXGroup;
54 | children = (
55 | C1DEB046205325A700137EAA /* TwitterX.framework */,
56 | C1DEB0312053195700137EAA /* LauncherX */,
57 | C1DEB0302053195700137EAA /* Products */,
58 | );
59 | sourceTree = "";
60 | };
61 | C1DEB0302053195700137EAA /* Products */ = {
62 | isa = PBXGroup;
63 | children = (
64 | C1DEB02F2053195700137EAA /* LauncherX.app */,
65 | );
66 | name = Products;
67 | sourceTree = "";
68 | };
69 | C1DEB0312053195700137EAA /* LauncherX */ = {
70 | isa = PBXGroup;
71 | children = (
72 | C1A59BD320542686003189A1 /* TwitterX.swift */,
73 | C1DEB0322053195700137EAA /* AppDelegate.swift */,
74 | C1DEB0342053195700137EAA /* Assets.xcassets */,
75 | C1DEB0362053195700137EAA /* MainMenu.xib */,
76 | C1DEB0392053195700137EAA /* Info.plist */,
77 | C1DEB03A2053195700137EAA /* LauncherX.entitlements */,
78 | );
79 | path = LauncherX;
80 | sourceTree = "";
81 | };
82 | /* End PBXGroup section */
83 |
84 | /* Begin PBXNativeTarget section */
85 | C1DEB02E2053195700137EAA /* LauncherX */ = {
86 | isa = PBXNativeTarget;
87 | buildConfigurationList = C1DEB03D2053195700137EAA /* Build configuration list for PBXNativeTarget "LauncherX" */;
88 | buildPhases = (
89 | C1DEB02B2053195700137EAA /* Sources */,
90 | C1DEB02C2053195700137EAA /* Frameworks */,
91 | C1DEB02D2053195700137EAA /* Resources */,
92 | C1DEB0442053256C00137EAA /* Embed Frameworks */,
93 | );
94 | buildRules = (
95 | );
96 | dependencies = (
97 | );
98 | name = LauncherX;
99 | productName = LauncherX;
100 | productReference = C1DEB02F2053195700137EAA /* LauncherX.app */;
101 | productType = "com.apple.product-type.application";
102 | };
103 | /* End PBXNativeTarget section */
104 |
105 | /* Begin PBXProject section */
106 | C1DEB0272053195700137EAA /* Project object */ = {
107 | isa = PBXProject;
108 | attributes = {
109 | LastSwiftUpdateCheck = 0920;
110 | LastUpgradeCheck = 0920;
111 | ORGANIZATIONNAME = nakioStudio;
112 | TargetAttributes = {
113 | C1DEB02E2053195700137EAA = {
114 | CreatedOnToolsVersion = 9.2;
115 | ProvisioningStyle = Automatic;
116 | SystemCapabilities = {
117 | com.apple.Sandbox = {
118 | enabled = 1;
119 | };
120 | };
121 | };
122 | };
123 | };
124 | buildConfigurationList = C1DEB02A2053195700137EAA /* Build configuration list for PBXProject "LauncherX" */;
125 | compatibilityVersion = "Xcode 8.0";
126 | developmentRegion = en;
127 | hasScannedForEncodings = 0;
128 | knownRegions = (
129 | en,
130 | Base,
131 | );
132 | mainGroup = C1DEB0262053195700137EAA;
133 | productRefGroup = C1DEB0302053195700137EAA /* Products */;
134 | projectDirPath = "";
135 | projectRoot = "";
136 | targets = (
137 | C1DEB02E2053195700137EAA /* LauncherX */,
138 | );
139 | };
140 | /* End PBXProject section */
141 |
142 | /* Begin PBXResourcesBuildPhase section */
143 | C1DEB02D2053195700137EAA /* Resources */ = {
144 | isa = PBXResourcesBuildPhase;
145 | buildActionMask = 2147483647;
146 | files = (
147 | C1DEB049205325B100137EAA /* TwitterX.framework in Resources */,
148 | C1DEB0352053195700137EAA /* Assets.xcassets in Resources */,
149 | C1DEB0382053195700137EAA /* MainMenu.xib in Resources */,
150 | );
151 | runOnlyForDeploymentPostprocessing = 0;
152 | };
153 | /* End PBXResourcesBuildPhase section */
154 |
155 | /* Begin PBXSourcesBuildPhase section */
156 | C1DEB02B2053195700137EAA /* Sources */ = {
157 | isa = PBXSourcesBuildPhase;
158 | buildActionMask = 2147483647;
159 | files = (
160 | C1A59BD420542686003189A1 /* TwitterX.swift in Sources */,
161 | C1DEB0332053195700137EAA /* AppDelegate.swift in Sources */,
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXSourcesBuildPhase section */
166 |
167 | /* Begin PBXVariantGroup section */
168 | C1DEB0362053195700137EAA /* MainMenu.xib */ = {
169 | isa = PBXVariantGroup;
170 | children = (
171 | C1DEB0372053195700137EAA /* Base */,
172 | );
173 | name = MainMenu.xib;
174 | sourceTree = "";
175 | };
176 | /* End PBXVariantGroup section */
177 |
178 | /* Begin XCBuildConfiguration section */
179 | C1DEB03B2053195700137EAA /* Debug */ = {
180 | isa = XCBuildConfiguration;
181 | buildSettings = {
182 | ALWAYS_SEARCH_USER_PATHS = NO;
183 | CLANG_ANALYZER_NONNULL = YES;
184 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
186 | CLANG_CXX_LIBRARY = "libc++";
187 | CLANG_ENABLE_MODULES = YES;
188 | CLANG_ENABLE_OBJC_ARC = YES;
189 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
190 | CLANG_WARN_BOOL_CONVERSION = YES;
191 | CLANG_WARN_COMMA = YES;
192 | CLANG_WARN_CONSTANT_CONVERSION = YES;
193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
194 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
195 | CLANG_WARN_EMPTY_BODY = YES;
196 | CLANG_WARN_ENUM_CONVERSION = YES;
197 | CLANG_WARN_INFINITE_RECURSION = YES;
198 | CLANG_WARN_INT_CONVERSION = YES;
199 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
200 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
202 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
203 | CLANG_WARN_STRICT_PROTOTYPES = YES;
204 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
205 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
206 | CLANG_WARN_UNREACHABLE_CODE = YES;
207 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
208 | CODE_SIGN_IDENTITY = "-";
209 | COPY_PHASE_STRIP = NO;
210 | DEBUG_INFORMATION_FORMAT = dwarf;
211 | ENABLE_STRICT_OBJC_MSGSEND = YES;
212 | ENABLE_TESTABILITY = YES;
213 | GCC_C_LANGUAGE_STANDARD = gnu11;
214 | GCC_DYNAMIC_NO_PIC = NO;
215 | GCC_NO_COMMON_BLOCKS = YES;
216 | GCC_OPTIMIZATION_LEVEL = 0;
217 | GCC_PREPROCESSOR_DEFINITIONS = (
218 | "DEBUG=1",
219 | "$(inherited)",
220 | );
221 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
222 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
223 | GCC_WARN_UNDECLARED_SELECTOR = YES;
224 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
225 | GCC_WARN_UNUSED_FUNCTION = YES;
226 | GCC_WARN_UNUSED_VARIABLE = YES;
227 | MACOSX_DEPLOYMENT_TARGET = 10.10;
228 | MTL_ENABLE_DEBUG_INFO = YES;
229 | ONLY_ACTIVE_ARCH = YES;
230 | SDKROOT = macosx;
231 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
232 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
233 | };
234 | name = Debug;
235 | };
236 | C1DEB03C2053195700137EAA /* Release */ = {
237 | isa = XCBuildConfiguration;
238 | buildSettings = {
239 | ALWAYS_SEARCH_USER_PATHS = NO;
240 | CLANG_ANALYZER_NONNULL = YES;
241 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
243 | CLANG_CXX_LIBRARY = "libc++";
244 | CLANG_ENABLE_MODULES = YES;
245 | CLANG_ENABLE_OBJC_ARC = YES;
246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
247 | CLANG_WARN_BOOL_CONVERSION = YES;
248 | CLANG_WARN_COMMA = YES;
249 | CLANG_WARN_CONSTANT_CONVERSION = YES;
250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
251 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
252 | CLANG_WARN_EMPTY_BODY = YES;
253 | CLANG_WARN_ENUM_CONVERSION = YES;
254 | CLANG_WARN_INFINITE_RECURSION = YES;
255 | CLANG_WARN_INT_CONVERSION = YES;
256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
257 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
258 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
259 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
260 | CLANG_WARN_STRICT_PROTOTYPES = YES;
261 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
262 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | CODE_SIGN_IDENTITY = "-";
266 | COPY_PHASE_STRIP = NO;
267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
268 | ENABLE_NS_ASSERTIONS = NO;
269 | ENABLE_STRICT_OBJC_MSGSEND = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu11;
271 | GCC_NO_COMMON_BLOCKS = YES;
272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
274 | GCC_WARN_UNDECLARED_SELECTOR = YES;
275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
276 | GCC_WARN_UNUSED_FUNCTION = YES;
277 | GCC_WARN_UNUSED_VARIABLE = YES;
278 | MACOSX_DEPLOYMENT_TARGET = 10.10;
279 | MTL_ENABLE_DEBUG_INFO = NO;
280 | SDKROOT = macosx;
281 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
282 | };
283 | name = Release;
284 | };
285 | C1DEB03E2053195700137EAA /* Debug */ = {
286 | isa = XCBuildConfiguration;
287 | buildSettings = {
288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
289 | CODE_SIGN_ENTITLEMENTS = LauncherX/LauncherX.entitlements;
290 | CODE_SIGN_STYLE = Automatic;
291 | COMBINE_HIDPI_IMAGES = YES;
292 | INFOPLIST_FILE = LauncherX/Info.plist;
293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
294 | MACOSX_DEPLOYMENT_TARGET = 10.10;
295 | PRODUCT_BUNDLE_IDENTIFIER = com.nakioStudio.LauncherX;
296 | PRODUCT_NAME = "$(TARGET_NAME)";
297 | SWIFT_VERSION = 4.0;
298 | };
299 | name = Debug;
300 | };
301 | C1DEB03F2053195700137EAA /* Release */ = {
302 | isa = XCBuildConfiguration;
303 | buildSettings = {
304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
305 | CODE_SIGN_ENTITLEMENTS = LauncherX/LauncherX.entitlements;
306 | CODE_SIGN_STYLE = Automatic;
307 | COMBINE_HIDPI_IMAGES = YES;
308 | INFOPLIST_FILE = LauncherX/Info.plist;
309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
310 | MACOSX_DEPLOYMENT_TARGET = 10.10;
311 | PRODUCT_BUNDLE_IDENTIFIER = com.nakioStudio.LauncherX;
312 | PRODUCT_NAME = "$(TARGET_NAME)";
313 | SWIFT_VERSION = 4.0;
314 | };
315 | name = Release;
316 | };
317 | /* End XCBuildConfiguration section */
318 |
319 | /* Begin XCConfigurationList section */
320 | C1DEB02A2053195700137EAA /* Build configuration list for PBXProject "LauncherX" */ = {
321 | isa = XCConfigurationList;
322 | buildConfigurations = (
323 | C1DEB03B2053195700137EAA /* Debug */,
324 | C1DEB03C2053195700137EAA /* Release */,
325 | );
326 | defaultConfigurationIsVisible = 0;
327 | defaultConfigurationName = Release;
328 | };
329 | C1DEB03D2053195700137EAA /* Build configuration list for PBXNativeTarget "LauncherX" */ = {
330 | isa = XCConfigurationList;
331 | buildConfigurations = (
332 | C1DEB03E2053195700137EAA /* Debug */,
333 | C1DEB03F2053195700137EAA /* Release */,
334 | );
335 | defaultConfigurationIsVisible = 0;
336 | defaultConfigurationName = Release;
337 | };
338 | /* End XCConfigurationList section */
339 | };
340 | rootObject = C1DEB0272053195700137EAA /* Project object */;
341 | }
342 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX/Theme/TWXDuskTheme.m:
--------------------------------------------------------------------------------
1 | //
2 | // TWXDuskheme.m
3 | // TwitterX
4 | //
5 | // Created by Tomoya Hirano on 2018/04/07.
6 | // Copyright © 2018年 twitterx. All rights reserved.
7 | //
8 |
9 | #import "TWXDuskTheme.h"
10 | #import
11 | #import "NSColor+TWX.h"
12 |
13 | @implementation TWXDuskTheme
14 | + (void)load{
15 |
16 | }
17 | + (id)themeImageFilenameIdentifier{
18 | return @"";
19 | }
20 | + (id)themeIdentifier{
21 | return @"com.twitter.twitter.themeIdentifiers.duskTheme";
22 | }
23 | - (id)imageNamed:(NSString *)name{
24 | NSLog(@"%@", name);
25 | return [NSImage imageNamed:name];
26 | }
27 | - (id)_themedImageNameForImageName:(id)arg1{
28 | return @"";
29 | }
30 | - (id)inactiveBadgeBackgroundColor{
31 | return [NSColor colorFromHexString:@"#1F2028"];
32 | }
33 | - (id)liveBadgeBackgroundColor{
34 | return [NSColor colorFromHexString:@"#1F2028"];
35 | }
36 | - (id)appearance {
37 | return nil;
38 | }
39 | - (id)videoPlayerPlaceholderColor{
40 | return [NSColor whiteColor];
41 | }
42 | - (id)GIFSearchRed{
43 | return [NSColor whiteColor];
44 | }
45 | - (id)GIFSearchOrange{
46 | return [NSColor whiteColor];
47 | }
48 | - (id)GIFSearchYellow{
49 | return [NSColor whiteColor];
50 | }
51 | - (id)GIFSearchBlue{
52 | return [NSColor whiteColor];
53 | }
54 | - (id)GIFSearchGreen{
55 | return [NSColor whiteColor];
56 | }
57 | - (id)GIFSearchPurple{
58 | return [NSColor whiteColor];
59 | }
60 | - (id)messageMineSelectedTextBackgroundColor{
61 | return [NSColor colorFromHexString:@"#1F2028"];
62 | }
63 | - (id)selectedTextBackgroundColor{
64 | return [NSColor colorFromHexString:@"#1F2028"];
65 | }
66 | - (id)copyrightTextColor{
67 | return [NSColor whiteColor];
68 | }
69 | - (id)versionTextColor{
70 | return [NSColor whiteColor];
71 | }
72 | - (id)logoColor{
73 | return [NSColor colorFromHexString:@"#616168"];
74 | }
75 | - (id)aboutBackgroundColor{
76 | return [NSColor colorFromHexString:@"#1F2028"];
77 | }
78 | - (id)momentsBlurColor{
79 | return [NSColor colorFromHexString:@"#2D2D2E"];
80 | }
81 | - (id)momentCoverSubtextColor{
82 | return [NSColor colorFromHexString:@"#FFFFFF"];
83 | }
84 | - (id)momentCoverTitleTextColor{
85 | return [NSColor colorFromHexString:@"#FFFFFF"];
86 | }
87 | - (id)momentCoverImageOverlayColor{
88 | return [[NSColor colorFromHexString:@"#2D2D2E"] colorWithAlphaComponent:0.5];
89 | }
90 | - (id)momentHeroTimeTextColor{
91 | return [NSColor colorFromHexString:@"#616168"];
92 | }
93 | - (id)momentHeroTitleTextColor{
94 | return [NSColor colorFromHexString:@"#FFFFFF"];
95 | }
96 | - (id)momentHeroSubcategoryTextColor{
97 | return [NSColor colorFromHexString:@"#FFFFFF"];
98 | }
99 | - (id)momentTimeTextColor{
100 | return [NSColor colorFromHexString:@"#616168"];
101 | }
102 | - (id)momentTitleTextColor{
103 | return [NSColor colorFromHexString:@"#FFFFFF"];
104 | }
105 | - (id)momentSubcategoryTextColor{
106 | return [NSColor colorFromHexString:@"#7CC155"];
107 | }
108 | - (id)momentsGuideNavigationBackgroundColor{
109 | return [NSColor colorFromHexString:@"#1F2028"];
110 | }
111 | - (id)momentsNavigationActiveColor{
112 | return [NSColor colorFromHexString:@"#FFFFFF"];
113 | }
114 | - (id)momentsNavigationColor{
115 | return [NSColor colorFromHexString:@"#FFFFFF"];
116 | }
117 | - (id)popoverSeparatorColor{
118 | return [NSColor colorFromHexString:@"#2D2D2E"];
119 | }
120 | - (id)popoverRemoveTextColorForState:(long long)arg1{
121 | return [NSColor whiteColor];
122 | }
123 | - (id)popoverRemoveTextColor{
124 | return [NSColor whiteColor];
125 | }
126 | - (id)popoverActionTextColorForState:(long long)arg1{
127 | return [NSColor whiteColor];
128 | }
129 | - (id)popoverActionTextColor{
130 | return [NSColor whiteColor];
131 | }
132 | - (id)popoverWarningTextColor{
133 | return [NSColor whiteColor];
134 | }
135 | - (id)popoverSubheadTextColor{
136 | return [NSColor whiteColor];
137 | }
138 | - (id)popoverTextColor{
139 | return [NSColor whiteColor];
140 | }
141 | - (id)searchSuggestionsOverlayBackgroundColor{
142 | return [NSColor colorFromHexString:@"#1F2028"];
143 | }
144 | - (id)searchFieldBackgroundColorForState:(long long)arg1{
145 | return [NSColor colorFromHexString:@"#1F2028"];
146 | }
147 | - (id)searchFieldBackgroundColor{
148 | return [NSColor colorFromHexString:@"#1F2028"];
149 | }
150 | - (id)trendNeutralColor{
151 | return [NSColor whiteColor];
152 | }
153 | - (id)trendDownColor{
154 | return [NSColor whiteColor];
155 | }
156 | - (id)trendUpColor{
157 | return [NSColor whiteColor];
158 | }
159 | - (id)trendColor{
160 | return [NSColor whiteColor];
161 | }
162 | - (id)savedSearchIconColor{
163 | return [NSColor whiteColor];
164 | }
165 | - (id)searchFilterAndClearIconColor{
166 | return [NSColor whiteColor];
167 | }
168 | - (id)searchScopeIconColor{
169 | return [NSColor whiteColor];
170 | }
171 | - (id)searchPlaceholderTextColor{
172 | return [NSColor whiteColor];
173 | }
174 | - (id)searchTextColor{
175 | return [NSColor whiteColor];
176 | }
177 | - (id)searchIconColor{
178 | return [NSColor whiteColor];
179 | }
180 | - (id)settingsToggleThumbColor{
181 | return [NSColor whiteColor];
182 | }
183 | - (id)settingsToggleOffColorForState:(long long)arg1{
184 | return [NSColor whiteColor];
185 | }
186 | - (id)settingsToggleOffColor{
187 | return [NSColor whiteColor];
188 | }
189 | - (id)settingsToggleOnColorForState:(long long)arg1{
190 | return [NSColor whiteColor];
191 | }
192 | - (id)settingsToggleOnColor{
193 | return [NSColor whiteColor];
194 | }
195 | - (id)profileSecondaryTextColor{
196 | return [NSColor colorFromHexString:@"#616168"];
197 | }
198 | - (id)profilePrimaryTextColor{
199 | return [NSColor colorFromHexString:@"#FFFFFF"];
200 | }
201 | - (id)profileBackButtonColorForState:(long long)arg1{
202 | return [NSColor colorFromHexString:@"#616168"];
203 | }
204 | - (id)profileBackButtonColor{
205 | return [NSColor colorFromHexString:@"#616168"];
206 | }
207 | - (id)followerRequestsCountBackgroundColor{
208 | return [NSColor colorFromHexString:@"#1F2028"];
209 | }
210 | - (id)followerRequestsCountTextColor{
211 | return [NSColor whiteColor];
212 | }
213 | - (id)profileProtectedIconColor{
214 | return [NSColor whiteColor];
215 | }
216 | - (id)profileProtectedBackgroundColor{
217 | return [NSColor colorFromHexString:@"#1F2028"];
218 | }
219 | - (id)profileProtectedTextColor{
220 | return [NSColor whiteColor];
221 | }
222 | - (id)profileAvatarBorderColor{
223 | return [NSColor colorFromHexString:@"#2D2D2E"];
224 | }
225 | - (id)profileAvatarPlaceholderColor{
226 | return [NSColor whiteColor];
227 | }
228 | - (id)profileBannerPlaceholdercolor{
229 | return [NSColor whiteColor];
230 | }
231 | - (id)profileTabBackgroundColorForState:(long long)arg1{
232 | return [NSColor colorFromHexString:@"#1F2028"];
233 | }
234 | - (id)profileTabBackgroundColor{
235 | return [NSColor colorFromHexString:@"#1F2028"];
236 | }
237 | - (id)profileTabSelectedBackgroundColorForState:(long long)arg1{
238 | return [NSColor colorFromHexString:@"#1F2028"];
239 | }
240 | - (id)profileTabSelectedBackgroundColor{
241 | return [NSColor colorFromHexString:@"#1F2028"];
242 | }
243 | - (id)profileTabSelectedTextColorForState:(long long)arg1{
244 | return [NSColor whiteColor];
245 | }
246 | - (id)profileTabSelectedTextColor{
247 | return [NSColor whiteColor];
248 | }
249 | - (id)profileTabTextColorForState:(long long)arg1{
250 | return [NSColor whiteColor];
251 | }
252 | - (id)profileTabTextColor{
253 | return [NSColor whiteColor];
254 | }
255 | - (id)profileTabBackgroundRuleColor{
256 | return [NSColor whiteColor];
257 | }
258 | - (id)profileTabDividerColor{
259 | return [NSColor colorFromHexString:@"#616168"];
260 | }
261 | - (id)profileFollowersCountColor{
262 | return [NSColor whiteColor];
263 | }
264 | - (id)profileFollowersLabelColor{
265 | return [NSColor whiteColor];
266 | }
267 | - (id)profileEditPlaceholderColor{
268 | return [NSColor whiteColor];
269 | }
270 | - (id)profileTitleBarBottomColor{
271 | return [NSColor whiteColor];
272 | }
273 | - (id)profileTitleBarTopColor{
274 | return [NSColor whiteColor];
275 | }
276 | - (id)followsYouTokenBackgroundColor{
277 | return [NSColor colorFromHexString:@"#1F2028"];
278 | }
279 | - (id)followsYouTokenColor{
280 | return [NSColor whiteColor];
281 | }
282 | - (id)directMessageFullNameColor{
283 | return [NSColor whiteColor];
284 | }
285 | - (id)directMessageEmptyOtherUserAvatarBackgroundColor{
286 | return [NSColor colorFromHexString:@"#1F2028"];
287 | }
288 | - (id)directMessageLeaveConversationTextColorForState:(long long)arg1{
289 | return [NSColor whiteColor];
290 | }
291 | - (id)directMessageLeaveConversationTextColor{
292 | return [NSColor whiteColor];
293 | }
294 | - (id)directMessageAlreadyAddedIconColor{
295 | return [NSColor whiteColor];
296 | }
297 | - (id)directMessageNoMatchTextColor{
298 | return [NSColor whiteColor];
299 | }
300 | - (id)directMessageAddPeopleTextColorForState:(long long)arg1{
301 | return [NSColor whiteColor];
302 | }
303 | - (id)directMessageAddPeopleTextColor{
304 | return [NSColor whiteColor];
305 | }
306 | - (id)directMessageComposeMediaIconColorForState:(long long)arg1{
307 | return [NSColor whiteColor];
308 | }
309 | - (id)directMessageComposeMediaIconColor{
310 | return [NSColor whiteColor];
311 | }
312 | - (id)directMessageComposePlaceholderTextColor{
313 | return [NSColor whiteColor];
314 | }
315 | - (id)directMessageComposeTextColor{
316 | return [NSColor whiteColor];
317 | }
318 | - (id)directMessageComposeBorderColor{
319 | return [NSColor colorFromHexString:@"#2D2D2E"];
320 | }
321 | - (id)directMessageComposeBackgroundColor{
322 | return [NSColor colorFromHexString:@"#1F2028"];
323 | }
324 | - (id)directMessageActionTextColor{
325 | return [NSColor whiteColor];
326 | }
327 | - (id)recipientTokenTextColorForState:(long long)arg1{
328 | return [NSColor whiteColor];
329 | }
330 | - (id)recipientTokenTextColor{
331 | return [NSColor whiteColor];
332 | }
333 | - (id)recipientTokenBackgroundColorForState:(long long)arg1{
334 | return [NSColor colorFromHexString:@"#1F2028"];
335 | }
336 | - (id)recipientTokenBackgroundColor{
337 | return [NSColor colorFromHexString:@"#1F2028"];
338 | }
339 | - (id)otherChatBubbleBackgroundColor{
340 | return [NSColor colorFromHexString:@"#1F2028"];
341 | }
342 | - (id)otherChatBubbleHashtagColor{
343 | return [NSColor whiteColor];
344 | }
345 | - (id)otherChatBubbleLinkColor{
346 | return [NSColor colorFromHexString:@"#7CC155"];
347 | }
348 | - (id)otherChatBubbleTextColor{
349 | return [NSColor whiteColor];
350 | }
351 | - (id)mineChatBubbleBackgroundColor{
352 | return [NSColor colorFromHexString:@"#1F2028"];
353 | }
354 | - (id)mineChatBubbleHashtagColor{
355 | return [NSColor whiteColor];
356 | }
357 | - (id)mineChatBubbleLinkColor{
358 | return [NSColor colorFromHexString:@"#7CC155"];
359 | }
360 | - (id)mineChatBubbleTextColor{
361 | return [NSColor whiteColor];
362 | }
363 | - (id)participantBackgroundColor{
364 | return [NSColor colorFromHexString:@"#1F2028"];
365 | }
366 | - (id)pollChoiceInputTextColor{
367 | return [NSColor whiteColor];
368 | }
369 | - (id)pollChoiceInputPlaceholderTextColor{
370 | return [NSColor whiteColor];
371 | }
372 | - (id)pollChoiceInputBackgroundColor{
373 | return [NSColor colorFromHexString:@"#1F2028"];
374 | }
375 | - (id)cardPollWinningBarColor{
376 | return [NSColor whiteColor];
377 | }
378 | - (id)cardPollBarColor{
379 | return [NSColor whiteColor];
380 | }
381 | - (id)cardPollBackgroundColor{
382 | return [NSColor colorFromHexString:@"#1F2028"];
383 | }
384 | - (id)cardButtonTextColorForState:(long long)arg1{
385 | return [NSColor whiteColor];
386 | }
387 | - (id)cardButtonTextColor{
388 | return [NSColor whiteColor];
389 | }
390 | - (id)cardButtonBorderColorForState:(long long)arg1{
391 | return [NSColor colorFromHexString:@"#2D2D2E"];
392 | }
393 | - (id)cardButtonBorderColor{
394 | return [NSColor colorFromHexString:@"#2D2D2E"];
395 | }
396 | - (id)cardBorderColor{
397 | return [NSColor colorFromHexString:@"#2D2D2E"];
398 | }
399 | - (id)detailMetadataCountColor{
400 | return [NSColor colorFromHexString:@"#FFFFFF"];
401 | }
402 | - (id)endOfDetailBackgroundColor{
403 | return [NSColor colorFromHexString:@"#1F2028"];
404 | }
405 | - (id)mediaViewerIconColor{
406 | return [NSColor whiteColor];
407 | }
408 | - (id)mediaViewerBackgroundColor{
409 | return [NSColor colorFromHexString:@"#1F2028"];
410 | }
411 | - (id)messageListExcerptTextColor{
412 | return [NSColor whiteColor];
413 | }
414 | - (id)messageListActionTextColor{
415 | return [NSColor whiteColor];
416 | }
417 | - (id)conversationMutedColor{
418 | return [NSColor whiteColor];
419 | }
420 | - (id)messageRepliedColor{
421 | return [NSColor whiteColor];
422 | }
423 | - (id)unreadIndicatorColor{
424 | return [NSColor whiteColor];
425 | }
426 | - (id)unreadMessageColor{
427 | return [NSColor whiteColor];
428 | }
429 | - (id)listInputPlaceHolderTextColor{
430 | return [NSColor whiteColor];
431 | }
432 | - (id)listInputBackgroundTextColor{
433 | return [NSColor whiteColor];
434 | }
435 | - (id)listProtectedIconColor{
436 | return [NSColor whiteColor];
437 | }
438 | - (id)autocompleteBackgroundColor{
439 | return [NSColor colorFromHexString:@"#1F2028"];
440 | }
441 | - (id)autocompleteSeparatorColor{
442 | return [NSColor colorFromHexString:@"#2D2D2E"];
443 | }
444 | - (id)autocompleteHighlightColor{
445 | return [NSColor whiteColor];
446 | }
447 | - (id)autocompleteHashTagColor{
448 | return [NSColor whiteColor];
449 | }
450 | - (id)autocompleteFullNameColor{
451 | return [NSColor whiteColor];
452 | }
453 | - (id)autocompleteTwitterHandleColor{
454 | return [NSColor whiteColor];
455 | }
456 | - (id)composeActionTextSelectedColorForState:(long long)arg1{
457 | return [NSColor whiteColor];
458 | }
459 | - (id)composeActionTextSelectedColor{
460 | return [NSColor whiteColor];
461 | }
462 | - (id)composeActionIconSelectedColorForState:(long long)arg1{
463 | return [NSColor whiteColor];
464 | }
465 | - (id)composeActionIconSelectedColor{
466 | return [NSColor whiteColor];
467 | }
468 | - (id)composeActionTextColorForState:(long long)arg1{
469 | return [NSColor whiteColor];
470 | }
471 | - (id)composeActionTextColor{
472 | return [NSColor whiteColor];
473 | }
474 | - (id)composeActionIconColorForState:(long long)arg1{
475 | return [NSColor whiteColor];
476 | }
477 | - (id)composeEmojiIconColor{
478 | return [NSColor whiteColor];
479 | }
480 | - (id)composeActionIconColor{
481 | return [NSColor whiteColor];
482 | }
483 | - (id)composeBackgroundColor{
484 | return [NSColor colorFromHexString:@"#1F2028"];
485 | }
486 | - (id)userRowSelectedTextBackgroundColor{
487 | return [NSColor colorFromHexString:@"#1F2028"];
488 | }
489 | - (id)userRowSelectedBackgroundColor{
490 | return [NSColor colorFromHexString:@"#1F2028"];
491 | }
492 | - (id)mediaDeleteOverlayColor{
493 | return [NSColor whiteColor];
494 | }
495 | - (id)placeholderTextColor{
496 | return [NSColor whiteColor];
497 | }
498 | - (id)addMediaIconColorForState:(long long)arg1{
499 | return [NSColor whiteColor];
500 | }
501 | - (id)addMediaIconColor{
502 | return [NSColor whiteColor];
503 | }
504 | - (id)characterCountOverflowColor{
505 | return [NSColor whiteColor];
506 | }
507 | - (id)characterCountColor{
508 | return [NSColor whiteColor];
509 | }
510 | - (id)insertionPointColor{
511 | return [NSColor whiteColor];
512 | }
513 | - (id)notificationTweetTextColor{
514 | return [NSColor whiteColor];
515 | }
516 | - (id)notificationListIconColor{
517 | return [NSColor whiteColor];
518 | }
519 | - (id)notificationContactIconColor{
520 | return [NSColor whiteColor];
521 | }
522 | - (id)notificationFollowIconColor{
523 | return [NSColor whiteColor];
524 | }
525 | - (id)notificationRetweetIconColor{
526 | return [NSColor whiteColor];
527 | }
528 | - (id)notificationFavoriteIconColor{
529 | return [NSColor whiteColor];
530 | }
531 | - (id)quoteMentionSelectedBackgroundColor{
532 | return [NSColor colorFromHexString:@"#1F2028"];
533 | }
534 | - (id)quoteSelectedBackgroundColor{
535 | return [NSColor colorFromHexString:@"#1F2028"];
536 | }
537 | - (id)quoteBackgroundColor{
538 | return [NSColor colorFromHexString:@"#1F2028"];
539 | }
540 | - (id)quoteMentionSelectedBorderColor{
541 | return [NSColor colorFromHexString:@"#2D2D2E"];
542 | }
543 | - (id)quoteSelectedBorderColor{
544 | return [NSColor colorFromHexString:@"#2D2D2E"];
545 | }
546 | - (id)quoteBorderColor{
547 | return [NSColor colorFromHexString:@"#2D2D2E"];
548 | }
549 | - (id)newFollowerColor{
550 | return [NSColor whiteColor];
551 | }
552 | - (id)retweetSelectedColorForState:(long long)arg1{
553 | return [NSColor whiteColor];
554 | }
555 | - (id)retweetSelectedColor{
556 | return [NSColor colorFromHexString:@"#7CC155"];
557 | }
558 | - (id)favoriteSelectedColorForState:(long long)arg1{
559 | return [NSColor whiteColor];
560 | }
561 | - (id)favoriteSelectedColor{
562 | return [NSColor colorFromHexString:@"#DA2C38"];
563 | }
564 | - (id)tweetActionTextColorForState:(long long)arg1{
565 | return [NSColor whiteColor];
566 | }
567 | - (id)tweetActionTextColor{
568 | return [NSColor colorFromHexString:@"#616168"];
569 | }
570 | - (id)tweetActionIconColorForState:(long long)arg1{
571 | return [NSColor colorFromHexString:@"#616168"];
572 | }
573 | - (id)tweetActionIconColor{
574 | return [NSColor colorFromHexString:@"#616168"];
575 | }
576 | - (id)inputCursorColor{
577 | return [NSColor whiteColor];
578 | }
579 | - (id)inputErrorBorderColor{
580 | return [NSColor colorFromHexString:@"#2D2D2E"];
581 | }
582 | - (id)inputErrorBackgroundColor{
583 | return [NSColor colorFromHexString:@"#1F2028"];
584 | }
585 | - (id)inputErrorPlaceholderTextColor{
586 | return [NSColor whiteColor];
587 | }
588 | - (id)inputErrorTextColor{
589 | return [NSColor whiteColor];
590 | }
591 | - (id)inputBorderColor{
592 | return [NSColor colorFromHexString:@"#2D2D2E"];
593 | }
594 | - (id)inputBackgroundColor{
595 | return [NSColor colorFromHexString:@"#1F2028"];
596 | }
597 | - (id)inputTextColor{
598 | return [NSColor whiteColor];
599 | }
600 | - (id)minorTextColor{
601 | return [NSColor whiteColor];
602 | }
603 | - (id)errorTextColor{
604 | return [NSColor whiteColor];
605 | }
606 | - (id)actionTextColorForState:(long long)arg1{
607 | return [NSColor whiteColor];
608 | }
609 | - (id)actionTextColor{
610 | return [NSColor whiteColor];
611 | }
612 | - (id)atReplyColorForState:(long long)arg1{
613 | return [NSColor whiteColor];
614 | }
615 | - (id)atReplyColor{
616 | return [NSColor whiteColor];
617 | }
618 | - (id)hashtagColorForState:(long long)arg1{
619 | return [NSColor whiteColor];
620 | }
621 | - (id)hashtagColor{
622 | return [NSColor colorFromHexString:@"#0090B9"];
623 | }
624 | - (id)linkColorForState:(long long)arg1{
625 | return [NSColor colorFromHexString:@"#7CC155"];
626 | }
627 | - (id)linkColor{
628 | return [NSColor colorFromHexString:@"#7CC155"];
629 | }
630 | - (id)subheadTextColor{
631 | return [NSColor whiteColor];
632 | }
633 | - (id)bodyTextColor{
634 | return [NSColor colorFromHexString:@"#FFFFFF"];
635 | }
636 | - (id)metadataLinkColor{
637 | return [NSColor colorFromHexString:@"#7CC155"];
638 | }
639 | - (id)metadataColor{
640 | return [NSColor whiteColor];
641 | }
642 | - (id)timestampColor{
643 | return [NSColor colorFromHexString:@"#616168"];
644 | }
645 | - (id)usernameColor{
646 | return [NSColor colorFromHexString:@"#B31789"];
647 | }
648 | - (id)fullNameColor{
649 | return [NSColor colorFromHexString:@"#B31789"];
650 | }
651 | - (id)progressIndicatorColor{
652 | return [NSColor whiteColor];
653 | }
654 | - (id)retweetedTextColor{
655 | return [NSColor colorFromHexString:@"#616168"];
656 | }
657 | - (id)retweetedIconColor{
658 | return [NSColor colorFromHexString:@"#7CC155"];
659 | }
660 | - (id)endOfLineIconColor{
661 | return [NSColor whiteColor];
662 | }
663 | - (id)endOfLineBackgroundColor{
664 | return [NSColor colorFromHexString:@"#1F2028"];
665 | }
666 | - (id)closeButtonColorForState:(long long)arg1{
667 | return [NSColor colorFromHexString:@"#616168"];
668 | }
669 | - (id)closeButtonColor{
670 | return [NSColor colorFromHexString:@"#616168"];
671 | }
672 | - (id)backButtonColorForState:(long long)arg1{
673 | return [NSColor colorFromHexString:@"#616168"];
674 | }
675 | - (id)backButtonColor{
676 | return [NSColor colorFromHexString:@"#616168"];
677 | }
678 | - (id)mediaPlaceholderColor{
679 | return [NSColor whiteColor];
680 | }
681 | - (id)topTweetIconColor{
682 | return [NSColor whiteColor];
683 | }
684 | - (id)promotedIconColor{
685 | return [NSColor whiteColor];
686 | }
687 | - (id)mentionSelectedBackgroundColor{
688 | return [NSColor colorFromHexString:@"#1F2028"];
689 | }
690 | - (id)selectedBackgroundColor{
691 | return [NSColor colorFromHexString:@"#1F2028"];
692 | }
693 | - (id)mentionBackgroundColor{
694 | return [NSColor colorFromHexString:@"#1F2028"];
695 | }
696 | - (id)horizontalSeparatorColor{
697 | return [NSColor colorFromHexString:@"#2D2D2E"];
698 | }
699 | - (id)emptyStateTextColor{
700 | return [NSColor whiteColor];
701 | }
702 | - (id)emptyStateIconColor{
703 | return [NSColor whiteColor];
704 | }
705 | - (id)appBackgroundColor{
706 | return [NSColor colorFromHexString:@"#1F2028"];
707 | }
708 | - (id)titlebarTextButtonTextColorForState:(long long)arg1{
709 | return [NSColor colorFromHexString:@"#FFFFFF"];
710 | }
711 | - (id)titlebarTextButtonTextColor{
712 | return [NSColor colorFromHexString:@"#616168"];
713 | }
714 | - (id)titleColorForState:(long long)arg1{
715 | return [NSColor whiteColor];
716 | }
717 | - (id)titleColor{
718 | return [NSColor colorFromHexString:@"#FFFFFF"];
719 | }
720 | - (id)rowActionIconColorForState:(long long)arg1{
721 | return [NSColor whiteColor];
722 | }
723 | - (id)rowActionIconColor{
724 | return [NSColor whiteColor];
725 | }
726 | - (id)rowUnselectedIconColorForState:(long long)arg1{
727 | return [NSColor whiteColor];
728 | }
729 | - (id)rowUnselectedIconColor{
730 | return [NSColor whiteColor];
731 | }
732 | - (id)rowSelectedIconColorForState:(long long)arg1{
733 | return [NSColor whiteColor];
734 | }
735 | - (id)rowSelectedIconColor{
736 | return [NSColor whiteColor];
737 | }
738 | - (id)borderlessTextColorForState:(long long)arg1{
739 | return [NSColor colorFromHexString:@"#2D2D2E"];
740 | }
741 | - (id)buttonWhiteTextColorForState:(long long)arg1{
742 | return [NSColor colorFromHexString:@"#2D2D2E"];
743 | }
744 | - (id)buttonWhiteTextColor{
745 | return [NSColor whiteColor];
746 | }
747 | - (id)buttonWhiteIconColorForState:(long long)arg1{
748 | return [NSColor whiteColor];
749 | }
750 | - (id)buttonWhiteIconColor{
751 | return [NSColor whiteColor];
752 | }
753 | - (id)buttonMuteTextColorForState:(long long)arg1{
754 | return [NSColor whiteColor];
755 | }
756 | - (id)buttonMuteTextColor{
757 | return [NSColor whiteColor];
758 | }
759 | - (id)buttonMuteIconColorForState:(long long)arg1{
760 | return [NSColor whiteColor];
761 | }
762 | - (id)buttonMuteIconColor{
763 | return [NSColor whiteColor];
764 | }
765 | - (id)buttonGrayTextColorForState:(long long)arg1{
766 | return [NSColor whiteColor];
767 | }
768 | - (id)buttonGrayTextColor{
769 | return [NSColor whiteColor];
770 | }
771 | - (id)buttonGrayIconColorForState:(long long)arg1{
772 | return [NSColor whiteColor];
773 | }
774 | - (id)buttonGrayIconColor{
775 | return [NSColor whiteColor];
776 | }
777 | - (id)buttonPollTextColorForState:(long long)arg1{
778 | return [NSColor whiteColor];
779 | }
780 | - (id)buttonPollTextColor{
781 | return [NSColor whiteColor];
782 | }
783 | - (id)buttonPollIconColorForState:(long long)arg1{
784 | return [NSColor whiteColor];
785 | }
786 | - (id)buttonPollIconColor{
787 | return [NSColor whiteColor];
788 | }
789 | - (id)buttonBlueTextColorForState:(long long)arg1{
790 | return [NSColor whiteColor];
791 | }
792 | - (id)buttonBlueTextColor{
793 | return [NSColor whiteColor];
794 | }
795 | - (id)buttonBlueIconColorForState:(long long)arg1{
796 | return [NSColor whiteColor];
797 | }
798 | - (id)buttonBlueIconColor{
799 | return [NSColor whiteColor];
800 | }
801 | - (id)buttonKeyTextColorForState:(long long)arg1{
802 | return [NSColor whiteColor];
803 | }
804 | - (id)buttonKeyTextColor{
805 | return [NSColor whiteColor];
806 | }
807 | - (id)buttonKeyIconColorForState:(long long)arg1{
808 | return [NSColor whiteColor];
809 | }
810 | - (id)buttonKeyIconColor{
811 | return [NSColor whiteColor];
812 | }
813 | - (id)buttonStandardTextColorForState:(long long)arg1{
814 | return [NSColor whiteColor];
815 | }
816 | - (id)buttonStandardTextColor{
817 | return [NSColor whiteColor];
818 | }
819 | - (id)buttonStandardIconColorForState:(long long)arg1{
820 | return [NSColor whiteColor];
821 | }
822 | - (id)buttonStandardIconColor{
823 | return [NSColor whiteColor];
824 | }
825 | - (id)sidebarBackgroundColorForState:(long long)arg1{
826 | return [NSColor colorFromHexString:@"#1F2028"];
827 | }
828 | - (id)sidebarBackgroundColor{
829 | return [NSColor colorFromHexString:@"#1F2028"];
830 | }
831 | - (id)sidebarSeparatorColor{
832 | return [NSColor colorFromHexString:@"#2D2D2E"];
833 | }
834 | - (id)sidebarButtonSelectedColorForState:(long long)arg1{
835 | return [NSColor whiteColor];
836 | }
837 | - (id)sidebarButtonSelectedColor{
838 | return [NSColor colorFromHexString:@"#FFFFFF"];
839 | }
840 | - (id)sidebarButtonColorForState:(long long)arg1{
841 | return [NSColor colorFromHexString:@"#616168"];
842 | }
843 | - (id)sidebarButtonColor{
844 | return [NSColor whiteColor];
845 | }
846 | - (id)iconGray{
847 | return [NSColor whiteColor];
848 | }
849 | - (id)textGray{
850 | return [NSColor whiteColor];
851 | }
852 | - (id)backgroundGray{
853 | return [NSColor whiteColor];
854 | }
855 | - (id)placeholderGray{
856 | return [NSColor whiteColor];
857 | }
858 | - (id)disabledGray{
859 | return [NSColor whiteColor];
860 | }
861 | - (id)inactiveGray{
862 | return [NSColor whiteColor];
863 | }
864 | - (id)strongGray{
865 | return [NSColor whiteColor];
866 | }
867 | - (id)weakGray{
868 | return [NSColor whiteColor];
869 | }
870 | - (id)red{
871 | return [NSColor whiteColor];
872 | }
873 | - (id)blue{
874 | return [NSColor whiteColor];
875 | }
876 | - (id)neededColor{
877 | return [NSColor whiteColor];
878 | }
879 | - (id)modifiedColorForControlState:(long long)arg1 color:(id)arg2{
880 | return [NSColor whiteColor];
881 | }
882 | - (id)pressedColorForColor:(id)arg1{
883 | return [NSColor whiteColor];
884 | }
885 | - (id)hoverColorForColor:(id)arg1{
886 | return [NSColor whiteColor];
887 | }
888 | - (id)inactiveColorForColor:(id)arg1{
889 | return [NSColor whiteColor];
890 | }
891 | - (id)disabledColorForColor:(id)arg1{
892 | return [NSColor whiteColor];
893 | }
894 | @end
895 |
--------------------------------------------------------------------------------
/TwitterX/TwitterX.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 33075926203D9EF700CAA86C /* TWXEnhancedFullscreen.h in Headers */ = {isa = PBXBuildFile; fileRef = 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */; };
11 | 33075927203D9EF700CAA86C /* TWXEnhancedFullscreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */; };
12 | 335D38CD20387EE300379A21 /* TwitterX.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38CB20387EE300379A21 /* TwitterX.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 335D38D320387EF100379A21 /* TWXFeature.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38B420386E4F00379A21 /* TWXFeature.h */; };
14 | 335D38D520387EF600379A21 /* TWXCharactersLimit.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38B820386E8300379A21 /* TWXCharactersLimit.h */; };
15 | 335D38D620387EF900379A21 /* TWXCharactersLimit.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38B920386E8300379A21 /* TWXCharactersLimit.m */; };
16 | 335D38D720387EFC00379A21 /* TWXRoundedAvatars.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */; };
17 | 335D38D820387EFF00379A21 /* TWXRoundedAvatars.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */; };
18 | 335D38DD2038DA6100379A21 /* TWXRuntime.h in Headers */ = {isa = PBXBuildFile; fileRef = 335D38DB2038DA6100379A21 /* TWXRuntime.h */; };
19 | 335D38DE2038DA6100379A21 /* TWXRuntime.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38DC2038DA6100379A21 /* TWXRuntime.m */; };
20 | 335D38E02038E45C00379A21 /* TwitterX.m in Sources */ = {isa = PBXBuildFile; fileRef = 335D38DF2038E45C00379A21 /* TwitterX.m */; };
21 | 33B95C172062767F0060B8D5 /* TWXAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C152062767F0060B8D5 /* TWXAPI.h */; };
22 | 33B95C182062767F0060B8D5 /* TWXAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C162062767F0060B8D5 /* TWXAPI.m */; };
23 | 33B95C1D206294EE0060B8D5 /* TWXAPI+Bookmarks.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */; };
24 | 33B95C1E206294EE0060B8D5 /* TWXAPI+Bookmarks.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */; };
25 | 33B95C212062D1B30060B8D5 /* TWXBookmarksTimeline.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */; };
26 | 33B95C222062D1B30060B8D5 /* TWXBookmarksTimeline.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */; };
27 | 33B95C252062E7720060B8D5 /* TWXBookmarksSidebarItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */; };
28 | 33B95C262062E7720060B8D5 /* TWXBookmarksSidebarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */; };
29 | 33B95C34206308190060B8D5 /* TwitterXAssets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */; };
30 | 33B95C3E206310760060B8D5 /* NSImage+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C3C206310760060B8D5 /* NSImage+TWX.h */; };
31 | 33B95C3F206310760060B8D5 /* NSImage+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C3D206310760060B8D5 /* NSImage+TWX.m */; };
32 | 33B95C4620631AB30060B8D5 /* bookmarks.png in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C4420631AB20060B8D5 /* bookmarks.png */; };
33 | 33B95C4720631AB30060B8D5 /* bookmarks@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */; };
34 | 33B95C4A2063A6EA0060B8D5 /* NSBundle+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */; };
35 | 33B95C4B2063A6EA0060B8D5 /* NSBundle+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */; };
36 | 37E3E2222078D41800C5B683 /* TWXDuskTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */; };
37 | 37E3E2232078D41800C5B683 /* TWXDuskTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */; };
38 | 37E3E2262078D93200C5B683 /* TWXThemeMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */; };
39 | 37E3E2272078D93200C5B683 /* TWXThemeMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */; };
40 | 37E3E2322078F98700C5B683 /* NSColor+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2302078F98700C5B683 /* NSColor+TWX.h */; };
41 | 37E3E2332078F98700C5B683 /* NSColor+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2312078F98700C5B683 /* NSColor+TWX.m */; };
42 | 37E3E23A2079ED5900C5B683 /* TWXTheme.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2382079ED5900C5B683 /* TWXTheme.h */; };
43 | 37E3E23B2079ED5900C5B683 /* TWXTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2392079ED5900C5B683 /* TWXTheme.m */; };
44 | 37E3E23E2079FE8700C5B683 /* NSString+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */; };
45 | 37E3E23F2079FE8700C5B683 /* NSString+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */; };
46 | 37E3E2422079FF3600C5B683 /* TWXSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3E2402079FF3600C5B683 /* TWXSource.h */; };
47 | 37E3E2432079FF3600C5B683 /* TWXSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3E2412079FF3600C5B683 /* TWXSource.m */; };
48 | C13554702061C4E700F6504D /* TWXBookmarkActions.h in Headers */ = {isa = PBXBuildFile; fileRef = C135546E2061C4E700F6504D /* TWXBookmarkActions.h */; };
49 | C13554712061C4E700F6504D /* TWXBookmarkActions.m in Sources */ = {isa = PBXBuildFile; fileRef = C135546F2061C4E700F6504D /* TWXBookmarkActions.m */; };
50 | C18C74F620507003001B0AAC /* TWXDirectMessagesCrash.h in Headers */ = {isa = PBXBuildFile; fileRef = C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */; };
51 | C18C74F720507003001B0AAC /* TWXDirectMessagesCrash.m in Sources */ = {isa = PBXBuildFile; fileRef = C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */; };
52 | C1B3282F2059160D00009EA8 /* NSView+TWX.h in Headers */ = {isa = PBXBuildFile; fileRef = C1B3282D2059160D00009EA8 /* NSView+TWX.h */; };
53 | C1B328302059160D00009EA8 /* NSView+TWX.m in Sources */ = {isa = PBXBuildFile; fileRef = C1B3282E2059160D00009EA8 /* NSView+TWX.m */; };
54 | C1D846F42041BB080016E52D /* TWXRoundedStatusContent.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */; };
55 | C1D846F52041BB080016E52D /* TWXRoundedStatusContent.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */; };
56 | C1D846F82041F5950016E52D /* TWXLightThemeReborn.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */; };
57 | C1D846F92041F5950016E52D /* TWXLightThemeReborn.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */; };
58 | C1D846FC2042B08C0016E52D /* TWXRepliesCount.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */; };
59 | C1D846FD2042B08C0016E52D /* TWXRepliesCount.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */; };
60 | C1D84700204358FE0016E52D /* TWXConsumerKeysOverride.h in Headers */ = {isa = PBXBuildFile; fileRef = C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */; };
61 | C1D84701204358FE0016E52D /* TWXConsumerKeysOverride.m in Sources */ = {isa = PBXBuildFile; fileRef = C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */; };
62 | /* End PBXBuildFile section */
63 |
64 | /* Begin PBXFileReference section */
65 | 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXEnhancedFullscreen.h; sourceTree = ""; };
66 | 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXEnhancedFullscreen.m; sourceTree = ""; };
67 | 335D38B420386E4F00379A21 /* TWXFeature.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXFeature.h; sourceTree = ""; };
68 | 335D38B820386E8300379A21 /* TWXCharactersLimit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXCharactersLimit.h; sourceTree = ""; };
69 | 335D38B920386E8300379A21 /* TWXCharactersLimit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXCharactersLimit.m; sourceTree = ""; };
70 | 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRoundedAvatars.h; sourceTree = ""; };
71 | 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRoundedAvatars.m; sourceTree = ""; };
72 | 335D38C920387EE300379A21 /* TwitterX.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TwitterX.framework; sourceTree = BUILT_PRODUCTS_DIR; };
73 | 335D38CB20387EE300379A21 /* TwitterX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TwitterX.h; sourceTree = ""; };
74 | 335D38CC20387EE300379A21 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
75 | 335D38DB2038DA6100379A21 /* TWXRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRuntime.h; sourceTree = ""; };
76 | 335D38DC2038DA6100379A21 /* TWXRuntime.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRuntime.m; sourceTree = ""; };
77 | 335D38DF2038E45C00379A21 /* TwitterX.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwitterX.m; sourceTree = ""; };
78 | 33B95C152062767F0060B8D5 /* TWXAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXAPI.h; sourceTree = ""; };
79 | 33B95C162062767F0060B8D5 /* TWXAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXAPI.m; sourceTree = ""; };
80 | 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TWXAPI+Bookmarks.h"; sourceTree = ""; };
81 | 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "TWXAPI+Bookmarks.m"; sourceTree = ""; };
82 | 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarksTimeline.h; sourceTree = ""; };
83 | 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarksTimeline.m; sourceTree = ""; };
84 | 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarksSidebarItem.h; sourceTree = ""; };
85 | 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarksSidebarItem.m; sourceTree = ""; };
86 | 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TwitterXAssets.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
87 | 33B95C2D2062FF820060B8D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
88 | 33B95C3C206310760060B8D5 /* NSImage+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSImage+TWX.h"; sourceTree = ""; };
89 | 33B95C3D206310760060B8D5 /* NSImage+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSImage+TWX.m"; sourceTree = ""; };
90 | 33B95C4420631AB20060B8D5 /* bookmarks.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bookmarks.png; sourceTree = ""; };
91 | 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bookmarks@2x.png"; sourceTree = ""; };
92 | 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSBundle+TWX.h"; sourceTree = ""; };
93 | 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+TWX.m"; sourceTree = ""; };
94 | 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXDuskTheme.h; sourceTree = ""; };
95 | 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXDuskTheme.m; sourceTree = ""; };
96 | 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXThemeMenu.h; sourceTree = ""; };
97 | 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXThemeMenu.m; sourceTree = ""; };
98 | 37E3E2302078F98700C5B683 /* NSColor+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSColor+TWX.h"; sourceTree = ""; };
99 | 37E3E2312078F98700C5B683 /* NSColor+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSColor+TWX.m"; sourceTree = ""; };
100 | 37E3E2382079ED5900C5B683 /* TWXTheme.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXTheme.h; sourceTree = ""; };
101 | 37E3E2392079ED5900C5B683 /* TWXTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXTheme.m; sourceTree = ""; };
102 | 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+TWX.h"; sourceTree = ""; };
103 | 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+TWX.m"; sourceTree = ""; };
104 | 37E3E2402079FF3600C5B683 /* TWXSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXSource.h; sourceTree = ""; };
105 | 37E3E2412079FF3600C5B683 /* TWXSource.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXSource.m; sourceTree = ""; };
106 | C135546E2061C4E700F6504D /* TWXBookmarkActions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXBookmarkActions.h; sourceTree = ""; };
107 | C135546F2061C4E700F6504D /* TWXBookmarkActions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXBookmarkActions.m; sourceTree = ""; };
108 | C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXDirectMessagesCrash.h; sourceTree = ""; };
109 | C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXDirectMessagesCrash.m; sourceTree = ""; };
110 | C1B3282D2059160D00009EA8 /* NSView+TWX.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+TWX.h"; sourceTree = ""; };
111 | C1B3282E2059160D00009EA8 /* NSView+TWX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+TWX.m"; sourceTree = ""; };
112 | C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRoundedStatusContent.h; sourceTree = ""; };
113 | C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRoundedStatusContent.m; sourceTree = ""; };
114 | C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXLightThemeReborn.h; sourceTree = ""; };
115 | C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXLightThemeReborn.m; sourceTree = ""; };
116 | C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXRepliesCount.h; sourceTree = ""; };
117 | C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXRepliesCount.m; sourceTree = ""; };
118 | C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TWXConsumerKeysOverride.h; sourceTree = ""; };
119 | C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TWXConsumerKeysOverride.m; sourceTree = ""; };
120 | /* End PBXFileReference section */
121 |
122 | /* Begin PBXFrameworksBuildPhase section */
123 | 335D38C520387EE300379A21 /* Frameworks */ = {
124 | isa = PBXFrameworksBuildPhase;
125 | buildActionMask = 2147483647;
126 | files = (
127 | );
128 | runOnlyForDeploymentPostprocessing = 0;
129 | };
130 | 33B95C282062FF820060B8D5 /* Frameworks */ = {
131 | isa = PBXFrameworksBuildPhase;
132 | buildActionMask = 2147483647;
133 | files = (
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | /* End PBXFrameworksBuildPhase section */
138 |
139 | /* Begin PBXGroup section */
140 | 335D389720382C9100379A21 = {
141 | isa = PBXGroup;
142 | children = (
143 | 335D38A220382C9100379A21 /* TwitterX */,
144 | 33B95C2C2062FF820060B8D5 /* TwitterXAssets */,
145 | 335D38A120382C9100379A21 /* Products */,
146 | );
147 | sourceTree = "";
148 | };
149 | 335D38A120382C9100379A21 /* Products */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 335D38C920387EE300379A21 /* TwitterX.framework */,
153 | 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */,
154 | );
155 | name = Products;
156 | sourceTree = "";
157 | };
158 | 335D38A220382C9100379A21 /* TwitterX */ = {
159 | isa = PBXGroup;
160 | children = (
161 | 37E3E21F2078D3F200C5B683 /* Theme */,
162 | 33B95C1A206294D20060B8D5 /* Service */,
163 | 335D38DA2038DA4B00379A21 /* Utilities */,
164 | 335D38B220386E2D00379A21 /* Features */,
165 | 335D38CB20387EE300379A21 /* TwitterX.h */,
166 | 335D38DF2038E45C00379A21 /* TwitterX.m */,
167 | 335D38CC20387EE300379A21 /* Info.plist */,
168 | );
169 | path = TwitterX;
170 | sourceTree = "";
171 | };
172 | 335D38B220386E2D00379A21 /* Features */ = {
173 | isa = PBXGroup;
174 | children = (
175 | 335D38B420386E4F00379A21 /* TWXFeature.h */,
176 | 335D38B820386E8300379A21 /* TWXCharactersLimit.h */,
177 | 335D38B920386E8300379A21 /* TWXCharactersLimit.m */,
178 | 335D38BC20386EA500379A21 /* TWXRoundedAvatars.h */,
179 | 335D38BD20386EA500379A21 /* TWXRoundedAvatars.m */,
180 | 33075924203D9EF700CAA86C /* TWXEnhancedFullscreen.h */,
181 | 33075925203D9EF700CAA86C /* TWXEnhancedFullscreen.m */,
182 | C1D846F22041BB080016E52D /* TWXRoundedStatusContent.h */,
183 | C1D846F32041BB080016E52D /* TWXRoundedStatusContent.m */,
184 | C1D846F62041F5940016E52D /* TWXLightThemeReborn.h */,
185 | C1D846F72041F5940016E52D /* TWXLightThemeReborn.m */,
186 | C1D846FE204358FE0016E52D /* TWXConsumerKeysOverride.h */,
187 | C1D846FF204358FE0016E52D /* TWXConsumerKeysOverride.m */,
188 | C1D846FA2042B08C0016E52D /* TWXRepliesCount.h */,
189 | C1D846FB2042B08C0016E52D /* TWXRepliesCount.m */,
190 | C18C74F420507003001B0AAC /* TWXDirectMessagesCrash.h */,
191 | C18C74F520507003001B0AAC /* TWXDirectMessagesCrash.m */,
192 | C135546E2061C4E700F6504D /* TWXBookmarkActions.h */,
193 | C135546F2061C4E700F6504D /* TWXBookmarkActions.m */,
194 | 33B95C1F2062D1B30060B8D5 /* TWXBookmarksTimeline.h */,
195 | 33B95C202062D1B30060B8D5 /* TWXBookmarksTimeline.m */,
196 | 33B95C232062E7720060B8D5 /* TWXBookmarksSidebarItem.h */,
197 | 33B95C242062E7720060B8D5 /* TWXBookmarksSidebarItem.m */,
198 | 37E3E2242078D93200C5B683 /* TWXThemeMenu.h */,
199 | 37E3E2252078D93200C5B683 /* TWXThemeMenu.m */,
200 | 37E3E2382079ED5900C5B683 /* TWXTheme.h */,
201 | 37E3E2392079ED5900C5B683 /* TWXTheme.m */,
202 | 37E3E2402079FF3600C5B683 /* TWXSource.h */,
203 | 37E3E2412079FF3600C5B683 /* TWXSource.m */,
204 | );
205 | path = Features;
206 | sourceTree = "";
207 | };
208 | 335D38DA2038DA4B00379A21 /* Utilities */ = {
209 | isa = PBXGroup;
210 | children = (
211 | 335D38DB2038DA6100379A21 /* TWXRuntime.h */,
212 | 335D38DC2038DA6100379A21 /* TWXRuntime.m */,
213 | C1B3282D2059160D00009EA8 /* NSView+TWX.h */,
214 | C1B3282E2059160D00009EA8 /* NSView+TWX.m */,
215 | 33B95C482063A6EA0060B8D5 /* NSBundle+TWX.h */,
216 | 33B95C492063A6EA0060B8D5 /* NSBundle+TWX.m */,
217 | 33B95C3C206310760060B8D5 /* NSImage+TWX.h */,
218 | 33B95C3D206310760060B8D5 /* NSImage+TWX.m */,
219 | 37E3E2302078F98700C5B683 /* NSColor+TWX.h */,
220 | 37E3E2312078F98700C5B683 /* NSColor+TWX.m */,
221 | 37E3E23C2079FE8700C5B683 /* NSString+TWX.h */,
222 | 37E3E23D2079FE8700C5B683 /* NSString+TWX.m */,
223 | );
224 | path = Utilities;
225 | sourceTree = "";
226 | };
227 | 33B95C1A206294D20060B8D5 /* Service */ = {
228 | isa = PBXGroup;
229 | children = (
230 | 33B95C152062767F0060B8D5 /* TWXAPI.h */,
231 | 33B95C162062767F0060B8D5 /* TWXAPI.m */,
232 | 33B95C1B206294EE0060B8D5 /* TWXAPI+Bookmarks.h */,
233 | 33B95C1C206294EE0060B8D5 /* TWXAPI+Bookmarks.m */,
234 | );
235 | path = Service;
236 | sourceTree = "";
237 | };
238 | 33B95C2C2062FF820060B8D5 /* TwitterXAssets */ = {
239 | isa = PBXGroup;
240 | children = (
241 | 33B95C2D2062FF820060B8D5 /* Info.plist */,
242 | 33B95C3720630A210060B8D5 /* Images */,
243 | );
244 | path = TwitterXAssets;
245 | sourceTree = "";
246 | };
247 | 33B95C3720630A210060B8D5 /* Images */ = {
248 | isa = PBXGroup;
249 | children = (
250 | 33B95C4420631AB20060B8D5 /* bookmarks.png */,
251 | 33B95C4520631AB20060B8D5 /* bookmarks@2x.png */,
252 | );
253 | path = Images;
254 | sourceTree = "";
255 | };
256 | 37E3E21F2078D3F200C5B683 /* Theme */ = {
257 | isa = PBXGroup;
258 | children = (
259 | 37E3E2202078D41800C5B683 /* TWXDuskTheme.h */,
260 | 37E3E2212078D41800C5B683 /* TWXDuskTheme.m */,
261 | );
262 | path = Theme;
263 | sourceTree = "";
264 | };
265 | /* End PBXGroup section */
266 |
267 | /* Begin PBXHeadersBuildPhase section */
268 | 335D38C620387EE300379A21 /* Headers */ = {
269 | isa = PBXHeadersBuildPhase;
270 | buildActionMask = 2147483647;
271 | files = (
272 | C1D846FC2042B08C0016E52D /* TWXRepliesCount.h in Headers */,
273 | 335D38D520387EF600379A21 /* TWXCharactersLimit.h in Headers */,
274 | 33B95C4A2063A6EA0060B8D5 /* NSBundle+TWX.h in Headers */,
275 | 37E3E2322078F98700C5B683 /* NSColor+TWX.h in Headers */,
276 | 33B95C1D206294EE0060B8D5 /* TWXAPI+Bookmarks.h in Headers */,
277 | C1D84700204358FE0016E52D /* TWXConsumerKeysOverride.h in Headers */,
278 | C18C74F620507003001B0AAC /* TWXDirectMessagesCrash.h in Headers */,
279 | C1D846F82041F5950016E52D /* TWXLightThemeReborn.h in Headers */,
280 | 37E3E2262078D93200C5B683 /* TWXThemeMenu.h in Headers */,
281 | 335D38DD2038DA6100379A21 /* TWXRuntime.h in Headers */,
282 | 37E3E23A2079ED5900C5B683 /* TWXTheme.h in Headers */,
283 | 33B95C3E206310760060B8D5 /* NSImage+TWX.h in Headers */,
284 | 335D38D320387EF100379A21 /* TWXFeature.h in Headers */,
285 | 33B95C172062767F0060B8D5 /* TWXAPI.h in Headers */,
286 | C1D846F42041BB080016E52D /* TWXRoundedStatusContent.h in Headers */,
287 | C13554702061C4E700F6504D /* TWXBookmarkActions.h in Headers */,
288 | C1B3282F2059160D00009EA8 /* NSView+TWX.h in Headers */,
289 | 335D38CD20387EE300379A21 /* TwitterX.h in Headers */,
290 | 37E3E23E2079FE8700C5B683 /* NSString+TWX.h in Headers */,
291 | 37E3E2422079FF3600C5B683 /* TWXSource.h in Headers */,
292 | 33B95C252062E7720060B8D5 /* TWXBookmarksSidebarItem.h in Headers */,
293 | 37E3E2222078D41800C5B683 /* TWXDuskTheme.h in Headers */,
294 | 33B95C212062D1B30060B8D5 /* TWXBookmarksTimeline.h in Headers */,
295 | 335D38D720387EFC00379A21 /* TWXRoundedAvatars.h in Headers */,
296 | 33075926203D9EF700CAA86C /* TWXEnhancedFullscreen.h in Headers */,
297 | );
298 | runOnlyForDeploymentPostprocessing = 0;
299 | };
300 | /* End PBXHeadersBuildPhase section */
301 |
302 | /* Begin PBXNativeTarget section */
303 | 335D38C820387EE300379A21 /* TwitterX */ = {
304 | isa = PBXNativeTarget;
305 | buildConfigurationList = 335D38CE20387EE300379A21 /* Build configuration list for PBXNativeTarget "TwitterX" */;
306 | buildPhases = (
307 | 335D38C420387EE300379A21 /* Sources */,
308 | 335D38C520387EE300379A21 /* Frameworks */,
309 | 335D38C620387EE300379A21 /* Headers */,
310 | 335D38C720387EE300379A21 /* Resources */,
311 | );
312 | buildRules = (
313 | );
314 | dependencies = (
315 | );
316 | name = TwitterX;
317 | productName = TwitterX;
318 | productReference = 335D38C920387EE300379A21 /* TwitterX.framework */;
319 | productType = "com.apple.product-type.framework";
320 | };
321 | 33B95C2A2062FF820060B8D5 /* TwitterXAssets */ = {
322 | isa = PBXNativeTarget;
323 | buildConfigurationList = 33B95C2E2062FF820060B8D5 /* Build configuration list for PBXNativeTarget "TwitterXAssets" */;
324 | buildPhases = (
325 | 33B95C272062FF820060B8D5 /* Sources */,
326 | 33B95C282062FF820060B8D5 /* Frameworks */,
327 | 33B95C292062FF820060B8D5 /* Resources */,
328 | );
329 | buildRules = (
330 | );
331 | dependencies = (
332 | );
333 | name = TwitterXAssets;
334 | productName = TwitterXAssets;
335 | productReference = 33B95C2B2062FF820060B8D5 /* TwitterXAssets.bundle */;
336 | productType = "com.apple.product-type.bundle";
337 | };
338 | /* End PBXNativeTarget section */
339 |
340 | /* Begin PBXProject section */
341 | 335D389820382C9100379A21 /* Project object */ = {
342 | isa = PBXProject;
343 | attributes = {
344 | LastUpgradeCheck = 0920;
345 | ORGANIZATIONNAME = twitterx;
346 | TargetAttributes = {
347 | 335D38C820387EE300379A21 = {
348 | CreatedOnToolsVersion = 9.2;
349 | ProvisioningStyle = Automatic;
350 | };
351 | 33B95C2A2062FF820060B8D5 = {
352 | CreatedOnToolsVersion = 9.2;
353 | ProvisioningStyle = Automatic;
354 | };
355 | };
356 | };
357 | buildConfigurationList = 335D389B20382C9100379A21 /* Build configuration list for PBXProject "TwitterX" */;
358 | compatibilityVersion = "Xcode 8.0";
359 | developmentRegion = en;
360 | hasScannedForEncodings = 0;
361 | knownRegions = (
362 | en,
363 | );
364 | mainGroup = 335D389720382C9100379A21;
365 | productRefGroup = 335D38A120382C9100379A21 /* Products */;
366 | projectDirPath = "";
367 | projectRoot = "";
368 | targets = (
369 | 335D38C820387EE300379A21 /* TwitterX */,
370 | 33B95C2A2062FF820060B8D5 /* TwitterXAssets */,
371 | );
372 | };
373 | /* End PBXProject section */
374 |
375 | /* Begin PBXResourcesBuildPhase section */
376 | 335D38C720387EE300379A21 /* Resources */ = {
377 | isa = PBXResourcesBuildPhase;
378 | buildActionMask = 2147483647;
379 | files = (
380 | 33B95C34206308190060B8D5 /* TwitterXAssets.bundle in Resources */,
381 | );
382 | runOnlyForDeploymentPostprocessing = 0;
383 | };
384 | 33B95C292062FF820060B8D5 /* Resources */ = {
385 | isa = PBXResourcesBuildPhase;
386 | buildActionMask = 2147483647;
387 | files = (
388 | 33B95C4720631AB30060B8D5 /* bookmarks@2x.png in Resources */,
389 | 33B95C4620631AB30060B8D5 /* bookmarks.png in Resources */,
390 | );
391 | runOnlyForDeploymentPostprocessing = 0;
392 | };
393 | /* End PBXResourcesBuildPhase section */
394 |
395 | /* Begin PBXSourcesBuildPhase section */
396 | 335D38C420387EE300379A21 /* Sources */ = {
397 | isa = PBXSourcesBuildPhase;
398 | buildActionMask = 2147483647;
399 | files = (
400 | 335D38E02038E45C00379A21 /* TwitterX.m in Sources */,
401 | 335D38D820387EFF00379A21 /* TWXRoundedAvatars.m in Sources */,
402 | 33B95C182062767F0060B8D5 /* TWXAPI.m in Sources */,
403 | 37E3E23F2079FE8700C5B683 /* NSString+TWX.m in Sources */,
404 | C18C74F720507003001B0AAC /* TWXDirectMessagesCrash.m in Sources */,
405 | 37E3E2272078D93200C5B683 /* TWXThemeMenu.m in Sources */,
406 | 33B95C3F206310760060B8D5 /* NSImage+TWX.m in Sources */,
407 | 33B95C1E206294EE0060B8D5 /* TWXAPI+Bookmarks.m in Sources */,
408 | 33B95C4B2063A6EA0060B8D5 /* NSBundle+TWX.m in Sources */,
409 | 33075927203D9EF700CAA86C /* TWXEnhancedFullscreen.m in Sources */,
410 | 37E3E2432079FF3600C5B683 /* TWXSource.m in Sources */,
411 | 37E3E23B2079ED5900C5B683 /* TWXTheme.m in Sources */,
412 | C1D84701204358FE0016E52D /* TWXConsumerKeysOverride.m in Sources */,
413 | 37E3E2332078F98700C5B683 /* NSColor+TWX.m in Sources */,
414 | C13554712061C4E700F6504D /* TWXBookmarkActions.m in Sources */,
415 | 335D38D620387EF900379A21 /* TWXCharactersLimit.m in Sources */,
416 | 37E3E2232078D41800C5B683 /* TWXDuskTheme.m in Sources */,
417 | 33B95C262062E7720060B8D5 /* TWXBookmarksSidebarItem.m in Sources */,
418 | C1D846FD2042B08C0016E52D /* TWXRepliesCount.m in Sources */,
419 | 335D38DE2038DA6100379A21 /* TWXRuntime.m in Sources */,
420 | 33B95C222062D1B30060B8D5 /* TWXBookmarksTimeline.m in Sources */,
421 | C1D846F92041F5950016E52D /* TWXLightThemeReborn.m in Sources */,
422 | C1B328302059160D00009EA8 /* NSView+TWX.m in Sources */,
423 | C1D846F52041BB080016E52D /* TWXRoundedStatusContent.m in Sources */,
424 | );
425 | runOnlyForDeploymentPostprocessing = 0;
426 | };
427 | 33B95C272062FF820060B8D5 /* Sources */ = {
428 | isa = PBXSourcesBuildPhase;
429 | buildActionMask = 2147483647;
430 | files = (
431 | );
432 | runOnlyForDeploymentPostprocessing = 0;
433 | };
434 | /* End PBXSourcesBuildPhase section */
435 |
436 | /* Begin XCBuildConfiguration section */
437 | 335D38A720382C9100379A21 /* Debug */ = {
438 | isa = XCBuildConfiguration;
439 | buildSettings = {
440 | ALWAYS_SEARCH_USER_PATHS = NO;
441 | CLANG_ANALYZER_NONNULL = YES;
442 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
444 | CLANG_CXX_LIBRARY = "libc++";
445 | CLANG_ENABLE_MODULES = YES;
446 | CLANG_ENABLE_OBJC_ARC = YES;
447 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
448 | CLANG_WARN_BOOL_CONVERSION = YES;
449 | CLANG_WARN_COMMA = YES;
450 | CLANG_WARN_CONSTANT_CONVERSION = YES;
451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
452 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
453 | CLANG_WARN_EMPTY_BODY = YES;
454 | CLANG_WARN_ENUM_CONVERSION = YES;
455 | CLANG_WARN_INFINITE_RECURSION = YES;
456 | CLANG_WARN_INT_CONVERSION = YES;
457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
461 | CLANG_WARN_STRICT_PROTOTYPES = YES;
462 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
463 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
464 | CLANG_WARN_UNREACHABLE_CODE = YES;
465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
466 | CODE_SIGN_IDENTITY = "-";
467 | COPY_PHASE_STRIP = NO;
468 | DEBUG_INFORMATION_FORMAT = dwarf;
469 | ENABLE_STRICT_OBJC_MSGSEND = YES;
470 | ENABLE_TESTABILITY = YES;
471 | GCC_C_LANGUAGE_STANDARD = gnu11;
472 | GCC_DYNAMIC_NO_PIC = NO;
473 | GCC_NO_COMMON_BLOCKS = YES;
474 | GCC_OPTIMIZATION_LEVEL = 0;
475 | GCC_PREPROCESSOR_DEFINITIONS = (
476 | "DEBUG=1",
477 | "$(inherited)",
478 | );
479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
481 | GCC_WARN_UNDECLARED_SELECTOR = YES;
482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
483 | GCC_WARN_UNUSED_FUNCTION = YES;
484 | GCC_WARN_UNUSED_VARIABLE = YES;
485 | MACOSX_DEPLOYMENT_TARGET = 10.10;
486 | MTL_ENABLE_DEBUG_INFO = YES;
487 | ONLY_ACTIVE_ARCH = YES;
488 | SDKROOT = macosx;
489 | };
490 | name = Debug;
491 | };
492 | 335D38A820382C9100379A21 /* Release */ = {
493 | isa = XCBuildConfiguration;
494 | buildSettings = {
495 | ALWAYS_SEARCH_USER_PATHS = NO;
496 | CLANG_ANALYZER_NONNULL = YES;
497 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
498 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
499 | CLANG_CXX_LIBRARY = "libc++";
500 | CLANG_ENABLE_MODULES = YES;
501 | CLANG_ENABLE_OBJC_ARC = YES;
502 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
503 | CLANG_WARN_BOOL_CONVERSION = YES;
504 | CLANG_WARN_COMMA = YES;
505 | CLANG_WARN_CONSTANT_CONVERSION = YES;
506 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
507 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
508 | CLANG_WARN_EMPTY_BODY = YES;
509 | CLANG_WARN_ENUM_CONVERSION = YES;
510 | CLANG_WARN_INFINITE_RECURSION = YES;
511 | CLANG_WARN_INT_CONVERSION = YES;
512 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
513 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
514 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
515 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
516 | CLANG_WARN_STRICT_PROTOTYPES = YES;
517 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
518 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
519 | CLANG_WARN_UNREACHABLE_CODE = YES;
520 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
521 | CODE_SIGN_IDENTITY = "-";
522 | COPY_PHASE_STRIP = NO;
523 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
524 | ENABLE_NS_ASSERTIONS = NO;
525 | ENABLE_STRICT_OBJC_MSGSEND = YES;
526 | GCC_C_LANGUAGE_STANDARD = gnu11;
527 | GCC_NO_COMMON_BLOCKS = YES;
528 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
529 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
530 | GCC_WARN_UNDECLARED_SELECTOR = YES;
531 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
532 | GCC_WARN_UNUSED_FUNCTION = YES;
533 | GCC_WARN_UNUSED_VARIABLE = YES;
534 | MACOSX_DEPLOYMENT_TARGET = 10.10;
535 | MTL_ENABLE_DEBUG_INFO = NO;
536 | SDKROOT = macosx;
537 | };
538 | name = Release;
539 | };
540 | 335D38CF20387EE300379A21 /* Debug */ = {
541 | isa = XCBuildConfiguration;
542 | buildSettings = {
543 | CODE_SIGN_IDENTITY = "";
544 | CODE_SIGN_STYLE = Automatic;
545 | COMBINE_HIDPI_IMAGES = YES;
546 | CURRENT_PROJECT_VERSION = 1;
547 | DEFINES_MODULE = YES;
548 | DYLIB_COMPATIBILITY_VERSION = 1;
549 | DYLIB_CURRENT_VERSION = 1;
550 | DYLIB_INSTALL_NAME_BASE = "@rpath";
551 | FRAMEWORK_VERSION = A;
552 | GCC_WARN_UNDECLARED_SELECTOR = NO;
553 | GCC_WARN_UNUSED_VALUE = NO;
554 | GCC_WARN_UNUSED_VARIABLE = YES;
555 | INFOPLIST_FILE = TwitterX/Info.plist;
556 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
557 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
558 | MACOSX_DEPLOYMENT_TARGET = 10.10;
559 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterX;
560 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
561 | SKIP_INSTALL = YES;
562 | VERSIONING_SYSTEM = "apple-generic";
563 | VERSION_INFO_PREFIX = "";
564 | };
565 | name = Debug;
566 | };
567 | 335D38D020387EE300379A21 /* Release */ = {
568 | isa = XCBuildConfiguration;
569 | buildSettings = {
570 | CODE_SIGN_IDENTITY = "";
571 | CODE_SIGN_STYLE = Automatic;
572 | COMBINE_HIDPI_IMAGES = YES;
573 | CURRENT_PROJECT_VERSION = 1;
574 | DEFINES_MODULE = YES;
575 | DYLIB_COMPATIBILITY_VERSION = 1;
576 | DYLIB_CURRENT_VERSION = 1;
577 | DYLIB_INSTALL_NAME_BASE = "@rpath";
578 | FRAMEWORK_VERSION = A;
579 | GCC_WARN_UNDECLARED_SELECTOR = NO;
580 | GCC_WARN_UNUSED_VALUE = NO;
581 | GCC_WARN_UNUSED_VARIABLE = YES;
582 | INFOPLIST_FILE = TwitterX/Info.plist;
583 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
584 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
585 | MACOSX_DEPLOYMENT_TARGET = 10.10;
586 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterX;
587 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
588 | SKIP_INSTALL = YES;
589 | VERSIONING_SYSTEM = "apple-generic";
590 | VERSION_INFO_PREFIX = "";
591 | };
592 | name = Release;
593 | };
594 | 33B95C2F2062FF820060B8D5 /* Debug */ = {
595 | isa = XCBuildConfiguration;
596 | buildSettings = {
597 | CODE_SIGN_STYLE = Automatic;
598 | COMBINE_HIDPI_IMAGES = YES;
599 | INFOPLIST_FILE = TwitterXAssets/Info.plist;
600 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
601 | MACOSX_DEPLOYMENT_TARGET = 10.9;
602 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterXAssets;
603 | PRODUCT_NAME = "$(TARGET_NAME)";
604 | SKIP_INSTALL = YES;
605 | WRAPPER_EXTENSION = bundle;
606 | };
607 | name = Debug;
608 | };
609 | 33B95C302062FF820060B8D5 /* Release */ = {
610 | isa = XCBuildConfiguration;
611 | buildSettings = {
612 | CODE_SIGN_STYLE = Automatic;
613 | COMBINE_HIDPI_IMAGES = YES;
614 | INFOPLIST_FILE = TwitterXAssets/Info.plist;
615 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
616 | MACOSX_DEPLOYMENT_TARGET = 10.9;
617 | PRODUCT_BUNDLE_IDENTIFIER = com.twitterx.TwitterXAssets;
618 | PRODUCT_NAME = "$(TARGET_NAME)";
619 | SKIP_INSTALL = YES;
620 | WRAPPER_EXTENSION = bundle;
621 | };
622 | name = Release;
623 | };
624 | /* End XCBuildConfiguration section */
625 |
626 | /* Begin XCConfigurationList section */
627 | 335D389B20382C9100379A21 /* Build configuration list for PBXProject "TwitterX" */ = {
628 | isa = XCConfigurationList;
629 | buildConfigurations = (
630 | 335D38A720382C9100379A21 /* Debug */,
631 | 335D38A820382C9100379A21 /* Release */,
632 | );
633 | defaultConfigurationIsVisible = 0;
634 | defaultConfigurationName = Release;
635 | };
636 | 335D38CE20387EE300379A21 /* Build configuration list for PBXNativeTarget "TwitterX" */ = {
637 | isa = XCConfigurationList;
638 | buildConfigurations = (
639 | 335D38CF20387EE300379A21 /* Debug */,
640 | 335D38D020387EE300379A21 /* Release */,
641 | );
642 | defaultConfigurationIsVisible = 0;
643 | defaultConfigurationName = Release;
644 | };
645 | 33B95C2E2062FF820060B8D5 /* Build configuration list for PBXNativeTarget "TwitterXAssets" */ = {
646 | isa = XCConfigurationList;
647 | buildConfigurations = (
648 | 33B95C2F2062FF820060B8D5 /* Debug */,
649 | 33B95C302062FF820060B8D5 /* Release */,
650 | );
651 | defaultConfigurationIsVisible = 0;
652 | defaultConfigurationName = Release;
653 | };
654 | /* End XCConfigurationList section */
655 | };
656 | rootObject = 335D389820382C9100379A21 /* Project object */;
657 | }
658 |
--------------------------------------------------------------------------------